wood 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +8 -0
  3. data/LICENSE +19 -0
  4. data/README.md +21 -0
  5. data/Rakefile +55 -0
  6. data/examples/binary_ops.rb +138 -0
  7. data/examples/node_finder.rb +96 -0
  8. data/examples/visitors.rb +105 -0
  9. data/lib/core_ext.rb +4 -0
  10. data/lib/core_ext/class.rb +13 -0
  11. data/lib/core_ext/enumerable.rb +47 -0
  12. data/lib/core_ext/kernel.rb +16 -0
  13. data/lib/core_ext/string.rb +32 -0
  14. data/lib/wood.rb +13 -0
  15. data/lib/wood/indented_printer.rb +48 -0
  16. data/lib/wood/node.rb +259 -0
  17. data/lib/wood/node_rewriter.rb +29 -0
  18. data/lib/wood/node_visitor.rb +60 -0
  19. data/lib/wood/nodes.rb +22 -0
  20. data/lib/wood/nodes/assignment.rb +34 -0
  21. data/lib/wood/nodes/break.rb +4 -0
  22. data/lib/wood/nodes/code_block.rb +44 -0
  23. data/lib/wood/nodes/continue.rb +4 -0
  24. data/lib/wood/nodes/for_loop.rb +11 -0
  25. data/lib/wood/nodes/function.rb +45 -0
  26. data/lib/wood/nodes/if_else.rb +6 -0
  27. data/lib/wood/nodes/literals.rb +68 -0
  28. data/lib/wood/nodes/nested_node.rb +5 -0
  29. data/lib/wood/nodes/no_op.rb +7 -0
  30. data/lib/wood/nodes/null.rb +4 -0
  31. data/lib/wood/nodes/operator.rb +28 -0
  32. data/lib/wood/nodes/return.rb +15 -0
  33. data/lib/wood/nodes/switch.rb +13 -0
  34. data/lib/wood/nodes/variable.rb +24 -0
  35. data/lib/wood/nodes/while_loop.rb +10 -0
  36. data/lib/wood/tree_pattern.rb +14 -0
  37. data/lib/wood/tree_pattern/any_matcher.rb +20 -0
  38. data/lib/wood/tree_pattern/matcher.rb +40 -0
  39. data/lib/wood/tree_pattern/node_finder.rb +164 -0
  40. data/lib/wood/tree_pattern/or_matcher.rb +33 -0
  41. data/lib/wood/tree_pattern/pattern_builder.rb +57 -0
  42. data/lib/wood/tree_pattern/pattern_callback.rb +5 -0
  43. data/lib/wood/tree_pattern/replacement_builder.rb +27 -0
  44. data/lib/wood/tree_pattern/type_matcher.rb +59 -0
  45. data/lib/wood/tree_pattern/variable_matcher.rb +31 -0
  46. data/lib/wood/tree_rewriter.rb +67 -0
  47. data/lib/wood/types.rb +318 -0
  48. data/lib/wood/version.rb +3 -0
  49. data/spec/core_ext/enumerable_spec.rb +34 -0
  50. data/spec/core_ext/string_spec.rb +25 -0
  51. data/spec/spec_cov.rb +2 -0
  52. data/spec/spec_helper.rb +3 -0
  53. data/spec/wood/indented_printer_spec.rb +61 -0
  54. data/spec/wood/node_spec.rb +258 -0
  55. data/spec/wood/node_visitor_spec.rb +43 -0
  56. data/spec/wood/nodes/code_block_spec.rb +32 -0
  57. data/spec/wood/nodes/no_op_spec.rb +5 -0
  58. data/spec/wood/nodes/operator_spec.rb +31 -0
  59. data/spec/wood/tree_pattern/any_matcher_spec.rb +32 -0
  60. data/spec/wood/tree_pattern/matcher_spec.rb +316 -0
  61. data/spec/wood/tree_pattern/node_finder_spec.rb +104 -0
  62. data/spec/wood/tree_pattern/or_matcher_spec.rb +43 -0
  63. data/spec/wood/tree_pattern/type_matcher_spec.rb +69 -0
  64. data/spec/wood/tree_pattern/variable_matcher_spec.rb +37 -0
  65. data/spec/wood/tree_rewriter_spec.rb +351 -0
  66. metadata +114 -0
@@ -0,0 +1,69 @@
1
+ describe Wood::TreePattern::TypeMatcher do#
2
+ context "match certain node pattern and type pattern" do
3
+ before do
4
+ @m = Wood::TreePattern::TypeMatcher[
5
+ node: Variable::Reference,
6
+ type: Wood::Types::U8
7
+ ]
8
+ @m2 = Variable::Reference.with_type(Wood::Types::U8)
9
+ end
10
+
11
+ it "matches the right nodes" do
12
+ [
13
+ Variable::Reference[name: :foo, type: Wood::Types::U8],
14
+ Variable::Reference[name: :bar, type: Wood::Types::U8]
15
+ ].each do |node|
16
+ @m.should === node
17
+ @m2.should === node
18
+ end
19
+ end
20
+
21
+ it "doesn't match the wrong nodes" do
22
+ [
23
+ Variable::Reference[name: :foo, type: Wood::Types::Char],
24
+ Variable::Reference[name: :bar, type: Wood::Types::Int],
25
+ Variable::Declaration[],
26
+ NoOp[]
27
+ ].each do |node|
28
+ @m.should_not === node
29
+ @m.should_not == node
30
+ @m2.should_not === node
31
+ @m2.should_not == node
32
+ end
33
+ end
34
+ end
35
+
36
+ context "match any node pattern and certain type pattern" do
37
+ before do
38
+ @type = Wood::Types::Array[Wood::Types::Int]
39
+ @m = Wood::TreePattern::TypeMatcher[type: @type]
40
+ @m2 = Node.with_type(@type)
41
+ end
42
+
43
+ it "matches the right nodes" do
44
+ [
45
+ Function::Declaration[type: @type, name: :foo],
46
+ Return[type: @type, expression: Variable::Reference[name: :bar]],
47
+ Variable::Reference[type: @type, name: :baz]
48
+ ].each do |node|
49
+ @m.should === node
50
+ @m.should == node
51
+ @m2.should === node
52
+ @m2.should_not == node
53
+ end
54
+ end
55
+
56
+ it "doesn't match the wrong nodes" do
57
+ [
58
+ Function::Declaration[type: Wood::Types::Int, name: :foo],
59
+ Return[type: Wood::Types::Int, expression: Variable::Reference[name: :bar]],
60
+ Variable::Reference[type: Wood::Types::Int, name: :baz]
61
+ ].each do |node|
62
+ @m.should_not === node
63
+ @m.should_not == node
64
+ @m2.should_not === node
65
+ @m2.should_not == node
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,37 @@
1
+ describe Wood::TreePattern::VariableMatcher do
2
+ before :each do
3
+ @pb = Wood::TreePattern::PatternBuilder.new
4
+ @vm = Wood::TreePattern::VariableMatcher.new(@pb, :var_name)
5
+ end
6
+
7
+ it "matches any node and adds a corresponding var to its PatternBuilder" do
8
+ @pb.vars.should be_empty
9
+ @vm.should === IntLiteral[10] # do the match
10
+ @pb.vars.should_not be_empty
11
+
12
+ @pb.vars.first.tap do |var|
13
+ var.name.should == :var_name
14
+ var.value.should == IntLiteral[10]
15
+ end
16
+ end
17
+
18
+ it "equal to any node" do
19
+ @vm.should == IntLiteral[10]
20
+ @vm.should == StringLiteral["foo"]
21
+ @vm.should == @vm
22
+ end
23
+
24
+ it "returns a sexp notation" do
25
+ @vm.sexp.should == [:variable_matcher, :var_name]
26
+ end
27
+
28
+ it "returns a string notation of self" do
29
+ @vm.inspect.should == @vm.sexp.inspect
30
+ end
31
+
32
+ it "returns a TypeMatcher based on self" do
33
+ tm = @vm.with_type(Wood::Types::Int)
34
+ tm.node_pattern.should == @vm
35
+ tm.type_pattern.should == Wood::Types::Int
36
+ end
37
+ end
@@ -0,0 +1,351 @@
1
+ describe Wood::TreeRewriter do
2
+ it "rewrites nested nodes based on tree patterns" do
3
+ class MyStringMatcher
4
+ include Wood::TreePattern::Matcher
5
+ pattern {
6
+ StringLiteral[val]
7
+ }.rewrite {
8
+ StringLiteral[val * 2]
9
+ }
10
+ end
11
+
12
+ class MultByZeroMatcher
13
+ include Wood::TreePattern::Matcher
14
+ ZERO = IntLiteral[0]
15
+
16
+ pattern {
17
+ Operator[name: :*, left: ZERO, right: _]
18
+ }.rewrite {
19
+ ZERO
20
+ }
21
+
22
+ pattern {
23
+ Operator[name: :*, left: _, right: ZERO]
24
+ }.rewrite {
25
+ ZERO
26
+ }
27
+ end
28
+
29
+ class MyTreeRewriter
30
+ include Wood::TreeRewriter
31
+ patterns MyStringMatcher.new, MultByZeroMatcher.new
32
+ end
33
+
34
+ tree = CodeBlock[
35
+ Variable::Declaration[
36
+ type: Wood::Types::Int,
37
+ name: :x,
38
+ init_val: Operator[
39
+ name: :*,
40
+ left: IntLiteral[0],
41
+ right: IntLiteral[10]
42
+ ]
43
+ ],
44
+ Variable::Declaration[
45
+ type: Wood::Types::Int,
46
+ name: :y,
47
+ init_val: Operator[
48
+ name: :*,
49
+ left: IntLiteral[10],
50
+ right: IntLiteral[0]
51
+ ]
52
+ ]
53
+ ]
54
+ rw = MyTreeRewriter.new
55
+ rw.rewrite(tree)
56
+ tree.should == CodeBlock[
57
+ Variable::Declaration[
58
+ type: Wood::Types::Int,
59
+ name: :x,
60
+ init_val: IntLiteral[0]
61
+ ],
62
+ Variable::Declaration[
63
+ type: Wood::Types::Int,
64
+ name: :y,
65
+ init_val: IntLiteral[0]
66
+ ]
67
+ ]
68
+
69
+ tree = Function::Declaration[
70
+ name: :foo,
71
+ type: Wood::Types::Int,
72
+ body: CodeBlock[
73
+ Variable::Declaration[
74
+ type: Wood::Types::Int,
75
+ name: :x,
76
+ init_val: IntLiteral[2]
77
+ ],
78
+ Variable::Declaration[
79
+ type: Wood::Types::Int,
80
+ name: :y,
81
+ init_val: Operator[
82
+ name: :*,
83
+ left: IntLiteral[2],
84
+ right: Operator[
85
+ name: :*,
86
+ left: Variable::Reference[:x],
87
+ right: IntLiteral[0]
88
+ ]
89
+ ]
90
+ ],
91
+ Variable::Declaration[
92
+ type: Wood::Types::Int,
93
+ name: :z,
94
+ init_val: Operator[
95
+ name: :*,
96
+ left: IntLiteral[0],
97
+ right: Variable::Reference[:x]
98
+ ]
99
+ ],
100
+ Variable::Declaration[
101
+ type: Wood::Types::String,
102
+ name: :foo,
103
+ init_val: StringLiteral["hello, world"]
104
+ ]
105
+ ]
106
+ ]
107
+ rw = MyTreeRewriter.new
108
+ rw.rewrite(tree, false)
109
+
110
+ rewritten_code = <<-C
111
+ int foo() {
112
+ int x = 2;
113
+ int y = 0;
114
+ int z = 0;
115
+ char * foo = "hello, world!hello, world!";
116
+ }
117
+ C
118
+ rewritten_code = Function::Declaration[
119
+ name: :foo,
120
+ type: Wood::Types::Int,
121
+ body: CodeBlock[
122
+ Variable::Declaration[
123
+ type: Wood::Types::Int,
124
+ name: :x,
125
+ init_val: IntLiteral[2]
126
+ ],
127
+ Variable::Declaration[
128
+ type: Wood::Types::Int,
129
+ name: :y,
130
+ init_val: IntLiteral[0]
131
+ ],
132
+ Variable::Declaration[
133
+ type: Wood::Types::Int,
134
+ name: :z,
135
+ init_val: IntLiteral[0]
136
+ ],
137
+ Variable::Declaration[
138
+ type: Wood::Types::String,
139
+ name: :foo,
140
+ init_val: StringLiteral["hello, world!hello, world!"]
141
+ ]
142
+ ]
143
+ ]
144
+ tree.should == rewritten_code
145
+ end
146
+
147
+ it "works with OrMatchers" do
148
+ class MultByZeroOrRewriter
149
+ include Wood::TreeRewriter
150
+ include Wood::TreePattern::Matcher
151
+ ZERO = IntLiteral[0]
152
+
153
+ pattern {
154
+ Operator[
155
+ name: :*,
156
+ left: ZERO,
157
+ right: _
158
+ ] | Operator[
159
+ name: :*,
160
+ left: _,
161
+ right: ZERO
162
+ ]
163
+ }.rewrite {
164
+ ZERO
165
+ }
166
+
167
+ pattern {
168
+ Operator[
169
+ name: :+,
170
+ left: ZERO,
171
+ right: val
172
+ ] | Operator[
173
+ name: :+,
174
+ left: val,
175
+ right: ZERO
176
+ ]
177
+ }.rewrite {
178
+ val
179
+ }
180
+
181
+ patterns self.new
182
+ end
183
+
184
+ rw = MultByZeroOrRewriter.new
185
+ tree = CodeBlock[
186
+ Variable::Declaration[
187
+ type: Wood::Types::Int,
188
+ name: :x,
189
+ init_val: Operator[
190
+ name: :+,
191
+ left: Operator[
192
+ name: :*,
193
+ left: IntLiteral[2],
194
+ right: IntLiteral[3]
195
+ ],
196
+ right: Operator[
197
+ name: :*,
198
+ left: IntLiteral[2],
199
+ right: IntLiteral[0]
200
+ ]
201
+ ]
202
+ ]
203
+ ]
204
+ # call rewrite with repeated=false
205
+ rw.rewrite(tree)
206
+ tree.should == CodeBlock[
207
+ Variable::Declaration[
208
+ type: Wood::Types::Int,
209
+ name: :x,
210
+ init_val: Operator[
211
+ name: :+,
212
+ left: Operator[
213
+ name: :*,
214
+ left: IntLiteral[2],
215
+ right: IntLiteral[3]
216
+ ],
217
+ right: IntLiteral[0]
218
+ ]
219
+ ]
220
+ ]
221
+
222
+ # rewrite again to apply second pattern rewrite
223
+ rw.rewrite(tree)
224
+ tree.should == CodeBlock[
225
+ Variable::Declaration[
226
+ type: Wood::Types::Int,
227
+ name: :x,
228
+ init_val: Operator[
229
+ name: :*,
230
+ left: IntLiteral[2],
231
+ right: IntLiteral[3]
232
+ ]
233
+ ]
234
+ ]
235
+
236
+ # call rewrite with repeated=true (apply all rules in one run)
237
+ tree = Variable::Declaration[
238
+ type: Wood::Types::Int,
239
+ name: :x,
240
+ init_val: Operator[
241
+ name: :+,
242
+ left: Operator[
243
+ name: :*,
244
+ left: IntLiteral[2],
245
+ right: IntLiteral[3]
246
+ ],
247
+ right: Operator[
248
+ name: :*,
249
+ left: IntLiteral[2],
250
+ right: IntLiteral[0]
251
+ ]
252
+ ]
253
+ ]
254
+ rw.rewrite(tree, true)
255
+
256
+ tree.should == Variable::Declaration[
257
+ type: Wood::Types::Int,
258
+ name: :x,
259
+ init_val: Operator[
260
+ name: :*,
261
+ left: IntLiteral[2],
262
+ right: IntLiteral[3]
263
+ ]
264
+ ]
265
+ end
266
+
267
+ it "rewrites the root node" do
268
+ class MyRootRewriter
269
+ include Wood::TreePattern::Matcher
270
+ include Wood::TreeRewriter
271
+
272
+ pattern {
273
+ IfElse[
274
+ condition: Operator[name: :<, left: _, right: _],
275
+ then_branch: _
276
+ ]
277
+ }.perform {
278
+ node.condition = TrueLiteral[]
279
+ node.then_branch = CodeBlock[Return[StringLiteral["Hello, world!"]]]
280
+ }
281
+
282
+ patterns self.new
283
+ end
284
+
285
+ if_else = IfElse[
286
+ condition: Operator[name: :<, left: IntLiteral[1], right: IntLiteral[2]],
287
+ then_branch: CodeBlock[Return[StringLiteral["This should be rewritten"]]]
288
+ ]
289
+
290
+ MyRootRewriter.new.rewrite(if_else)
291
+
292
+ if_else.condition.should == TrueLiteral[]
293
+ if_else.then_branch.should == CodeBlock[Return[StringLiteral["Hello, world!"]]]
294
+ end
295
+
296
+ it "looks up parent nodes" do
297
+ class MyParentNodeFinderRewriter
298
+ include Wood::TreePattern::Matcher
299
+ include Wood::TreeRewriter
300
+
301
+ pattern {
302
+ Assignment[var: ref, value: val]
303
+ }.rewrite {
304
+ if (wl = node.find_parent_node(WhileLoop)) && wl != node.parent_node
305
+ case wl.condition
306
+ when Assignment[var: ref]
307
+ Function::Call[
308
+ name: :triedToAssign,
309
+ args: [
310
+ StringLiteral[ref.name.to_s],
311
+ val
312
+ ]
313
+ ]
314
+ end
315
+ end
316
+ }
317
+
318
+ patterns self.new
319
+ end
320
+
321
+ ast = WhileLoop[
322
+ condition: Assignment[
323
+ var: Variable::Reference[:x],
324
+ value: Function::Call[name: :foo]
325
+ ],
326
+ body: CodeBlock[
327
+ Assignment[
328
+ var: Variable::Reference[:x],
329
+ value: IntLiteral[10]
330
+ ]
331
+ ]
332
+ ]
333
+
334
+ MyParentNodeFinderRewriter.new.rewrite(ast)
335
+
336
+ target = WhileLoop[
337
+ condition: Assignment[
338
+ var: Variable::Reference[:x],
339
+ value: Function::Call[name: :foo]
340
+ ],
341
+ body: CodeBlock[
342
+ Function::Call[
343
+ name: :triedToAssign,
344
+ args: [StringLiteral["x"], IntLiteral[10]]
345
+ ]
346
+ ]
347
+ ]
348
+
349
+ ast.should == target
350
+ end
351
+ end