Coverage for src/rejuvenation/refactor_examples_different_styles.py: 97%
71 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 multiple C AST refactoring styles: adding comments, replacing types, and finding nodes."""
9# This script demonstrates various techniques for refactoring C code using an abstract syntax tree (AST) approach.
10# It showcases how to add comments, replace types, and find specific nodes in the AST using different methods.
11from renaissance.integrations.clang import ClangASTNode, CPatternFactory
12from renaissance.integrations.clang.predicates import is_clang_type_reference
13from renaissance.syntax_tree import (
14 ASTFactory,
15 ASTFinder,
16 ASTRewriter,
17 ASTShower,
18)
19from renaissance.syntax_tree.ast_finder import find_nodes
20from renaissance.syntax_tree.match_finder import find_all, match_pattern
22example_code = """
23 typedef int fancy_new;
24 typedef int old;
25 void f(){
26 int a = 1;
27 old b = 2;
28 int c = 3;
29 old d = 4;
30 old e;
31 }
32 """
33expected_result_old_fancy_new = """
34 typedef int fancy_new;
35 typedef int old;
36 void f(){
37 int a = 1;
38 fancy_new b = 2;
39 int c = 3;
40 fancy_new d = 4;
41 fancy_new e;
42 }
43 """.strip()
45expected_result_old_with_comment = """
46 typedef int fancy_new;
47 typedef int old;
48 void f(){
49 int a = 1;
50 // old has become obsolete
51 old b = 2;
52 int c = 3;
53 // old has become obsolete
54 old d = 4;
55 // old has become obsolete
56 old e;
57 }
58 """.strip()
61def example_add_comment_and_commit(factory, pattern_factory):
62 """AI: Insert a comment before each obsolete 'old' declaration, commit, and return the rewritten source."""
63 # create a pattern that matches the declaration of old
64 # please note that we need to help by telling the old is a type and $value is a variable
65 pattern1 = pattern_factory.create_declarations(
66 "old $name = $value;",
67 extra_declarations=["typedef int old;"],
68 parameters=["$value"],
69 )
70 pattern2 = pattern_factory.create_declarations("old $name;", extra_declarations=["typedef int old;"], parameters=["$value"])
71 # put the patterns in a matrix because we want to find both statements in one go and not a sequence
72 patterns_list = [pattern1, pattern2]
74 ASTShower.show_node(pattern1[0])
75 # if you want to find both statements in one go, you should pass a list of patterns
76 # if you don't do that a sequence of the patterns is searched for
78 # create translation unit
79 atu = factory.create_from_text(example_code, "test.c")
81 ASTShower.show_node(atu)
83 # create an ASTRewriter
84 rewriter = ASTRewriter(atu)
86 # search matches and replace them
87 for match in find_all(atu.children, *patterns_list):
88 rewriter.insert_before("// old has become obsolete", match)
90 def commit():
91 rewriter.apply_to_string()
92 atu = factory.create_from_text(rewriter.apply_to_string(), rewriter.get_filename())
93 return atu, ASTRewriter(atu)
95 # commit
96 atu, rewriter = commit()
98 # look at the print that marks all old declarations with the provided comment
99 print("results after adding comments to the obsolete types:")
100 result = rewriter.apply_to_string().strip()
101 print(result)
102 return result, expected_result_old_with_comment
105def example_replace_old_by_fancy_new(factory, pattern_factory):
106 """AI: Replace 'old' declarations with 'fancy_new' using pattern matching and filtering."""
107 # using some different techniques to show the possibilities of map and filter
108 pattern1 = pattern_factory.create_declarations("$old $name = $value;", types=["$old"], parameters=["$value"])
109 pattern2 = pattern_factory.create_declarations("$old $name;", types=["$old"], parameters=["$value"])
110 # put the patterns in a matrix because we want to find both statements in one go and not a sequence
111 patterns_list = [pattern1, pattern2]
113 # an example of how to use a function iso of lambda to filter the nodes
114 def matches_old(node):
115 return bool("$old" in node and node["$old"][0].name == "old")
117 atu = factory.create_from_text(example_code, "test.c")
118 rewriter = ASTRewriter(atu)
120 [rewriter.replace("fancy_new", match.nodes) for match in match_pattern(atu.children, *patterns_list) if matches_old(match.expansions)]
122 print("results after replacing the old type by fancy_new using MatchFinder:")
123 result = rewriter.apply_to_string().strip()
124 print(result)
125 return result, expected_result_old_fancy_new
128def example_use_ast_kind_finder(factory, _):
129 """AI: Replace 'old' type references with 'fancy_new' using a Clang node-kind predicate."""
130 # Create the translation unit from the provided code or example code
131 atu = factory.create_from_text(example_code, "test.c")
132 # Create an ASTRewriter for the translation unit
133 rewriter = ASTRewriter(atu)
135 # Find all nodes of kind TYPE_REF (case-insensitive) and filter those with name 'old'
136 [rewriter.replace("fancy_new", node) for node in find_nodes(atu, is_clang_type_reference) if node.name == "old"]
138 # Print the results after replacing the old type by fancy_new
139 print("results after replacing the old type by fancy_new using a Clang predicate")
140 result = rewriter.apply_to_string().strip()
141 print(result)
142 return result, expected_result_old_fancy_new
145def example_use_ast_function_finder(factory, _):
146 """AI: Replace 'old' type references with 'fancy_new' using `ASTFinder.find_all`."""
147 # Create the translation unit from the provided code or example code
148 atu = factory.create_from_text(example_code, "test.c")
149 # Create an ASTRewriter for the translation unit
150 rewriter = ASTRewriter(atu)
152 ASTShower.show_node(atu)
154 # Define a match function to find nodes of kind TYPE_REF with name 'old'
155 def match(node):
156 res = is_clang_type_reference(node) and node.name == "old"
157 return res
159 # Use ASTFinder to find all matching nodes and replace 'old' with 'fancy_new'
160 [rewriter.replace("fancy_new", node) for node in ASTFinder.find_all(atu, match)]
162 # Print the results after replacing the old type by fancy_new
163 print("results after replacing the old type by fancy_new using ASTFinder.find_all")
164 result = rewriter.apply_to_string().strip()
165 print(result)
166 return result, expected_result_old_fancy_new
169def main(args) -> None:
170 """AI: Run all four 'old'-to-'fancy_new' refactoring style examples against the parsed args."""
171 # the first argument is the code to be parsed
172 code = args[1] if len(args) > 1 else ""
174 # Create a factory args from the command line are passed to the factory for example -I/usr/include
175 factory = ASTFactory(ClangASTNode, args if not code else args[1:])
176 # Create a pattern factory (using the factory (hence also its args)
177 pattern_factory = CPatternFactory(factory)
179 example_add_comment_and_commit(factory, pattern_factory)
180 example_replace_old_by_fancy_new(factory, pattern_factory)
181 example_use_ast_kind_finder(factory, pattern_factory)
182 example_use_ast_function_finder(factory, pattern_factory)
185if __name__ == "__main__":
186 import sys
188 main(sys.argv)