Coverage for src/rejuvenation/refactor_with_nested_compositions.py: 94%

50 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-10-02 13:12 +0000

1"""AI: Example script demonstrating nested pattern replacements when rewriting C source.""" 

2 

3# This script demonstrates the use of the syntax_tree library to parse and rewrite C code. 

4# It specifically showcases nested replacements and multiple patterns. 

5import textwrap 

6 

7from renaissance.integrations.clang import ClangASTNode, CPatternFactory 

8from renaissance.syntax_tree import ASTFactory, ASTRewriter, ASTShower 

9from renaissance.syntax_tree.ast_finder import find_semantic_kind 

10from renaissance.syntax_tree.match_finder import find_all 

11from renaissance.syntax_tree.semantic_kind import SemanticKind 

12 

13example_code = """ 

14void f1(int a, int b, int c); 

15void f2(int a, int c); 

16void f(){ 

17 const int a = 1; 

18 const int b = 2; 

19 int isAOne = a==1; 

20 int c = 0, d=0; 

21 if (a==1) { 

22 d++; 

23 if(a==1){ 

24 d++; 

25 c=d; 

26 f1(a,b,c); 

27 } 

28 } 

29 if (a==2) { 

30 c++; 

31 f1(a,b,c); 

32 } 

33 f1(a,b,c); 

34} 

35""".strip() 

36 

37expected_result = """ 

38void f1(int a, int b, int c); 

39void f2(int a, int c); 

40void f(){ 

41 const int a = 1; 

42 const int b = 2; 

43 int isAOne = a==1; 

44 int c = 0, d=0; 

45 //changed if expr to const 

46 if(isAOne){ 

47 d++; 

48 //changed if expr to const 

49 if(isAOne){ 

50 d++; 

51 c=d; 

52 //changed function f1 to f2 

53 f2(a,c); 

54 } 

55 } 

56 if (a==2) { 

57 c++; 

58 //changed function f1 to f2 

59 f2(a,c); 

60 } 

61 //changed function f1 to f2 

62 f2(a,c); 

63} 

64""".strip() 

65 

66 

67def refactor_with_nested_compositions(args): 

68 """AI: Demonstrate nested pattern composition by rewriting calls inside matched if-statement bodies.""" 

69 # the first argument is the code to be parsed 

70 code = args[1] if len(args) > 1 else "" 

71 

72 # Create a factory args from the command line are passed to the factory for example -I/usr/include 

73 factory = ASTFactory(ClangASTNode, args if not code else args[1:]) 

74 # Create a pattern factory (using the factory (hence also its args) 

75 # create translation unit 

76 atu = factory.create(code) if code else factory.create_from_text(example_code, "example.c") 

77 # create a pattern factory atu is passed to the pattern factory for use of all # includes, #defines and declarations 

78 pattern_factory = CPatternFactory(factory, atu) 

79 # create a pattern that matches an if statement with a==1 as the condition and a block of statements as the body 

80 # the type is important so it's declared as const int a 

81 pattern1 = pattern_factory.create_statements("if(a==1){$$stmts;}", extra_declarations=["const int a;"]) 

82 # for pattern 2 we create a fully functional c snippet with a call to f1 

83 # note that the f1 declaration is derived from the atu 

84 pattern2 = pattern_factory.create("int $a,$b,$c; void fff() {f1($a,$b,$c);}") 

85 ASTShower.show_node(pattern1[0], include_properties=True) 

86 

87 # we only want to search the call expression as a pattern so it's searched using the kind 

88 pattern2 = find_semantic_kind(pattern2, SemanticKind.CALL) 

89 

90 # the replacement code strip indent is used to be agnostic to the indentation of the replacement 

91 pattern1replacement = textwrap.dedent(""" 

92 //changed if expr to const 

93 if(isAOne){ 

94 $$stmts; 

95 }""") 

96 

97 pattern2replacement = "\n//changed function f1 to f2\nf2($a,$c);" 

98 

99 # show node and patterns enable include properties to show the properties of the nodes 

100 include_properties = True 

101 ASTShower.show_node(atu, include_properties) 

102 ASTShower.show_node(pattern1[0], include_properties) 

103 ASTShower.show_node(pattern2[0], include_properties) 

104 

105 result1 = None 

106 while atu: 

107 # create an ASTRewriter 

108 rewriter = ASTRewriter(atu) 

109 

110 def raw(nodes): 

111 res = "" 

112 for node in nodes: 

113 res += node.text 

114 return res + "\n" 

115 

116 # create a refactoring that use different replacement code for different patterns 

117 def refactor(match1): 

118 print(f"peek: f{match1.signature}") 

119 if match1.patterns == pattern1: 

120 replacement_text = pattern1replacement 

121 for repl_snippet in match1.expansions: 

122 replacement_text = replacement_text.replace(repl_snippet, raw(match1.expansions[repl_snippet])) 

123 else: 

124 replacement_text = pattern2replacement 

125 for repl_snippet in match1.expansions: 

126 replacement_text = replacement_text.replace(repl_snippet, match1.expansions[repl_snippet][0].signature) 

127 return rewriter.replace(replacement_text, match1.nodes) 

128 

129 # search matches for pattern1 and pattern2 and replace them using the refactor function 

130 for match in find_all(atu.children, pattern1, pattern2): 

131 refactor(match) 

132 

133 # print the rewritten code 

134 result1 = rewriter.apply_to_string() 

135 atu = factory.create_from_text(result1, "example.c") if rewriter.has_changed() else None 

136 return result1 

137 

138 

139if __name__ == "__main__": 

140 import sys 

141 

142 result = refactor_with_nested_compositions(sys.argv) 

143 print(result)