Coverage for src / renaissance / syntax_tree / ast_processor.py: 85%
67 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-09-09 14:04 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-09-09 14:04 +0000
1from collections.abc import Callable, Iterator, Sequence
2from pathlib import Path
4import renaissance.syntax_tree.match_finder
5from renaissance.integrations.types import Type
6from renaissance.syntax_tree import ASTNode
7from renaissance.syntax_tree.ast_factory import ASTFactory
8from renaissance.syntax_tree.ast_finder import ASTFinder, find_ast_type
9from renaissance.syntax_tree.ast_rewriter import ASTRewriter
10from renaissance.syntax_tree.match_finder import PatternMatch
13class ASTProcessor:
14 def __init__(
15 self,
16 root: ASTNode,
17 ast_factory: ASTFactory,
18 in_memory: bool = False,
19 ) -> None:
20 self.__root_node = root
21 self.__rewriter = ASTRewriter(root)
22 self.__ast_factory = ast_factory
23 self.in_memory = in_memory
24 self.repeat_step = 0
26 @property
27 def factory(self) -> ASTFactory:
28 return self.__ast_factory
30 @property
31 def node(self) -> ASTNode:
32 return self.__root_node
34 @property
35 def filename(self) -> str:
36 return self.__rewriter.get_filename()
38 @property
39 def root(self) -> ASTNode:
40 return self.__root_node
42 def replace(
43 self,
44 new_content: str,
45 target: ASTNode | Sequence[ASTNode] | PatternMatch | Sequence[PatternMatch],
46 include_whitespace: bool = True,
47 include_comments: bool = True,
48 ) -> None:
49 self.__rewriter.replace(new_content, target, include_whitespace, include_comments)
51 def remove(
52 self,
53 target: ASTNode | Sequence[ASTNode] | PatternMatch | Sequence[PatternMatch],
54 include_whitespace: bool = True,
55 include_comments: bool = True,
56 ) -> None:
57 self.__rewriter.remove(target, include_whitespace, include_comments)
59 def insert_before(
60 self,
61 new_content: str,
62 target: ASTNode | Sequence[ASTNode] | PatternMatch | Sequence[PatternMatch],
63 include_whitespace: bool = True,
64 include_comments: bool = True,
65 ) -> None:
66 self.__rewriter.insert_before(new_content, target, include_whitespace, include_comments)
68 def insert_after(
69 self,
70 new_content: str,
71 target: ASTNode | Sequence[ASTNode] | PatternMatch | Sequence[PatternMatch],
72 include_whitespace: bool = True,
73 include_comments: bool = True,
74 ) -> None:
75 self.__rewriter.insert_after(new_content, target, include_whitespace, include_comments)
77 def find_all(self, function: Callable[[ASTNode], Iterator[ASTNode] | bool]) -> Sequence[ASTNode]:
78 return ASTFinder.find_all(self.__root_node, function)
80 def find_ast_type(self, kind: type[Type]) -> Sequence[ASTNode]:
81 return find_ast_type(self.__root_node, kind)
83 def find_match(self, *patterns_list, recursive: bool = True) -> Sequence[PatternMatch]:
84 return renaissance.syntax_tree.match_finder.find_all(
85 self.__root_node.children,
86 *patterns_list,
87 recursive=recursive,
88 )
90 def has_changed(self) -> bool:
91 return self.__rewriter.has_changed()
93 def apply_to_string(self) -> str:
94 return self.__rewriter.apply_to_string()
96 def commit(self) -> ASTProcessor:
97 """Commits the current changes to the AST (Abstract Syntax Tree) and returns a new ASTProcessor instance.
99 This method applies the current changes to the source code and creates a new ASTProcessor instance
100 with the updated AST. If the changes are in-memory, it directly creates the new AST from the updated
101 code string. Otherwise, it writes the changes to the file, reloads the file, and then creates the new AST.
103 Returns:
104 ASTProcessor: A new instance of ASTProcessor with the updated AST.
106 Raises:
107 IOError: If there is an error writing to the file.
109 """
110 if not self.__rewriter.has_changed():
111 return self
112 self.__root_node, self.__rewriter = self._commit(self.__rewriter, self.__ast_factory, self.in_memory)
113 return ASTProcessor(self.__root_node, self.__ast_factory, self.in_memory)
115 @staticmethod
116 def _commit(rewriter: ASTRewriter, factory: ASTFactory, in_memory: bool = False):
117 rewriter.apply_to_string()
118 if in_memory:
119 atu = factory.create_from_text(rewriter.apply_to_string(), rewriter.get_filename())
120 return atu, ASTRewriter(atu)
121 # save file first then reload it
122 with Path(rewriter.get_filename()).open("wb") as f:
123 f.write(rewriter.apply())
124 atu = factory.create(Path(rewriter.get_filename()))
125 return atu, ASTRewriter(atu)
128# main
129if __name__ == "__main__":
131 def test[T](_: str, factory: type[T]) -> T:
132 result = factory()
133 assert isinstance(result, factory)
134 return result
136 test("key", str)