Coverage for src/rejuvenation/refactor_with_nested_compositions.py: 94%
50 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-15 10:34 +0000
« 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
7"""AI: Example script demonstrating nested pattern replacements when rewriting C source."""
9# This script demonstrates the use of the syntax_tree library to parse and rewrite C code.
10# It specifically showcases nested replacements and multiple patterns.
11import textwrap
13from renaissance.integrations.clang import ClangASTNode, CPatternFactory
14from renaissance.syntax_tree import ASTFactory, ASTRewriter, ASTShower
15from renaissance.syntax_tree.ast_finder import find_semantic_kind
16from renaissance.syntax_tree.match_finder import find_all
17from renaissance.syntax_tree.semantic_kind import SemanticKind
19example_code = """
20void f1(int a, int b, int c);
21void f2(int a, int c);
22void f(){
23 const int a = 1;
24 const int b = 2;
25 int isAOne = a==1;
26 int c = 0, d=0;
27 if (a==1) {
28 d++;
29 if(a==1){
30 d++;
31 c=d;
32 f1(a,b,c);
33 }
34 }
35 if (a==2) {
36 c++;
37 f1(a,b,c);
38 }
39 f1(a,b,c);
40}
41""".strip()
43expected_result = """
44void f1(int a, int b, int c);
45void f2(int a, int c);
46void f(){
47 const int a = 1;
48 const int b = 2;
49 int isAOne = a==1;
50 int c = 0, d=0;
51 //changed if expr to const
52 if(isAOne){
53 d++;
54 //changed if expr to const
55 if(isAOne){
56 d++;
57 c=d;
58 //changed function f1 to f2
59 f2(a,c);
60 }
61 }
62 if (a==2) {
63 c++;
64 //changed function f1 to f2
65 f2(a,c);
66 }
67 //changed function f1 to f2
68 f2(a,c);
69}
70""".strip()
73def refactor_with_nested_compositions(args):
74 """AI: Demonstrate nested pattern composition by rewriting calls inside matched if-statement bodies."""
75 # the first argument is the code to be parsed
76 code = args[1] if len(args) > 1 else ""
78 # Create a factory args from the command line are passed to the factory for example -I/usr/include
79 factory = ASTFactory(ClangASTNode, args if not code else args[1:])
80 # Create a pattern factory (using the factory (hence also its args)
81 # create translation unit
82 atu = factory.create(code) if code else factory.create_from_text(example_code, "example.c")
83 # create a pattern factory atu is passed to the pattern factory for use of all # includes, #defines and declarations
84 pattern_factory = CPatternFactory(factory, atu)
85 # create a pattern that matches an if statement with a==1 as the condition and a block of statements as the body
86 # the type is important so it's declared as const int a
87 pattern1 = pattern_factory.create_statements("if(a==1){$$stmts;}", extra_declarations=["const int a;"])
88 # for pattern 2 we create a fully functional c snippet with a call to f1
89 # note that the f1 declaration is derived from the atu
90 pattern2 = pattern_factory.create("int $a,$b,$c; void fff() {f1($a,$b,$c);}")
91 ASTShower.show_node(pattern1[0], include_properties=True)
93 # we only want to search the call expression as a pattern so it's searched using the kind
94 pattern2 = find_semantic_kind(pattern2, SemanticKind.CALL)
96 # the replacement code strip indent is used to be agnostic to the indentation of the replacement
97 pattern1replacement = textwrap.dedent("""
98 //changed if expr to const
99 if(isAOne){
100 $$stmts;
101 }""")
103 pattern2replacement = "\n//changed function f1 to f2\nf2($a,$c);"
105 # show node and patterns enable include properties to show the properties of the nodes
106 include_properties = True
107 ASTShower.show_node(atu, include_properties)
108 ASTShower.show_node(pattern1[0], include_properties)
109 ASTShower.show_node(pattern2[0], include_properties)
111 result1 = None
112 while atu:
113 # create an ASTRewriter
114 rewriter = ASTRewriter(atu)
116 def raw(nodes):
117 res = ""
118 for node in nodes:
119 res += node.text
120 return res + "\n"
122 # create a refactoring that use different replacement code for different patterns
123 def refactor(match1):
124 print(f"peek: f{match1.signature}")
125 if match1.patterns == pattern1:
126 replacement_text = pattern1replacement
127 for repl_snippet in match1.expansions:
128 replacement_text = replacement_text.replace(repl_snippet, raw(match1.expansions[repl_snippet]))
129 else:
130 replacement_text = pattern2replacement
131 for repl_snippet in match1.expansions:
132 replacement_text = replacement_text.replace(repl_snippet, match1.expansions[repl_snippet][0].signature)
133 return rewriter.replace(replacement_text, match1.nodes)
135 # search matches for pattern1 and pattern2 and replace them using the refactor function
136 for match in find_all(atu.children, pattern1, pattern2):
137 refactor(match)
139 # print the rewritten code
140 result1 = rewriter.apply_to_string()
141 atu = factory.create_from_text(result1, "example.c") if rewriter.has_changed() else None
142 return result1
145if __name__ == "__main__":
146 import sys
148 result = refactor_with_nested_compositions(sys.argv)
149 print(result)