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.
- checksums.yaml +7 -0
- data/Gemfile +8 -0
- data/LICENSE +19 -0
- data/README.md +21 -0
- data/Rakefile +55 -0
- data/examples/binary_ops.rb +138 -0
- data/examples/node_finder.rb +96 -0
- data/examples/visitors.rb +105 -0
- data/lib/core_ext.rb +4 -0
- data/lib/core_ext/class.rb +13 -0
- data/lib/core_ext/enumerable.rb +47 -0
- data/lib/core_ext/kernel.rb +16 -0
- data/lib/core_ext/string.rb +32 -0
- data/lib/wood.rb +13 -0
- data/lib/wood/indented_printer.rb +48 -0
- data/lib/wood/node.rb +259 -0
- data/lib/wood/node_rewriter.rb +29 -0
- data/lib/wood/node_visitor.rb +60 -0
- data/lib/wood/nodes.rb +22 -0
- data/lib/wood/nodes/assignment.rb +34 -0
- data/lib/wood/nodes/break.rb +4 -0
- data/lib/wood/nodes/code_block.rb +44 -0
- data/lib/wood/nodes/continue.rb +4 -0
- data/lib/wood/nodes/for_loop.rb +11 -0
- data/lib/wood/nodes/function.rb +45 -0
- data/lib/wood/nodes/if_else.rb +6 -0
- data/lib/wood/nodes/literals.rb +68 -0
- data/lib/wood/nodes/nested_node.rb +5 -0
- data/lib/wood/nodes/no_op.rb +7 -0
- data/lib/wood/nodes/null.rb +4 -0
- data/lib/wood/nodes/operator.rb +28 -0
- data/lib/wood/nodes/return.rb +15 -0
- data/lib/wood/nodes/switch.rb +13 -0
- data/lib/wood/nodes/variable.rb +24 -0
- data/lib/wood/nodes/while_loop.rb +10 -0
- data/lib/wood/tree_pattern.rb +14 -0
- data/lib/wood/tree_pattern/any_matcher.rb +20 -0
- data/lib/wood/tree_pattern/matcher.rb +40 -0
- data/lib/wood/tree_pattern/node_finder.rb +164 -0
- data/lib/wood/tree_pattern/or_matcher.rb +33 -0
- data/lib/wood/tree_pattern/pattern_builder.rb +57 -0
- data/lib/wood/tree_pattern/pattern_callback.rb +5 -0
- data/lib/wood/tree_pattern/replacement_builder.rb +27 -0
- data/lib/wood/tree_pattern/type_matcher.rb +59 -0
- data/lib/wood/tree_pattern/variable_matcher.rb +31 -0
- data/lib/wood/tree_rewriter.rb +67 -0
- data/lib/wood/types.rb +318 -0
- data/lib/wood/version.rb +3 -0
- data/spec/core_ext/enumerable_spec.rb +34 -0
- data/spec/core_ext/string_spec.rb +25 -0
- data/spec/spec_cov.rb +2 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/wood/indented_printer_spec.rb +61 -0
- data/spec/wood/node_spec.rb +258 -0
- data/spec/wood/node_visitor_spec.rb +43 -0
- data/spec/wood/nodes/code_block_spec.rb +32 -0
- data/spec/wood/nodes/no_op_spec.rb +5 -0
- data/spec/wood/nodes/operator_spec.rb +31 -0
- data/spec/wood/tree_pattern/any_matcher_spec.rb +32 -0
- data/spec/wood/tree_pattern/matcher_spec.rb +316 -0
- data/spec/wood/tree_pattern/node_finder_spec.rb +104 -0
- data/spec/wood/tree_pattern/or_matcher_spec.rb +43 -0
- data/spec/wood/tree_pattern/type_matcher_spec.rb +69 -0
- data/spec/wood/tree_pattern/variable_matcher_spec.rb +37 -0
- data/spec/wood/tree_rewriter_spec.rb +351 -0
- metadata +114 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
describe Wood::TreePattern::Matcher do
|
|
2
|
+
it "matches any node" do
|
|
3
|
+
class MatchAll
|
|
4
|
+
include Wood::TreePattern::Matcher
|
|
5
|
+
pattern { _ }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
MatchAll.pattern_builders.should_not be_empty
|
|
9
|
+
|
|
10
|
+
m = MatchAll.new
|
|
11
|
+
m.should === Operator[
|
|
12
|
+
name: :*,
|
|
13
|
+
left: IntLiteral[3],
|
|
14
|
+
right: IntLiteral[2]
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "matches string literal nodes" do
|
|
19
|
+
class MatchString
|
|
20
|
+
include Wood::TreePattern::Matcher
|
|
21
|
+
pattern {
|
|
22
|
+
StringLiteral[val]
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
m = MatchString.new
|
|
27
|
+
m.should === StringLiteral["Hello, World!"]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "matches a specific int literal node" do
|
|
31
|
+
class MatchIntLit3
|
|
32
|
+
include Wood::TreePattern::Matcher
|
|
33
|
+
pattern {
|
|
34
|
+
IntLiteral[3]
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
m = MatchIntLit3.new
|
|
39
|
+
m.should === IntLiteral[3]
|
|
40
|
+
m.should_not === IntLiteral[4]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "rewrites a int literal to a string literal node" do
|
|
44
|
+
class RewriteIntLiteral
|
|
45
|
+
include Wood::TreePattern::Matcher
|
|
46
|
+
pattern {
|
|
47
|
+
IntLiteral[val]
|
|
48
|
+
}.rewrite {
|
|
49
|
+
StringLiteral[val.to_s]
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
rw = RewriteIntLiteral.new
|
|
54
|
+
rw.replacement_for(IntLiteral[10]).should == StringLiteral["10"]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "rewrites a nested node" do
|
|
58
|
+
class RewriteNestedNode
|
|
59
|
+
include Wood::TreePattern::Matcher
|
|
60
|
+
pattern {
|
|
61
|
+
Operator[
|
|
62
|
+
name: :*,
|
|
63
|
+
left: left,
|
|
64
|
+
right: IntLiteral[10]
|
|
65
|
+
]
|
|
66
|
+
}.rewrite {
|
|
67
|
+
Operator[
|
|
68
|
+
name: :*,
|
|
69
|
+
left: left,
|
|
70
|
+
right: IntLiteral[100]
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
rw = RewriteNestedNode.new
|
|
76
|
+
# no change if no match:
|
|
77
|
+
rw.replacement_for(StringLiteral[10]).should == StringLiteral[10]
|
|
78
|
+
|
|
79
|
+
1.upto(10).each do |i|
|
|
80
|
+
original = Operator[name: :*, left: IntLiteral[i], right: IntLiteral[10]]
|
|
81
|
+
replacement = Operator[name: :*, left: IntLiteral[i], right: IntLiteral[100]]
|
|
82
|
+
|
|
83
|
+
rw.replacement_for(original).should == replacement
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "defines VariableMatchers via method_missing" do
|
|
88
|
+
class VarMatcherWithMethodMissing
|
|
89
|
+
include Wood::TreePattern::Matcher
|
|
90
|
+
pattern {
|
|
91
|
+
StringLiteral[val]
|
|
92
|
+
}.rewrite {
|
|
93
|
+
StringLiteral[val * 2]
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
VarMatcherWithMethodMissing.new.replacement_for(StringLiteral["foo"]).should == StringLiteral["foofoo"]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "defines VariableMatchers via _" do
|
|
101
|
+
class VarMatcherWith_
|
|
102
|
+
include Wood::TreePattern::Matcher
|
|
103
|
+
pattern {
|
|
104
|
+
StringLiteral[_(:val)]
|
|
105
|
+
}.rewrite {
|
|
106
|
+
StringLiteral[val * 2]
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
VarMatcherWith_.new.replacement_for(StringLiteral["foo"]).should == StringLiteral["foofoo"]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "returns the node itself if no ReplacementBuilder has been defined" do
|
|
114
|
+
class NoReplacement
|
|
115
|
+
include Wood::TreePattern::Matcher
|
|
116
|
+
pattern { StringLiteral[val] }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
sl = StringLiteral["foo"]
|
|
120
|
+
nr = NoReplacement.new
|
|
121
|
+
|
|
122
|
+
nr.should === sl
|
|
123
|
+
nr.replacement_for(sl).should == sl
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "returns a matched child node" do
|
|
127
|
+
class ChildNodeReplacement
|
|
128
|
+
include Wood::TreePattern::Matcher
|
|
129
|
+
pattern {
|
|
130
|
+
Operator[name: :*, left: left, right: _]
|
|
131
|
+
}.rewrite {
|
|
132
|
+
left
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
left = Operator[name: :+, left: IntLiteral[10], right: IntLiteral[42]]
|
|
137
|
+
op = Operator[name: :*, left: left, right: IntLiteral[10]]
|
|
138
|
+
ChildNodeReplacement.new.replacement_for(op).should == left
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "has node defined (apart from variables) in the rewrite block" do
|
|
142
|
+
class NodeDefinedInRewrite
|
|
143
|
+
include Wood::TreePattern::Matcher
|
|
144
|
+
pattern {
|
|
145
|
+
StringLiteral["foo"]
|
|
146
|
+
}.rewrite {
|
|
147
|
+
node.should == StringLiteral["foo"]
|
|
148
|
+
StringLiteral["bar"]
|
|
149
|
+
}
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
match = StringLiteral["foo"]
|
|
153
|
+
no_match = StringLiteral["nope"]
|
|
154
|
+
NodeDefinedInRewrite.new.replacement_for(match).should == StringLiteral["bar"]
|
|
155
|
+
NodeDefinedInRewrite.new.replacement_for(no_match).should == no_match
|
|
156
|
+
NodeDefinedInRewrite.new.should === match
|
|
157
|
+
NodeDefinedInRewrite.new.should_not === no_match
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "calls a block if a pattern is matched" do
|
|
161
|
+
MatchedNodes = []
|
|
162
|
+
class CallBlockMatcher
|
|
163
|
+
include Wood::TreePattern::Matcher
|
|
164
|
+
pattern {
|
|
165
|
+
StringLiteral["foo"]
|
|
166
|
+
}.perform {
|
|
167
|
+
MatchedNodes << node
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
pattern {
|
|
171
|
+
Operator[name: :*, left: IntLiteral[left], right: IntLiteral[right]]
|
|
172
|
+
}.perform {
|
|
173
|
+
MatchedNodes << IntLiteral[left * right]
|
|
174
|
+
}
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
cbm = CallBlockMatcher.new
|
|
178
|
+
|
|
179
|
+
MatchedNodes.should be_empty
|
|
180
|
+
|
|
181
|
+
cbm.should === StringLiteral["foo"]
|
|
182
|
+
MatchedNodes.should == [StringLiteral["foo"]]
|
|
183
|
+
|
|
184
|
+
cbm.should_not === StringLiteral["foobar"]
|
|
185
|
+
MatchedNodes.should == [StringLiteral["foo"]]
|
|
186
|
+
|
|
187
|
+
cbm.should === Operator[
|
|
188
|
+
name: :*,
|
|
189
|
+
left: IntLiteral[2],
|
|
190
|
+
right: IntLiteral[3]
|
|
191
|
+
]
|
|
192
|
+
MatchedNodes.should == [
|
|
193
|
+
StringLiteral["foo"], IntLiteral[6]
|
|
194
|
+
]
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it "names the root node in a pattern" do
|
|
198
|
+
class MyRootNodeMatcher
|
|
199
|
+
include Wood::TreePattern::Matcher
|
|
200
|
+
pattern {
|
|
201
|
+
my_string StringLiteral[val]
|
|
202
|
+
}.perform {
|
|
203
|
+
my_string.should == node
|
|
204
|
+
my_string.value.should == node.value
|
|
205
|
+
my_string.value.should == val
|
|
206
|
+
}
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
MyRootNodeMatcher.new.should === StringLiteral["foo"]
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it "names child nodes in a nested pattern" do
|
|
213
|
+
class MyNestedNodeMatcher
|
|
214
|
+
include Wood::TreePattern::Matcher
|
|
215
|
+
pattern {
|
|
216
|
+
Operator[
|
|
217
|
+
name: opname,
|
|
218
|
+
left: outer_left(Operator[
|
|
219
|
+
name: :*,
|
|
220
|
+
left: inner_left(Operator[
|
|
221
|
+
name: _,
|
|
222
|
+
left: IntLiteral[_],
|
|
223
|
+
right: IntLiteral[_]
|
|
224
|
+
]),
|
|
225
|
+
right: inner_right(_)
|
|
226
|
+
]),
|
|
227
|
+
right: outer_right(IntLiteral[42])
|
|
228
|
+
]
|
|
229
|
+
}.perform {
|
|
230
|
+
opname.should == node.name
|
|
231
|
+
outer_left.should == node.left
|
|
232
|
+
outer_right.should == node.right
|
|
233
|
+
inner_left.should == node.left.left
|
|
234
|
+
inner_right.should == node.left.right
|
|
235
|
+
}
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
MyNestedNodeMatcher.new.should === Operator[
|
|
239
|
+
name: :+,
|
|
240
|
+
left: Operator[
|
|
241
|
+
name: :*,
|
|
242
|
+
left: Operator[
|
|
243
|
+
name: :+,
|
|
244
|
+
left: IntLiteral[3],
|
|
245
|
+
right: IntLiteral[5]
|
|
246
|
+
],
|
|
247
|
+
right: IntLiteral[2]
|
|
248
|
+
],
|
|
249
|
+
right: IntLiteral[42]
|
|
250
|
+
]
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it "matches a nested node via a within-pattern" do
|
|
254
|
+
MatchedInts = []
|
|
255
|
+
class MyWithinMatcher
|
|
256
|
+
include Wood::TreePattern::Matcher
|
|
257
|
+
pattern {
|
|
258
|
+
CodeBlock
|
|
259
|
+
}.perform {
|
|
260
|
+
within(node) {
|
|
261
|
+
pattern {
|
|
262
|
+
IntLiteral[v]
|
|
263
|
+
}.perform {
|
|
264
|
+
MatchedInts << v
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
m = MyWithinMatcher.new
|
|
271
|
+
m.should === CodeBlock[
|
|
272
|
+
expressions: [
|
|
273
|
+
StringLiteral["foo"],
|
|
274
|
+
IntLiteral[10],
|
|
275
|
+
FloatLiteral[1.5],
|
|
276
|
+
IntLiteral[100]
|
|
277
|
+
]
|
|
278
|
+
]
|
|
279
|
+
MatchedInts.should == [10, 100]
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it "matches nodes with a given type" do
|
|
283
|
+
MyType = Struct.new(:name).new(:my_type)
|
|
284
|
+
class MyTypedMatcher
|
|
285
|
+
include Wood::TreePattern::Matcher
|
|
286
|
+
pattern {
|
|
287
|
+
Variable::Reference.with_type Wood::Types::Array[MyType]
|
|
288
|
+
}.perform {
|
|
289
|
+
node.type = MyType
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
pattern {
|
|
293
|
+
Variable::Reference[name: /foo_(\d+)/].with_type(Wood::Types::U8)
|
|
294
|
+
}.perform {
|
|
295
|
+
node.name = :"__gen__#{node.name}"
|
|
296
|
+
}
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
m = MyTypedMatcher.new
|
|
300
|
+
ref = Variable::Reference[
|
|
301
|
+
name: :foo,
|
|
302
|
+
type: Wood::Types::Array[MyType]
|
|
303
|
+
]
|
|
304
|
+
|
|
305
|
+
m.should === ref
|
|
306
|
+
ref.type.should == MyType
|
|
307
|
+
|
|
308
|
+
ref = Variable::Reference[
|
|
309
|
+
name: :foo_123,
|
|
310
|
+
type: Wood::Types::U8
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
m.should === ref
|
|
314
|
+
ref.name.should == :__gen__foo_123
|
|
315
|
+
end
|
|
316
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
describe Wood::TreePattern::NodeFinder do
|
|
2
|
+
before do
|
|
3
|
+
@nf = Wood::TreePattern::NodeFinder.new
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
it "finds a node up the tree" do
|
|
7
|
+
ast = WhileLoop[
|
|
8
|
+
condition: TrueLiteral[],
|
|
9
|
+
body: CodeBlock[
|
|
10
|
+
IfElse[
|
|
11
|
+
condition: Operator[
|
|
12
|
+
name: :<,
|
|
13
|
+
left: Variable::Reference[:x],
|
|
14
|
+
right: IntLiteral[10]
|
|
15
|
+
],
|
|
16
|
+
then_branch: CodeBlock[Return[CharLiteral["a"]]],
|
|
17
|
+
else_branch: CodeBlock[Return[CharLiteral["b"]]]
|
|
18
|
+
]
|
|
19
|
+
]
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
@nf.ast = ast.body.first.else_branch.first
|
|
23
|
+
@nf.find_parent_node(WhileLoop).should == ast
|
|
24
|
+
@nf.find_parent_node(IfElse).should == ast.body.first
|
|
25
|
+
@nf.find_parent_node(Operator).should be_nil
|
|
26
|
+
@nf.find_parent_node(Function::Call).should be_nil
|
|
27
|
+
@nf.find_parent_node(Return).should be_nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "finds multiple nodes up the tree" do
|
|
31
|
+
ast = WhileLoop[
|
|
32
|
+
condition: Operator[
|
|
33
|
+
name: :<,
|
|
34
|
+
left: Variable::Reference[:x],
|
|
35
|
+
right: Variable::Reference[:y]
|
|
36
|
+
],
|
|
37
|
+
body: CodeBlock[
|
|
38
|
+
Assignment[
|
|
39
|
+
var: Variable::Reference[:x],
|
|
40
|
+
value: Function::Call[name: :foo, args: [Variable::Reference[:y]]]
|
|
41
|
+
],
|
|
42
|
+
WhileLoop[
|
|
43
|
+
condition: Operator[
|
|
44
|
+
name: :<,
|
|
45
|
+
left: Variable::Reference[:y],
|
|
46
|
+
right: Variable::Reference[:z]
|
|
47
|
+
],
|
|
48
|
+
body: CodeBlock[
|
|
49
|
+
Assignment[
|
|
50
|
+
var: Variable::Reference[:y],
|
|
51
|
+
value: Function::Call[name: :foo, args: [Variable::Reference[:z]]]
|
|
52
|
+
]
|
|
53
|
+
]
|
|
54
|
+
]
|
|
55
|
+
]
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
@nf.ast = ast.body[1].body.first
|
|
59
|
+
@nf.find_parent_nodes(WhileLoop).should == [ast.body[1], ast]
|
|
60
|
+
@nf.find_parent_nodes(Function::Declaration).should == []
|
|
61
|
+
@nf.find_parent_nodes(Assignment).should == []
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "finds a node down the tree" do
|
|
65
|
+
ast = WhileLoop[
|
|
66
|
+
condition: Variable::Reference[:x],
|
|
67
|
+
body: CodeBlock[
|
|
68
|
+
Switch[
|
|
69
|
+
expression: Variable::Reference[:x],
|
|
70
|
+
cases: [
|
|
71
|
+
Switch::Case[
|
|
72
|
+
expression: Variable::Reference[:FOO_1],
|
|
73
|
+
body: CodeBlock[Return[StringLiteral["foo1"]]]
|
|
74
|
+
],
|
|
75
|
+
Switch::Case[
|
|
76
|
+
expression: Variable::Reference[:FOO_2],
|
|
77
|
+
body: CodeBlock[Return[StringLiteral["foo2"]]]
|
|
78
|
+
],
|
|
79
|
+
Switch::Case[
|
|
80
|
+
expression: Variable::Reference[:FOO_3],
|
|
81
|
+
body: CodeBlock[Return[StringLiteral["foo3"]]]
|
|
82
|
+
],
|
|
83
|
+
Switch::DefaultCase[
|
|
84
|
+
body: CodeBlock[Return[Variable::Reference[:x]]]
|
|
85
|
+
]
|
|
86
|
+
]
|
|
87
|
+
]
|
|
88
|
+
]
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
switch = ast.body.first
|
|
92
|
+
@nf.ast = ast
|
|
93
|
+
|
|
94
|
+
@nf.find_child_node(Switch).should == switch
|
|
95
|
+
@nf.find_child_node(Switch::Case).should == switch.cases.first
|
|
96
|
+
@nf.find_child_node(Switch::DefaultCase).should == switch.cases[3]
|
|
97
|
+
@nf.find_child_nodes(IfElse).should == []
|
|
98
|
+
|
|
99
|
+
@nf.find_child_nodes(Switch::Case).should ==
|
|
100
|
+
switch.cases.grep(Switch::Case)
|
|
101
|
+
@nf.find_child_nodes(Return).should ==
|
|
102
|
+
switch.cases.map{|c| c.body.first }.grep(Return)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
describe Wood::TreePattern::OrMatcher do
|
|
2
|
+
OrMatcher = Wood::TreePattern::OrMatcher
|
|
3
|
+
|
|
4
|
+
it "matches either a or b but not c" do
|
|
5
|
+
a = Operator[name: :*, left: IntLiteral[10], right: IntLiteral[0]]
|
|
6
|
+
b = Operator[name: :*, left: IntLiteral[2], right: IntLiteral[3]]
|
|
7
|
+
c = Operator[name: :+, left: a, right: b]
|
|
8
|
+
om = OrMatcher.new(a, b)
|
|
9
|
+
|
|
10
|
+
om.should === a
|
|
11
|
+
om.should === b
|
|
12
|
+
om.should_not === c
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "is equal to a or b" do
|
|
16
|
+
a = Operator[name: :*, left: IntLiteral[3], right: IntLiteral[2]]
|
|
17
|
+
b = Operator[name: :+, left: IntLiteral[5], right: IntLiteral[4]]
|
|
18
|
+
c = Operator[name: :+, left: IntLiteral[1], right: IntLiteral[2]]
|
|
19
|
+
|
|
20
|
+
om = OrMatcher.new(a, b)
|
|
21
|
+
|
|
22
|
+
om.should == a
|
|
23
|
+
om.should == b
|
|
24
|
+
|
|
25
|
+
om.should_not == c
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context "debugging output" do
|
|
29
|
+
before do
|
|
30
|
+
@a = Operator[name: :*, left: IntLiteral[3], right: IntLiteral[2]]
|
|
31
|
+
@b = Null[]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "returns a sexp" do
|
|
35
|
+
OrMatcher.new(@a, @b).sexp.should == [:or_matcher, @a.sexp, @b.sexp]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "returns an inspect string" do
|
|
39
|
+
om = OrMatcher.new(@a, @b)
|
|
40
|
+
om.inspect.should == om.sexp.inspect
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|