Coverage for lobster/tools/trlc/conversion_rule_lookup.py: 100%
27 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-09-22 04:43 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-09-22 04:43 +0000
1# LOBSTER - Lightweight Open BMW Software Traceability Evidence Report
2# Copyright (C) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as
6# published by the Free Software Foundation, either version 3 of the
7# License, or (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12# Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public
15# License along with this program. If not, see
16# <https://www.gnu.org/licenses/>.
18from typing import Dict, Iterable
19from trlc import ast
21from lobster.tools.trlc.conversion_rule import ConversionRule
22from lobster.tools.trlc.hierarchy_tree import HierarchyTree
25def build_record_type_to_conversion_rule_lookup(
26 conversion_rules: Iterable[ConversionRule],
27 children_lookup: HierarchyTree,
28 symbol_table: ast.Symbol_Table,
29) -> Dict[ast.Record_Type, ConversionRule]:
30 """Iterates over all TRLC record types and generates a lookup dictionary
31 that also propagates conversion rules to derived record types (depending
32 on the value of ConversionRule.applies_to_derived_types).
34 That is, derived types can be looked up, too, and will return the
35 conversion rule of their parent type.
36 If a specific conversion rule is defined for a derived type, then the order
37 of the rules in the input list does not matter.
38 Only the order of the record types in the symbol table matters.
39 TRLC preserves the order of record types as found in the *.rsl files.
41 Example:
42 *.rsl defines these types in the given order:
43 type Level1 {}
44 type Level2 extends Level1 {}
46 Then any record object of Level2 will be converted using the
47 conversion rule for Level1, unless there is a specific conversion rule
48 for Level2 in the input list of conversion rules.
50 Note: The ConversionRule instance specifies only the record type name and
51 namespace, but not the concrete record type instance. That's why we need this
52 function after all.
53 This function searches for the concrete record type instance in the symbol table,
54 and then builds a lookup between the record types and their conversion rules.
56 If 'conversion_rules' contains a rule for a record type that is not
57 present in the symbol table, then that rule is ignored.
58 """
59 result = {}
60 # build temporary lookup based on fully qualified name of record type:
61 temporary_lookup = {(rule.package_name, rule.type_name): rule
62 for rule in conversion_rules}
63 # iterate over all record types in the symbol table
64 # and build the final lookup:
65 for record_type in get_record_types(symbol_table):
66 fully_qualified_name = (record_type.n_package.name, record_type.name)
67 rule = temporary_lookup.get(fully_qualified_name)
68 if rule:
69 result[record_type] = rule
70 if rule.applies_to_derived_types:
71 _propagate_rule_to_derived_types_recursively(
72 parent_extraction_rule=result[record_type],
73 parent_record_type=record_type,
74 children_lookup=children_lookup,
75 extraction_rules_lookup=result,
76 )
77 return result
80def _propagate_rule_to_derived_types_recursively(
81 parent_extraction_rule: ConversionRule,
82 parent_record_type: ast.Record_Type,
83 children_lookup: HierarchyTree,
84 extraction_rules_lookup: Dict[ast.Record_Type, ConversionRule]
85):
86 child_types = children_lookup.get(parent_record_type)
87 if child_types:
88 for child_type in child_types:
89 extraction_rules_lookup[child_type] = parent_extraction_rule
90 _propagate_rule_to_derived_types_recursively(
91 parent_extraction_rule,
92 child_type,
93 children_lookup,
94 extraction_rules_lookup,
95 )
98def get_record_types(symbol_table: ast.Symbol_Table) -> Iterable[ast.Record_Type]:
99 """Returns an iterable of all record types in the TRLC symbol table
100 while preserving the order.
101 """
103 # Note: The intuitive way to get all record types is to iterate like this:
104 # for n_pkg in symbol_table.values(ast.Package):
105 # yield from n_pkg.symbols.values(ast.Record_Type)
106 # Unfortunately the "ast.Symbol_Table.values" function returns the symbols in an
107 # alphabetically sorted order!
108 # So we have to implement our own iteration to preserve the order
110 for n_pkg in symbol_table.values(ast.Package):
111 table = n_pkg.symbols.table
112 for value in table.values():
113 if isinstance(value, ast.Record_Type):
114 yield value