diff --git a/blark/iec.lark b/blark/iec.lark index 529df93..90808d5 100644 --- a/blark/iec.lark +++ b/blark/iec.lark @@ -350,7 +350,7 @@ structure_type_declaration: structure_type_name_declaration [ extends ] ":" [ in initialized_structure: structure_type_name ":=" structure_initialization -structure_element_declaration: structure_element_name [ incomplete_location ] ":" ( initialized_structure | array_spec_init | simple_spec_init | subrange_spec_init | enumerated_spec_init | function_call ) +structure_element_declaration: var1_list ":" ( initialized_structure | array_spec_init | simple_spec_init | subrange_spec_init | enumerated_spec_init | function_call ) union_element_declaration: structure_element_name ":" ( array_specification | simple_specification | indirect_simple_specification | subrange_specification | enumerated_specification ) diff --git a/blark/summary.py b/blark/summary.py index 2b15285..b66dace 100644 --- a/blark/summary.py +++ b/blark/summary.py @@ -174,18 +174,19 @@ def from_declaration( result = {} if isinstance(item, tf.StructureElementDeclaration): - result[item.name] = DeclarationSummary( - name=str(item.name), - item=item, - location=item.location.name if item.location else None, - block=block_header, - type=item.full_type_name, # TODO -> get_type_summary? - base_type=item.base_type_name, - value=str(item.value), - parent=parent.name if parent is not None else "", - filename=filename, - **Summary.get_meta_kwargs(item.meta), - ) + for var in item.variables: + result[var.name] = DeclarationSummary( + name=str(var.name), + item=item, + location=str(var.location).replace("AT ", "") if var.location else None, + block=block_header, + type=item.full_type_name, # TODO -> get_type_summary? + base_type=item.base_type_name, + value=str(item.value), + parent=parent.name if parent is not None else "", + filename=filename, + **Summary.get_meta_kwargs(item.meta), + ) elif isinstance(item, tf.UnionElementDeclaration): result[item.name] = DeclarationSummary( name=str(item.name), diff --git a/blark/tests/source/repeated_declaration.st b/blark/tests/source/repeated_declaration.st new file mode 100644 index 0000000..4981b61 --- /dev/null +++ b/blark/tests/source/repeated_declaration.st @@ -0,0 +1,5 @@ +TYPE someStruct : +STRUCT + AlertTimer, SignalBadTimer, QualityBadTimer : library.TPUDO; +END_STRUCT +END_TYPE diff --git a/blark/tests/test_summary.py b/blark/tests/test_summary.py index 46ddb47..4f0b849 100644 --- a/blark/tests/test_summary.py +++ b/blark/tests/test_summary.py @@ -237,7 +237,7 @@ def test_twincat_general(twincat_general_281: PlcProjectMetadata): DeclarationCheck( name="I_EcatMaster1", base_type="AMSNETID", - location="input", + location="%I*", comments=[ "{attribute 'naming' := 'omit'}", "(* AMS Net ID used for FB_EcatDiag, among others *)" diff --git a/blark/tests/test_transformer.py b/blark/tests/test_transformer.py index f64bfbd..33ff4ee 100644 --- a/blark/tests/test_transformer.py +++ b/blark/tests/test_transformer.py @@ -392,6 +392,7 @@ def test_bool_literal_roundtrip(name, value, expected): param("var1_init_decl", "stVar1, stVar2 : TypeName := Value"), param("var1_init_decl", "stVar1, stVar2 : (Value1 := 1, Value2 := 2)"), param("var1_init_decl", "stVar1, stVar2 : (Value1 := 1, Value2 := 2) INT := Value1"), + param("structure_element_declaration", "Name1, Name2 : INT"), param("structure_type_declaration", "TypeName :\nSTRUCT\nEND_STRUCT"), param("structure_type_declaration", "TypeName EXTENDS Other.Type :\nSTRUCT\nEND_STRUCT"), param("structure_type_declaration", "TypeName : POINTER TO\nSTRUCT\nEND_STRUCT"), diff --git a/blark/transform.py b/blark/transform.py index 5d05c2e..df7b5c5 100644 --- a/blark/transform.py +++ b/blark/transform.py @@ -1924,7 +1924,7 @@ def __str__(self) -> str: body = "\n".join( ( "STRUCT", - indent("\n".join(str(decl) for decl in self.declarations)), + indent("\n".join(f"{decl};" for decl in self.declarations)), "END_STRUCT", ) ) @@ -1940,24 +1940,25 @@ def __str__(self) -> str: @_rule_handler("structure_element_declaration", comments=True) class StructureElementDeclaration: """ - Declaration of a single element of a structure. + Declaration line of a structure, typically with a single variable name. + + Excludes the trailing semicolon. Examples:: - iValue : INT := 3 + 4; - stTest : ST_Testing := (1, 2); - eValue : E_Test := E_Test.ABC; - arrValue : ARRAY [1..2] OF INT := [1, 2]; - arrValue1 : INT (1..2); - arrValue1 : (Value1 := 1) INT; - sValue : STRING := 'abc'; - iValue1 AT %I* : INT := 5; - sValue1 : STRING[10] := 'test'; + iValue : INT := 3 + 4 + stTest : ST_Testing := (1, 2) + eValue : E_Test := E_Test.ABC + arrValue : ARRAY [1..2] OF INT := [1, 2] + arrValue1 : INT (1..2) + arrValue1 : (Value1 := 1) INT + sValue : STRING := 'abc' + iValue1 AT %I* : INT := 5 + sValue1 : STRING[10] := 'test' + Timer1, Timer2, Timer3 : library.TPUDO """ - name: lark.Token - location: Optional[IncompleteLocation] + variables: List[DeclaredVariable] init: Union[ - StructureInitialization, ArrayTypeInitialization, StringTypeInitialization, TypeInitialization, @@ -1968,35 +1969,24 @@ class StructureElementDeclaration: ] meta: Optional[Meta] = meta_field() - @property - def variables(self) -> List[str]: - """API compat: list of variable names.""" - return [self.name] - @property def value(self) -> str: """The initialization value, if applicable.""" - if isinstance(self.init, StructureInitialization): - return str(self.init) return str(self.init.value) @property def base_type_name(self) -> Union[lark.Token, str]: """The base type name.""" - if isinstance(self.init, StructureInitialization): - return self.name return self.init.base_type_name @property - def full_type_name(self) -> lark.Token: + def full_type_name(self) -> Union[lark.Token, str]: """The full type name.""" - if isinstance(self.init, StructureInitialization): - return self.name return self.init.full_type_name def __str__(self) -> str: - name_and_location = join_if(self.name, " ", self.location) - return f"{name_and_location} : {self.init};" + variables = ", ".join(str(var) for var in self.variables) + return f"{variables} : {self.init}" UnionElementSpecification = Union[ @@ -4585,6 +4575,10 @@ def full_subrange(self): def var1_list(self, *items: DeclaredVariable) -> List[DeclaredVariable]: return list(items) + @_annotator_method_wrapper + def struct_var1_list(self, *items: DeclaredVariable) -> List[DeclaredVariable]: + return list(items) + @_annotator_method_wrapper def fb_decl_name_list(self, *items: lark.Token) -> List[lark.Token]: return list(items)