wood 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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