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

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/>. 

17 

18from typing import Dict, Iterable 

19from trlc import ast 

20 

21from lobster.tools.trlc.conversion_rule import ConversionRule 

22from lobster.tools.trlc.hierarchy_tree import HierarchyTree 

23 

24 

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). 

33 

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. 

40 

41 Example: 

42 *.rsl defines these types in the given order: 

43 type Level1 {} 

44 type Level2 extends Level1 {} 

45 

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. 

49 

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. 

55 

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 

78 

79 

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 ) 

96 

97 

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 """ 

102 

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 

109 

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