Coverage for src / renaissance / integrations / tree_sitter / lst.py: 91%

64 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-09-09 14:04 +0000

1import sys 

2from typing import Any, Self, cast 

3 

4from renaissance.integrations.types import KIND_MAP, UnknownType 

5from renaissance.utils.ast_utils import format_node, match_children, match_props, next_sibling, preceding_sibling 

6 

7IRRELEVANT_PROPS = {"source_code", "end_point", "start_point", "location", "type"} 

8IRRELEVANT_NODE = {"comment"} 

9 

10 

11class LSTNode: 

12 def __init__( 

13 self, 

14 node_type: str, 

15 properties: dict[str, Any], 

16 signature: str, 

17 offset: int = 0, 

18 children: list[Self] | None = None, 

19 parent: Self | None = None, 

20 root: Self | None = None, 

21 ): 

22 

23 self.root = root or self 

24 self.parent = parent 

25 self.children = [] if children is None else children 

26 self.properties = properties 

27 if node_type == "string" and signature.startswith("f"): 

28 node_type = "FormattedString" 

29 

30 self.ast_type = KIND_MAP.get(node_type, UnknownType) 

31 if self.ast_type == UnknownType: 

32 print(f'"{node_type}": {node_type},') 

33 

34 self.is_implicit = True 

35 self.show_props = False 

36 self.indent = "" 

37 

38 self.is_statement = node_type == "Expr" 

39 self.referenced_by = [] 

40 self.references = [] 

41 

42 self.signature = signature 

43 self.text = signature 

44 self.filename = "unknown" 

45 self.length = len(signature) 

46 self.offset = offset 

47 self.end_offset = self.offset + self.length 

48 self.extended_end_offset = self.end_offset 

49 

50 def __eq__(self, other): 

51 return ( 

52 isinstance(other, type(self)) 

53 and self.ast_type == other.ast_type 

54 and match_props(self.properties, other.properties, IRRELEVANT_PROPS) 

55 and match_children(self.children, other.children, IRRELEVANT_NODE) 

56 ) 

57 

58 def __hash__(self): 

59 return hash((self.ast_type.__name__, frozenset(self.properties.items()), tuple(self.children))) 

60 

61 def match_props(self, properties) -> bool: 

62 all_keys = (self.properties.keys() | properties.keys()) - IRRELEVANT_PROPS 

63 return all(self.properties.get(n) == properties.get(n) for n in all_keys) 

64 

65 def match_children(self, children): 

66 return all(i < len(self.children) and self.children[i] == child for i, child in enumerate(children)) 

67 

68 def add_child(self, child): # LSTNode): 

69 self.children.append(child) 

70 child.parent = self 

71 

72 @property 

73 def preceding_sibling(self) -> Self | None: 

74 return preceding_sibling(self) 

75 

76 @property 

77 def next_sibling(self) -> Self | None: 

78 return next_sibling(self) 

79 

80 @property 

81 def name(self) -> str: 

82 return self.properties.get("name", "") 

83 

84 def binary_file_content(self): 

85 src = cast("str", self.properties.get("source_code")) 

86 return src.encode(sys.getfilesystemencoding()) 

87 

88 @property 

89 def node(self): 

90 return self.ast_type() 

91 

92 def __repr__(self): 

93 return format_node(self) 

94 # raw_lines = self.signature.splitlines() 

95 # properties_text = "" if not self.show_props else self.properties 

96 # prefix = " " if len(raw_lines) < 2 else f"\n {self.indent}" 

97 # formatted_lines = [f"{prefix}|{line}|" for line in raw_lines] 

98 # return ( 

99 # f"{self.indent}({self.kind}, {self.name}," 

100 # f" {self.filename}[{self.offset}:{self.offset + self.length}])" 

101 # f"{properties_text}:{''.join(formatted_lines)}\n" 

102 # ) 

103 

104 def is_part_of_translation_unit(self): 

105 return self.root is not None 

106 

107 

108class LST: 

109 def __init__(self, root: LSTNode): 

110 self.root = root