diff --git a/examples/typedef_header.py b/examples/typedef_header.py index 8433dea..7ae34c0 100644 --- a/examples/typedef_header.py +++ b/examples/typedef_header.py @@ -18,9 +18,14 @@ code.append(C.blank()) code.append(C.sysinclude("stdint.h")) code.append(C.blank()) -struct = C.struct("heap_tag", members=[C.struct_member("pItem", "void", pointer=True), - C.struct_member("u32Value", "uint32_t")]) -code.append(C.statement(C.typedef("heap_t", struct))) +code.append([C.statement(C.struct_ref("os_task_tag")), C.line_comment("Forward declaration")]) +code.append(C.blank()) +struct = C.struct("os_alarm_cfg_tag", + members=[C.struct_member("taskPtr", C.struct_ref("os_task_tag", pointer=True)), + C.struct_member("eventMask", "uint32_t"), + C.struct_member("initDelayMs", "uint32_t"), + C.struct_member("periodMs", "uint32_t")]) +code.append(C.statement(C.typedef("os_alarm_cfg_t", struct))) code.append(C.blank()) code.append(C.ifndef("__cplusplus")) code.append(C.line("}")) diff --git a/src/cfile/core.py b/src/cfile/core.py index d5e8cf7..b0b026b 100644 --- a/src/cfile/core.py +++ b/src/cfile/core.py @@ -222,13 +222,19 @@ def make_member(self, return member +class StructRef(Type): + """ + Reference to struct type + """ + + class Variable(Element): """ Variable declaration """ def __init__(self, name: str, - data_type: str | Type | Struct, + data_type: str | Type | Struct | StructRef, const: bool = False, # Only used as pointer qualifier pointer: bool = False, extern: bool = False, @@ -270,7 +276,7 @@ class TypeDef(Element): """ def __init__(self, name: str, - data_type: str | Type | Struct, + data_type: str | Type | Struct | StructRef, const: bool = False, # Only used as pointer qualifier pointer: bool = False, array: int | None = None) -> None: @@ -278,7 +284,7 @@ def __init__(self, self.const = const self.pointer = pointer self.array = array - if isinstance(data_type, (Type, Struct)): + if isinstance(data_type, (StructRef, Struct, Type)): self.data_type = data_type elif isinstance(data_type, str): self.data_type = Type(data_type) @@ -303,11 +309,12 @@ def __init__(self, name: str, return_type: str | Type | None = None, static: bool = False, - const: bool = False, # This is not const of the return type - extern: bool = False) -> None: + const: bool = False, # const function (as seen in C++) + extern: bool = False, + parameters: Variable | list[Variable] | None = None) -> None: self.name = name self.static = static - self.const = const # Const function (as seen in C++) + self.const = const self.extern = extern if isinstance(return_type, Type): self.return_type = return_type @@ -318,10 +325,16 @@ def __init__(self, else: raise TypeError(str(type(return_type))) self.params: list[Variable] = [] - - def add_param(self, param: Variable) -> "Function": + if parameters is not None: + if isinstance(parameters, Variable): + self.append(parameters) + elif isinstance(parameters, list): + for parameter in parameters: + self.append(parameter) + + def append(self, param: Variable) -> "Function": """ - Add function parameter + Adds new function parameter """ if not isinstance(param, (Variable)): raise TypeError("Expected Variable or FunctionPtr object") @@ -338,7 +351,7 @@ def make_param(self, Creates new Variable from arguments and adds as parameter """ param = Variable(name, data_type, const=const, pointer=pointer, array=array) - return self.add_param(param) + return self.append(param) class FunctionCall(Element): diff --git a/src/cfile/factory.py b/src/cfile/factory.py index 2b93083..7cb120e 100644 --- a/src/cfile/factory.py +++ b/src/cfile/factory.py @@ -156,6 +156,17 @@ def struct(self, """ return core.Struct(name, members) + def struct_ref(self, + name: str, + const: bool = False, + pointer: bool = False, + volatile: bool = False, + array: int | None = None) -> core.StructRef: + """ + New struct reference + """ + return core.StructRef(name, const, pointer, volatile, array) + def variable(self, name: str, data_type: str | core.Type | core.Struct, diff --git a/src/cfile/writer.py b/src/cfile/writer.py index 6f20517..1598241 100644 --- a/src/cfile/writer.py +++ b/src/cfile/writer.py @@ -118,6 +118,7 @@ def __init__(self, style: c_style.StyleOptions) -> None: "EndifDirective": self._write_endif_directive, "Extern": self._write_extern, "Struct": self._write_struct, + "StructRef": self._write_type, } self.last_element = ElementType.NONE @@ -262,7 +263,7 @@ def _format_block_comment(self, lines: list[str], wrap_text: bool, width: int, l self._write_line(line_start + line) self._write(lines[-1] + f"{'*'*width}/") - def _write_type(self, elem: core.Type) -> None: + def _write_type(self, elem: core.Type | core.StructRef) -> None: """ Writes data type """ @@ -287,14 +288,17 @@ def _format_type(self, elem: core.Type) -> str: raise RuntimeError(f"Used qualifier '{key}' not part of selected qualifier_order list") return " ".join(parts) - def _format_type_part(self, elem: core.Type) -> str: + def _format_type_part(self, elem: core.Type | core.StructRef) -> str: """ Writes type name and pointer """ + result = "" + if isinstance(elem, core.StructRef): + result += "struct " if isinstance(elem.base_type, str): - result = elem.base_type + result += elem.base_type else: - result = self._format_type(elem.base_type) + result += self._format_type(elem.base_type) if elem.pointer: if self.style.pointer_alignment == c_style.Alignment.LEFT: result += "*" diff --git a/tests/test_writer.py b/tests/test_writer.py index 7f3e59c..186a297 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -343,7 +343,7 @@ def test_void_int_arg_using_make(self): self.assertEqual(output, "void my_func(int arg1)") def test_void_int_arg_manual(self): - element = core.Function("my_func", "void").add_param(core.Variable("arg1", "int")) + element = core.Function("my_func", "void").append(core.Variable("arg1", "int")) writer = cfile.Writer(cfile.StyleOptions()) output = writer.write_str_elem(element) self.assertEqual(output, "void my_func(int arg1)") @@ -622,25 +622,24 @@ class TestStructDeclaration(unittest.TestCase): def test_zero_sized_struct(self): element = core.Struct("MyStruct") writer = cfile.Writer(cfile.StyleOptions()) - output = writer.write_str_elem(element) expected = """struct MyStruct { }""" - self.assertEqual(expected, output) + self.assertEqual(expected, writer.write_str_elem(element)) def test_struct_with_int_member(self): element = core.Struct("MyStruct", members=core.StructMember("first", "int")) writer = cfile.Writer(cfile.StyleOptions()) - output = writer.write_str_elem(element) expected = """struct MyStruct { int first; }""" - self.assertEqual(expected, output) + self.assertEqual(expected, writer.write_str_elem(element)) def test_typedef_struct_statement(self): - struct = core.Struct("Struct_IntPair", members=[core.StructMember("first", "int"), - core.StructMember("second", "int")]) + struct = core.Struct("Struct_IntPair", + members=[core.StructMember("first", "int"), + core.StructMember("second", "int")]) statement = core.Statement(core.TypeDef("IntPair_T", struct)) writer = cfile.Writer(cfile.StyleOptions()) output = writer.write_str_elem(statement) @@ -649,9 +648,56 @@ def test_typedef_struct_statement(self): int first; int second; } IntPair_T;""" - self.assertEqual(expected, output) + def test_struct_declaration_with_struct_ref_member(self): + struct = core.Struct("os_alarm_cfg_tag", + members=[core.StructMember("taskPtr", core.StructRef("os_task_tag", pointer=True)), + core.StructMember("eventMask", "uint32_t"), + core.StructMember("initDelayMs", "uint32_t"), + core.StructMember("periodMs", "uint32_t")]) + statement = core.Statement(core.TypeDef("os_alarm_cfg_t", struct)) + writer = cfile.Writer(cfile.StyleOptions()) + expected = """typedef struct os_alarm_cfg_tag +{ + struct os_task_tag* taskPtr; + uint32_t eventMask; + uint32_t initDelayMs; + uint32_t periodMs; +} os_alarm_cfg_t;""" + self.assertEqual(expected, writer.write_str_elem(statement)) + + +class TestStructRef(unittest.TestCase): + + def test_struct_variable(self): + element = core.Variable("time", core.StructRef("timespec")) + writer = cfile.Writer(cfile.StyleOptions()) + self.assertEqual('struct timespec time', writer.write_str_elem(element)) + + def test_struct_parameter(self): + element = core.Function("process_time", "void", parameters=[core.Variable("time", core.StructRef("timespec"))]) + writer = cfile.Writer(cfile.StyleOptions()) + self.assertEqual('void process_time(struct timespec time)', writer.write_str_elem(element)) + + def test_struct_return_and_struct_parameter(self): + element = core.Function("timespec_add", + core.StructRef("timespec"), + static=True, + parameters=[core.Variable("time1", core.StructRef("timespec")), + core.Variable("time2", core.StructRef("timespec"))]) + writer = cfile.Writer(cfile.StyleOptions()) + output = writer.write_str_elem(element) + self.assertEqual('static struct timespec timespec_add(struct timespec time1, struct timespec time2)', output) + + def test_struct_forward_declaration(self): + """ + Forward declaration of struct as a separate statement + """ + element = core.Statement(core.StructRef("os_task_tag")) + writer = cfile.Writer(cfile.StyleOptions()) + self.assertEqual('struct os_task_tag;', writer.write_str_elem(element)) + if __name__ == '__main__': unittest.main()