diff --git a/index.rst b/index.rst index b4e286d1..6d75c14d 100644 --- a/index.rst +++ b/index.rst @@ -39,9 +39,10 @@ Most likely that is enough, but you can access the entire parse tree from this API. .. toctree:: - :maxdepth: 2 - :caption: TRLC API Docs + :maxdepth: 2 + :caption: TRLC API Docs - manual/infrastructure - manual/errors - manual/ast + manual/infrastructure + manual/errors + manual/ast + manual/section_api diff --git a/manual/section_api.rst b/manual/section_api.rst new file mode 100644 index 00000000..105bc0a5 --- /dev/null +++ b/manual/section_api.rst @@ -0,0 +1,38 @@ +Iteration API by section +============================= + +This is description for the end-user facing TRLC iteration API by section + +Function iter_record_objects_by_section() will yield +all the information about record objects, sections and file locations:: + + def iter_record_objects_by_section(self): + +You need to input trlc files with requirements which contain +sections or nested sections with record objects:: + + # Iterates over each record object in the trlc files + # and yields the file location of trlc files + for record_object in self.iter_record_objects(): + file_name = record_object.location.file_name + if location not in self.trlc_files: + self.trlc_files.append(location) + yield location + + # This code block checks section, if present + # it will yield the section and level of section, + # record object and level of record object + if record_object.section: + object_level = len(record_object.section) - 1 + for level, section in enumerate(record_object.section): + if section not in self.section_names: + self.section_names.append(section) + yield section.name, level + yield record_object, object_level + + # If section is not present + # then it will yield the record object and level of record object + else: + object_level = 0 + yield record_object, object_level + diff --git a/tests-unit/test_ast_bysection.py b/tests-unit/test_ast_bysection.py new file mode 100644 index 00000000..afdcc70a --- /dev/null +++ b/tests-unit/test_ast_bysection.py @@ -0,0 +1,40 @@ +import unittest +from unittest.mock import patch, MagicMock +from trlc.ast import Symbol_Table + +class TestRecordObject: + def __init__(self, location, section): + self.location = location + self.section = section + +class TestSection: + def __init__(self, name): + self.name = name + +class TestIterRecordObjectsBySection(unittest.TestCase): + + @patch("trlc.ast.Symbol_Table.iter_record_objects") + def test_iter_record_objects_by_section(self, mock_iter_record_objects): + mock_location1 = MagicMock(file_name = 'file1') + mock_section1 = TestSection('section1') + mock_section2 = TestSection('section2') + mock_location2 = MagicMock(file_name = 'file2') + record1 = TestRecordObject(mock_location1, [mock_section1, mock_section2]) + record2 = TestRecordObject(mock_location2, []) + mock_iter_record_objects.return_value = [record1, record2] + + results = list(Symbol_Table().iter_record_objects_by_section()) + + expected_results = [ + 'file1', + ('section1', 0), + ('section2', 1), + (record1, 1), + 'file2', + (record2, 0) + ] + + self.assertEqual(results, expected_results) + +if __name__ == '__main__': + unittest.main() diff --git a/trlc-lrm-generator.py b/trlc-lrm-generator.py index d39c3347..27dbbc54 100755 --- a/trlc-lrm-generator.py +++ b/trlc-lrm-generator.py @@ -812,8 +812,8 @@ def write_text_object(fd, mh, obj, context, bnf_parser): # Build current section if obj.section: - new_section = section_list(obj.section) - new_hashes = section_hashes(obj.section) + new_section = section_list(obj.section[-1]) + new_hashes = section_hashes(obj.section[-1]) else: new_section = [] @@ -1198,14 +1198,17 @@ def write_toc(fd, pkg_lrm): old_section = None for obj in pkg_lrm.symbols.iter_record_objects(): - if not obj.section: + if obj.section is None: continue - if old_section == obj.section: + obj_section = obj.section[-1] + if not obj_section: continue - old_section = obj.section + if old_section == obj_section: + continue + old_section = obj_section sections = [] - ptr = obj.section + ptr = obj_section while ptr: sections = [ptr] + sections ptr = ptr.parent diff --git a/trlc/ast.py b/trlc/ast.py index c63d95d3..5777e107 100644 --- a/trlc/ast.py +++ b/trlc/ast.py @@ -2944,7 +2944,7 @@ def __init__(self, name, location, n_typ, section, n_package): # lobster-trace: LRM.Record_Object_Declaration assert isinstance(n_typ, Record_Type) - assert isinstance(section, Section) or section is None + assert isinstance(section, list) or section is None assert isinstance(n_package, Package) super().__init__(name, location, n_typ) self.field = { @@ -2997,7 +2997,7 @@ def dump(self, indent=0): # pragma: no cover self.write_indent(indent + 1, "Field %s" % key) value.dump(indent + 2) if self.section: - self.section.dump(indent + 1) + self.section[-1].dump(indent + 1) def resolve_references(self, mh): assert isinstance(mh, Message_Handler) @@ -3052,7 +3052,7 @@ class Section(Entity): def __init__(self, name, location, parent): super().__init__(name, location) assert isinstance(parent, Section) or parent is None - self.parent = parent + self.parent = parent def dump(self, indent=0): # pragma: no cover self.write_indent(indent, "Section %s" % self.name) @@ -3075,6 +3075,8 @@ def __init__(self, parent=None): self.parent = parent self.imported = [] self.table = OrderedDict() + self.trlc_files = [] + self.section_names = [] @staticmethod def simplified_name(name): @@ -3093,6 +3095,31 @@ def all_names(self): rv |= self.parent.all_names() return rv + def iter_record_objects_by_section(self): + """API for users + + Retriving information about the section hierarchy for record objects + Inputs: folder with trlc files where trlc files have sections, + sub sections and record objects + Output: Information about sections and level of sections, + record objects and levels of record object + """ + for record_object in self.iter_record_objects(): + location = record_object.location.file_name + if location not in self.trlc_files: + self.trlc_files.append(location) + yield location + if record_object.section: + object_level = len(record_object.section) - 1 + for level, section in enumerate(record_object.section): + if section not in self.section_names: + self.section_names.append(section) + yield section.name, level + yield record_object, object_level + else: + object_level = 0 + yield record_object, object_level + def iter_record_objects(self): # lobster-exclude: API for users """ Iterate over all record objects diff --git a/trlc/parser.py b/trlc/parser.py index 15a67388..925100d9 100644 --- a/trlc/parser.py +++ b/trlc/parser.py @@ -1539,14 +1539,9 @@ def parse_section_declaration(self): self.match_kw("section") t_section = self.ct self.match("STRING") - if self.section: - sec = ast.Section(name = self.ct.value, - location = self.ct.location, - parent = self.section[-1]) - else: - sec = ast.Section(name = self.ct.value, - location = self.ct.location, - parent = None) + sec = ast.Section(name = self.ct.value, + location = self.ct.location, + parent = self.section[-1] if self.section else None) sec.set_ast_link(self.ct) sec.set_ast_link(t_section) self.section.append(sec) @@ -1792,7 +1787,7 @@ def parse_record_object_declaration(self): name = self.ct.value, location = self.ct.location, n_typ = r_typ, - section = self.section[-1] if self.section else None, + section = self.section.copy() if self.section else None, n_package = self.cu.package) self.cu.package.symbols.register(self.mh, obj) obj.set_ast_link(self.ct)