Coverage for lobster/tools/trlc/conversion_rule_lookup.py: 100%

27 statements  

« prev     ^ index     » next       coverage.py v7.10.5, created at 2025-08-27 13:02 +0000

1from typing import Dict, Iterable 

2from trlc import ast 

3 

4from lobster.tools.trlc.conversion_rule import ConversionRule 

5from lobster.tools.trlc.hierarchy_tree import HierarchyTree 

6 

7 

8def build_record_type_to_conversion_rule_lookup( 

9 conversion_rules: Iterable[ConversionRule], 

10 children_lookup: HierarchyTree, 

11 symbol_table: ast.Symbol_Table, 

12) -> Dict[ast.Record_Type, ConversionRule]: 

13 """Iterates over all TRLC record types and generates a lookup dictionary 

14 that also propagates conversion rules to derived record types (depending 

15 on the value of ConversionRule.applies_to_derived_types). 

16 

17 That is, derived types can be looked up, too, and will return the 

18 conversion rule of their parent type. 

19 If a specific conversion rule is defined for a derived type, then the order 

20 of the rules in the input list does not matter. 

21 Only the order of the record types in the symbol table matters. 

22 TRLC preserves the order of record types as found in the *.rsl files. 

23 

24 Example: 

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

26 type Level1 {} 

27 type Level2 extends Level1 {} 

28 

29 Then any record object of Level2 will be converted using the 

30 conversion rule for Level1, unless there is a specific conversion rule 

31 for Level2 in the input list of conversion rules. 

32 

33 Note: The ConversionRule instance specifies only the record type name and 

34 namespace, but not the concrete record type instance. That's why we need this 

35 function after all. 

36 This function searches for the concrete record type instance in the symbol table, 

37 and then builds a lookup between the record types and their conversion rules. 

38 

39 If 'conversion_rules' contains a rule for a record type that is not 

40 present in the symbol table, then that rule is ignored. 

41 """ 

42 result = {} 

43 # build temporary lookup based on fully qualified name of record type: 

44 temporary_lookup = {(rule.package_name, rule.type_name): rule 

45 for rule in conversion_rules} 

46 # iterate over all record types in the symbol table 

47 # and build the final lookup: 

48 for record_type in get_record_types(symbol_table): 

49 fully_qualified_name = (record_type.n_package.name, record_type.name) 

50 rule = temporary_lookup.get(fully_qualified_name) 

51 if rule: 

52 result[record_type] = rule 

53 if rule.applies_to_derived_types: 

54 _propagate_rule_to_derived_types_recursively( 

55 parent_extraction_rule=result[record_type], 

56 parent_record_type=record_type, 

57 children_lookup=children_lookup, 

58 extraction_rules_lookup=result, 

59 ) 

60 return result 

61 

62 

63def _propagate_rule_to_derived_types_recursively( 

64 parent_extraction_rule: ConversionRule, 

65 parent_record_type: ast.Record_Type, 

66 children_lookup: HierarchyTree, 

67 extraction_rules_lookup: Dict[ast.Record_Type, ConversionRule] 

68): 

69 child_types = children_lookup.get(parent_record_type) 

70 if child_types: 

71 for child_type in child_types: 

72 extraction_rules_lookup[child_type] = parent_extraction_rule 

73 _propagate_rule_to_derived_types_recursively( 

74 parent_extraction_rule, 

75 child_type, 

76 children_lookup, 

77 extraction_rules_lookup, 

78 ) 

79 

80 

81def get_record_types(symbol_table: ast.Symbol_Table) -> Iterable[ast.Record_Type]: 

82 """Returns an iterable of all record types in the TRLC symbol table 

83 while preserving the order. 

84 """ 

85 

86 # Note: The intuitive way to get all record types is to iterate like this: 

87 # for n_pkg in symbol_table.values(ast.Package): 

88 # yield from n_pkg.symbols.values(ast.Record_Type) 

89 # Unfortunately the "ast.Symbol_Table.values" function returns the symbols in an 

90 # alphabetically sorted order! 

91 # So we have to implement our own iteration to preserve the order 

92 

93 for n_pkg in symbol_table.values(ast.Package): 

94 table = n_pkg.symbols.table 

95 for value in table.values(): 

96 if isinstance(value, ast.Record_Type): 

97 yield value