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

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: Example script demonstrating nested pattern replacements when rewriting C source.""" 

8 

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 

12 

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 

18 

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() 

42 

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() 

71 

72 

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 "" 

77 

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) 

92 

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) 

95 

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 }""") 

102 

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

104 

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) 

110 

111 result1 = None 

112 while atu: 

113 # create an ASTRewriter 

114 rewriter = ASTRewriter(atu) 

115 

116 def raw(nodes): 

117 res = "" 

118 for node in nodes: 

119 res += node.text 

120 return res + "\n" 

121 

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) 

134 

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) 

138 

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 

143 

144 

145if __name__ == "__main__": 

146 import sys 

147 

148 result = refactor_with_nested_compositions(sys.argv) 

149 print(result)