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,67 @@
|
|
1
|
+
module Wood
|
2
|
+
module TreeRewriter
|
3
|
+
class Rewriter
|
4
|
+
def initialize
|
5
|
+
self.class.patterns self
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.inherited(klass)
|
9
|
+
klass.include Wood::TreePattern::Matcher
|
10
|
+
klass.include Wood::TreeRewriter
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.new(&block)
|
15
|
+
Class.new(Rewriter, &block).new
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def patterns(*patterns)
|
20
|
+
@patterns ||= []
|
21
|
+
@patterns = patterns unless patterns.empty?
|
22
|
+
@patterns
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.included(klass)
|
27
|
+
klass.extend ClassMethods
|
28
|
+
end
|
29
|
+
|
30
|
+
def rewrite(node, repeat = false)
|
31
|
+
return do_rewrite(node, repeat) unless repeat
|
32
|
+
|
33
|
+
changed = true
|
34
|
+
while changed
|
35
|
+
tmp = do_rewrite(node, repeat)
|
36
|
+
changed = tmp != node
|
37
|
+
node = tmp
|
38
|
+
end
|
39
|
+
|
40
|
+
return do_rewrite(node, false)
|
41
|
+
end
|
42
|
+
|
43
|
+
def do_rewrite(node, repeat = false)
|
44
|
+
patterns.each do |p|
|
45
|
+
node = p.replacement_for(node)
|
46
|
+
end
|
47
|
+
|
48
|
+
if node.respond_to? :each_child
|
49
|
+
node.each_child do |c|
|
50
|
+
node.set_child(c, rewrite(node.get_child(c), repeat))
|
51
|
+
end
|
52
|
+
else
|
53
|
+
if Array === node
|
54
|
+
node = node.map do |c|
|
55
|
+
rewrite(c, repeat)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
return node
|
61
|
+
end
|
62
|
+
|
63
|
+
def patterns
|
64
|
+
self.class.patterns
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/wood/types.rb
ADDED
@@ -0,0 +1,318 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module Wood
|
4
|
+
module Types
|
5
|
+
module TypeMatching
|
6
|
+
def self.included(klass)
|
7
|
+
klass.__send__ :include, Wood::TreePattern::CombinatorialMatching
|
8
|
+
klass.extend Wood::TreePattern::CombinatorialMatching
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class CompoundType
|
13
|
+
include TypeMatching
|
14
|
+
def self.[](type)
|
15
|
+
new(type)
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :type
|
19
|
+
def initialize(type)
|
20
|
+
@type = type
|
21
|
+
end
|
22
|
+
|
23
|
+
def == other
|
24
|
+
case other
|
25
|
+
when self.class
|
26
|
+
return type == other.type
|
27
|
+
else
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def numeric?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def builtin?
|
37
|
+
type && type.builtin?
|
38
|
+
end
|
39
|
+
|
40
|
+
def zero_value
|
41
|
+
@zero_value ||= Wood::Nodes::Null.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
class Array < CompoundType
|
47
|
+
def self.[](*options)
|
48
|
+
case options.first
|
49
|
+
when Hash
|
50
|
+
options = options.first
|
51
|
+
new(options[:type], options[:size])
|
52
|
+
else
|
53
|
+
new(*options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_reader :size
|
58
|
+
def initialize(type, size = nil)
|
59
|
+
super(type)
|
60
|
+
@size = size
|
61
|
+
end
|
62
|
+
|
63
|
+
def name
|
64
|
+
[:array, type.name]
|
65
|
+
end
|
66
|
+
|
67
|
+
def node_name
|
68
|
+
:array_type
|
69
|
+
end
|
70
|
+
|
71
|
+
def == other
|
72
|
+
case other
|
73
|
+
when Array
|
74
|
+
if size && other.size
|
75
|
+
return type == other.type && size == other.size
|
76
|
+
else
|
77
|
+
return type == other.type
|
78
|
+
end
|
79
|
+
else
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def === other
|
85
|
+
case other
|
86
|
+
when Array
|
87
|
+
if size && other.size
|
88
|
+
return type === other.type && size === other.size
|
89
|
+
else
|
90
|
+
return type === other.type
|
91
|
+
end
|
92
|
+
else
|
93
|
+
return false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def numeric?
|
98
|
+
type && type.numeric?
|
99
|
+
end
|
100
|
+
|
101
|
+
def builtin?
|
102
|
+
type && type.builtin?
|
103
|
+
end
|
104
|
+
|
105
|
+
def sexp
|
106
|
+
[:array, type.sexp, size.sexp]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class CustomType < Struct.new(:type)
|
111
|
+
include TypeMatching
|
112
|
+
def self.[](type)
|
113
|
+
case type
|
114
|
+
when BuiltinType
|
115
|
+
return type
|
116
|
+
when CustomType
|
117
|
+
return type
|
118
|
+
when CompoundType
|
119
|
+
return type
|
120
|
+
else
|
121
|
+
new(type)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def node_name
|
126
|
+
:custom_type
|
127
|
+
end
|
128
|
+
|
129
|
+
def sexp
|
130
|
+
[:custom_type, type.sexp]
|
131
|
+
end
|
132
|
+
|
133
|
+
def == other
|
134
|
+
case other
|
135
|
+
when CustomType
|
136
|
+
return type == other.type
|
137
|
+
else
|
138
|
+
return type == other
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def === other
|
143
|
+
case other
|
144
|
+
when CustomType
|
145
|
+
return type === other.type
|
146
|
+
else
|
147
|
+
return type === other
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def numeric?
|
152
|
+
false
|
153
|
+
end
|
154
|
+
|
155
|
+
def builtin?
|
156
|
+
false
|
157
|
+
end
|
158
|
+
|
159
|
+
def name
|
160
|
+
type.name
|
161
|
+
end
|
162
|
+
|
163
|
+
def method_missing(method, *args)
|
164
|
+
type.__send__(method, *args)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class BuiltinType < Struct.new(:name)
|
169
|
+
include TypeMatching
|
170
|
+
attr_reader :aliases
|
171
|
+
attr_accessor :zero_value
|
172
|
+
def initialize(name, numeric = false, *aliases)
|
173
|
+
super(name)
|
174
|
+
|
175
|
+
@aliases = aliases
|
176
|
+
@numeric = numeric
|
177
|
+
|
178
|
+
if numeric?
|
179
|
+
Types::NUMERIC_TYPES << self
|
180
|
+
else
|
181
|
+
Types::NON_NUMERIC_TYPES << self
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def numeric?
|
186
|
+
@numeric
|
187
|
+
end
|
188
|
+
|
189
|
+
def builtin?
|
190
|
+
true
|
191
|
+
end
|
192
|
+
|
193
|
+
def sexp
|
194
|
+
name
|
195
|
+
end
|
196
|
+
|
197
|
+
def node_name
|
198
|
+
(Array(name).map(&:to_s).join("_") + "_type").snake_cased.to_sym
|
199
|
+
end
|
200
|
+
|
201
|
+
def == other
|
202
|
+
case other
|
203
|
+
when BuiltinType
|
204
|
+
return name == other.name
|
205
|
+
else
|
206
|
+
return false
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def type
|
211
|
+
self
|
212
|
+
end
|
213
|
+
|
214
|
+
def zero_value
|
215
|
+
@zero_value ||= Wood::Nodes::Null.new
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
class NumericType < BuiltinType
|
220
|
+
def initialize(name, *aliases)
|
221
|
+
super(name, true, *aliases)
|
222
|
+
end
|
223
|
+
|
224
|
+
def zero_value
|
225
|
+
@zero_value ||= Wood::Nodes::Literal[type: self, value: 0]
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
class BooleanType < BuiltinType
|
230
|
+
def initialize(name, *aliases)
|
231
|
+
super(name, false, *aliases)
|
232
|
+
end
|
233
|
+
|
234
|
+
def zero_value
|
235
|
+
@zero_value ||= Wood::Nodes::FalseLiteral.new
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
module CommonType
|
240
|
+
def self.[](*types)
|
241
|
+
common_type_for(*types)
|
242
|
+
end
|
243
|
+
|
244
|
+
def self.type_order
|
245
|
+
@@order ||= [
|
246
|
+
Any,
|
247
|
+
Bool,
|
248
|
+
U8,
|
249
|
+
I8,
|
250
|
+
U16,
|
251
|
+
Short,
|
252
|
+
U32,
|
253
|
+
I32,
|
254
|
+
Int,
|
255
|
+
U64,
|
256
|
+
I64
|
257
|
+
]
|
258
|
+
end
|
259
|
+
|
260
|
+
def self.common_type_for(*types)
|
261
|
+
type_order[types_to_indices(types).last]
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
def self.smallest_type_of(*types)
|
266
|
+
type_order[types_to_indices(types).first]
|
267
|
+
end
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
def self.types_to_indices(types)
|
272
|
+
idx = Set.new(types).map do |t|
|
273
|
+
type_order.index(t).to_i
|
274
|
+
end.sort
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class AnyType < BuiltinType
|
279
|
+
def initialize(name, *aliases)
|
280
|
+
super(name, false, *aliases)
|
281
|
+
end
|
282
|
+
|
283
|
+
def == other
|
284
|
+
true
|
285
|
+
end
|
286
|
+
|
287
|
+
def === other
|
288
|
+
true
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
NUMERIC_TYPES = Set.new
|
294
|
+
NON_NUMERIC_TYPES = Set.new
|
295
|
+
|
296
|
+
Any = AnyType.new :Any, :any, :Object, :object
|
297
|
+
Int = NumericType.new :int
|
298
|
+
Float = NumericType.new :float
|
299
|
+
Short = NumericType.new :short
|
300
|
+
Long = NumericType.new :long
|
301
|
+
Double = NumericType.new :double
|
302
|
+
Char = BuiltinType.new :char
|
303
|
+
String = BuiltinType.new :string
|
304
|
+
Void = BuiltinType.new :void
|
305
|
+
Null = BuiltinType.new :null
|
306
|
+
Bool = BooleanType.new :bool
|
307
|
+
|
308
|
+
U8 = NumericType.new :u8, :uint8_t
|
309
|
+
U16 = NumericType.new :u16, :uint16_t
|
310
|
+
U32 = NumericType.new :u32, :uint32_t
|
311
|
+
U64 = NumericType.new :u64, :uint64_t
|
312
|
+
|
313
|
+
I8 = NumericType.new :i8, :int8_t
|
314
|
+
I16 = NumericType.new :i16, :int16_t
|
315
|
+
I32 = NumericType.new :i32, :int32_t
|
316
|
+
I64 = NumericType.new :i64, :int64_t
|
317
|
+
end
|
318
|
+
end
|
data/lib/wood/version.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
describe Enumerable do
|
2
|
+
it "calls a block in between calling another block for each object via in_between" do
|
3
|
+
s = ""
|
4
|
+
[1,2,3].in_between{ s << "," }.each do |x|
|
5
|
+
s << x.to_s
|
6
|
+
end
|
7
|
+
|
8
|
+
s.should == "1,2,3"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "allows calling each twice" do
|
12
|
+
s = ""
|
13
|
+
enum = [1,2,3].in_between{ s << "," }
|
14
|
+
enum.each do |x|
|
15
|
+
s << x.to_s
|
16
|
+
end
|
17
|
+
s.should == "1,2,3"
|
18
|
+
s << "-"
|
19
|
+
enum.each do |x|
|
20
|
+
s << (x * 10).to_s
|
21
|
+
end
|
22
|
+
s.should == "1,2,3-10,20,30"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "maps with an index parameter" do
|
26
|
+
[1,2,3].map_with_index do |x, i|
|
27
|
+
if i % 2 == 0
|
28
|
+
:even
|
29
|
+
else
|
30
|
+
x * 2
|
31
|
+
end
|
32
|
+
end.should == [:even, 4, :even]
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
describe String do
|
2
|
+
it "returns a snake-cased version of self" do
|
3
|
+
"FooBar".snake_cased.should == "foo_bar"
|
4
|
+
"Foobar".snake_cased.should == "foobar"
|
5
|
+
"fooBar".snake_cased.should == "foo_bar"
|
6
|
+
"HelloWorld-Yo".snake_cased.should == "hello_world_yo"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns a camel-cased version of self" do
|
10
|
+
"foo_bar".camel_cased.should == "FooBar"
|
11
|
+
"foo_bar_baz".camel_cased.should == "FooBarBaz"
|
12
|
+
"foo_Bar".camel_cased.should == "FooBar"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns true if it is capitalized" do
|
16
|
+
"".capitalized?.should be_falsey
|
17
|
+
"foo".capitalized?.should be_falsey
|
18
|
+
"fFoo".capitalized?.should be_falsey
|
19
|
+
|
20
|
+
"_".capitalized?.should be_truthy
|
21
|
+
"F".capitalized?.should be_truthy
|
22
|
+
"Foo".capitalized?.should be_truthy
|
23
|
+
"FFoo".capitalized?.should be_truthy
|
24
|
+
end
|
25
|
+
end
|