Coverage for src/renaissance/integrations/tree_sitter/adapter.py: 100%

24 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-15 10:34 +0000

1# Copyright (C) 2024-2026 TNO 

2# 

3# Licensed under the terms specified in the LICENSE file in the root directory 

4# of this repository, also available at: 

5# https://github.com/TNO/Renaissance.Py/blob/main/LICENSE 

6 

7"""AI: Adapter that parses source code with tree-sitter into the internal LST representation.""" 

8 

9from renaissance.integrations.tree_sitter.lst import LST, LSTNode 

10from renaissance.utils.ast_utils import detect_placeholder, replace_dollar 

11from tree_sitter import Language, Parser 

12 

13 

14class TreeSitterAdapter: 

15 """AI: Adapter that parses source code with tree-sitter into the internal LST representation.""" 

16 

17 def __init__(self, grammar_module) -> None: 

18 """AI: Configure a tree-sitter parser for the given language grammar.""" 

19 language = Language(grammar_module.language()) 

20 self.language = language 

21 self.parser = Parser(language) 

22 

23 def parse_code(self, source_code: str): 

24 """AI: Parse source_code with tree-sitter and return the resulting parse tree.""" 

25 return self.parser.parse(bytes(source_code, "utf8")) 

26 

27 def to_lst(self, source_code: str, tree) -> LST: 

28 """AI: Convert a tree-sitter parse tree of source_code into an LST.""" 

29 root_node = tree.root_node 

30 source_code = replace_dollar(source_code) 

31 return LST(self._convert_node(root_node, source_code, None)) 

32 

33 def _convert_node(self, node, source_code: str, parent, root=None) -> LSTNode: 

34 signature = source_code[node.start_byte : node.end_byte] 

35 is_ph, coerced_type, ph_name = detect_placeholder(signature, node.type) 

36 

37 lst_node = LSTNode( 

38 node_type=coerced_type if is_ph else node.type, 

39 properties={ 

40 "start_point": node.start_point, 

41 "end_point": node.end_point, 

42 "source_code": source_code, 

43 "name": ph_name, 

44 "is_named": node.is_named, 

45 **( 

46 { 

47 "placeholder": True, 

48 "placeholder_name": ph_name, 

49 "original_node_type": node.type, 

50 } 

51 if is_ph 

52 else {} 

53 ), 

54 }, 

55 signature=signature, 

56 offset=node.start_byte, 

57 children=[], 

58 parent=parent, 

59 root=root, 

60 ) 

61 if not root: 

62 root = lst_node 

63 

64 for child in node.children: 

65 lst_child = self._convert_node(child, source_code, lst_node, root) 

66 lst_node.add_child(lst_child) 

67 return lst_node