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,20 @@
|
|
1
|
+
module Wood::TreePattern
|
2
|
+
# Matches anything it is matched / compared against.
|
3
|
+
class AnyMatcher
|
4
|
+
def === node
|
5
|
+
return true
|
6
|
+
end
|
7
|
+
|
8
|
+
def == node
|
9
|
+
return true
|
10
|
+
end
|
11
|
+
|
12
|
+
def sexp
|
13
|
+
[:any_matcher]
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
sexp.inspect
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Wood::TreePattern
|
2
|
+
module Matcher
|
3
|
+
module ClassMethods
|
4
|
+
def pattern_builders
|
5
|
+
@pattern_builders = [] unless @pattern_builders
|
6
|
+
@pattern_builders
|
7
|
+
end
|
8
|
+
|
9
|
+
def pattern(&block)
|
10
|
+
p = PatternBuilder.new(&block)
|
11
|
+
pattern_builders << p
|
12
|
+
p
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.included(klass)
|
17
|
+
klass.extend ClassMethods
|
18
|
+
end
|
19
|
+
|
20
|
+
def replacement_for(node)
|
21
|
+
self.class.pattern_builders.each do |pb|
|
22
|
+
if pb === node
|
23
|
+
if replacement = pb.replacement_for(node)
|
24
|
+
return replacement
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
return node
|
29
|
+
end
|
30
|
+
|
31
|
+
def === node
|
32
|
+
self.class.pattern_builders.each do |p|
|
33
|
+
if match = (p === node)
|
34
|
+
return match
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module Wood::TreePattern
|
2
|
+
# Provides methods for searching for parent and child nodes
|
3
|
+
# relative to a given {Node}.
|
4
|
+
class NodeFinder
|
5
|
+
# {Node} to start relative search from.
|
6
|
+
attr_accessor :ast
|
7
|
+
|
8
|
+
# Initializes {NodeFinder} with a given {Node}.
|
9
|
+
#
|
10
|
+
# @param ast [Node] AST node to start the search from.
|
11
|
+
def initialize(ast = nil)
|
12
|
+
@ast = ast
|
13
|
+
end
|
14
|
+
|
15
|
+
# Finds a parent node that matches a given pattern.
|
16
|
+
#
|
17
|
+
# @param node_pattern Node pattern to find a parent node with.
|
18
|
+
# @return [Node] Parent node found via `node_pattern` or `nil`.
|
19
|
+
def find_parent_node(node_pattern)
|
20
|
+
ast = @ast
|
21
|
+
while ast = ast.parent_node
|
22
|
+
return ast if node_pattern === ast
|
23
|
+
end
|
24
|
+
return nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Finds any parent nodes that match a given pattern.
|
28
|
+
#
|
29
|
+
# @param node_pattern Node pattern to find parent nodes with.
|
30
|
+
# @return [Array<Node>] All parent nodes that match `node_pattern`.
|
31
|
+
def find_parent_nodes(node_pattern)
|
32
|
+
ast = @ast
|
33
|
+
found = []
|
34
|
+
while ast = ast.parent_node
|
35
|
+
found << ast if node_pattern === ast
|
36
|
+
end
|
37
|
+
return found
|
38
|
+
end
|
39
|
+
|
40
|
+
# Finds a child node that matches a given pattern.
|
41
|
+
#
|
42
|
+
# @param node_pattern Node pattern to find a child node with.
|
43
|
+
# @return [Node] First child node that matches `node_pattern` or `nil`.
|
44
|
+
def find_child_node(node_pattern)
|
45
|
+
ast.child_nodes.each do |child|
|
46
|
+
if found = __find_child_node__(child, node_pattern)
|
47
|
+
return found
|
48
|
+
end
|
49
|
+
end
|
50
|
+
return nil
|
51
|
+
end
|
52
|
+
|
53
|
+
# Finds all child nodes that match a given pattern.
|
54
|
+
#
|
55
|
+
# @param node_pattern Node pattern to find child nodes with.
|
56
|
+
# @return [Array<Node>] All child nodes that match `node_pattern`.
|
57
|
+
def find_child_nodes(node_pattern)
|
58
|
+
found = []
|
59
|
+
ast.child_nodes.each do |child|
|
60
|
+
found += __find_child_nodes__(child, node_pattern)
|
61
|
+
end
|
62
|
+
found.uniq!
|
63
|
+
return found
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete_child_node(node_pattern)
|
67
|
+
__delete_child_node__(ast, node_pattern)
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete_child_nodes(node_pattern)
|
71
|
+
__delete_child_nodes__(ast, node_pattern)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def __find_child_node__(node, node_pattern)
|
77
|
+
return node if node_pattern === node
|
78
|
+
return nil unless node.respond_to?(:child_nodes)
|
79
|
+
|
80
|
+
node.child_nodes.each do |child|
|
81
|
+
if node_pattern === child
|
82
|
+
return child
|
83
|
+
end
|
84
|
+
|
85
|
+
if found = __find_child_node__(child, node_pattern)
|
86
|
+
return found
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
return nil
|
91
|
+
end
|
92
|
+
|
93
|
+
def __find_child_nodes__(node, node_pattern)
|
94
|
+
found = []
|
95
|
+
found << node if node_pattern === node
|
96
|
+
return found unless node.respond_to?(:child_nodes)
|
97
|
+
|
98
|
+
node.child_nodes.each do |child|
|
99
|
+
if node_pattern === child
|
100
|
+
found << child
|
101
|
+
end
|
102
|
+
found += __find_child_nodes__(child, node_pattern)
|
103
|
+
end
|
104
|
+
|
105
|
+
return found
|
106
|
+
end
|
107
|
+
|
108
|
+
def __delete_child_node__(node, node_pattern)
|
109
|
+
return node if node_pattern === node
|
110
|
+
|
111
|
+
node.each_child_with_name do |child, child_name|
|
112
|
+
case child
|
113
|
+
when node_pattern
|
114
|
+
node.set_child(child_name, nil)
|
115
|
+
return child
|
116
|
+
end
|
117
|
+
|
118
|
+
if deleted = __delete_child_node__(child, node_pattern)
|
119
|
+
return deleted
|
120
|
+
end
|
121
|
+
end
|
122
|
+
return nil
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def __delete_child_nodes__(node, node_pattern)
|
127
|
+
deleted = []
|
128
|
+
|
129
|
+
if Array === node
|
130
|
+
to_del = node.select do |n|
|
131
|
+
node_pattern === n
|
132
|
+
end
|
133
|
+
|
134
|
+
deleted += to_del
|
135
|
+
|
136
|
+
node.each do |n|
|
137
|
+
if del = __delete_child_nodes__(n, node_pattern)
|
138
|
+
deleted += del
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
to_del.each do |n|
|
143
|
+
node.delete(n)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
if Wood::Node === node
|
148
|
+
node.each_child_with_name do |child, child_name|
|
149
|
+
case child
|
150
|
+
when node_pattern
|
151
|
+
node.set_child(child_name, nil)
|
152
|
+
deleted << child
|
153
|
+
end
|
154
|
+
|
155
|
+
if del = __delete_child_nodes__(child, node_pattern)
|
156
|
+
deleted += del
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
return deleted
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Wood::TreePattern
|
2
|
+
module CombinatorialMatching
|
3
|
+
# @return [OrMatcher] Matcher that matches `self` or `other`.
|
4
|
+
def | other
|
5
|
+
OrMatcher.new(self, other)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Matches a [Node], if any of two given patterns match it.
|
10
|
+
class OrMatcher
|
11
|
+
include CombinatorialMatching
|
12
|
+
def initialize(a, b)
|
13
|
+
@a = a
|
14
|
+
@b = b
|
15
|
+
end
|
16
|
+
|
17
|
+
def === node
|
18
|
+
@a === node || @b === node
|
19
|
+
end
|
20
|
+
|
21
|
+
def == node
|
22
|
+
@a == node || @b == node
|
23
|
+
end
|
24
|
+
|
25
|
+
def sexp
|
26
|
+
[:or_matcher, @a.sexp, @b.sexp]
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
sexp.inspect
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Wood::TreePattern
|
2
|
+
# Helper class for building node patterns & replacement actions.
|
3
|
+
class PatternBuilder < BasicObject
|
4
|
+
attr_reader :pattern, :vars, :replacement_builder
|
5
|
+
|
6
|
+
def initialize(&block)
|
7
|
+
@vars = []
|
8
|
+
@pattern = instance_eval(&block) if block
|
9
|
+
@callbacks = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def _(var_name = nil)
|
13
|
+
if var_name
|
14
|
+
VariableMatcher.new(self, var_name)
|
15
|
+
else
|
16
|
+
AnyMatcher.new
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_var(var)
|
21
|
+
@vars << var
|
22
|
+
end
|
23
|
+
|
24
|
+
def rewrite(&block)
|
25
|
+
@replacement_builder = ReplacementBuilder.new(self, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def perform(&block)
|
29
|
+
@callbacks << PatternCallback.new(self, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(var_name, var_value = nil)
|
33
|
+
if var_value
|
34
|
+
add_var PatternVariable.new(var_name, var_value)
|
35
|
+
return var_value
|
36
|
+
else
|
37
|
+
return VariableMatcher.new(self, var_name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def replacement_for(node)
|
42
|
+
if rb = replacement_builder
|
43
|
+
return rb.replacement_for(node)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def === node
|
48
|
+
if val = (pattern === node)
|
49
|
+
@callbacks.each do |c|
|
50
|
+
c.call(node)
|
51
|
+
end
|
52
|
+
return val
|
53
|
+
end
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Wood::TreePattern
|
2
|
+
class ReplacementBuilder
|
3
|
+
def initialize(pattern_builder, &block)
|
4
|
+
@pattern_builder = pattern_builder
|
5
|
+
@block = block
|
6
|
+
end
|
7
|
+
|
8
|
+
def replacement_for(node)
|
9
|
+
@pattern_builder.vars.each do |v|
|
10
|
+
singleton_class.__send__(:define_method, v.name) { v.value }
|
11
|
+
end
|
12
|
+
@node = node
|
13
|
+
instance_eval(&@block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def within(node, &block)
|
17
|
+
node.__rewriter_class__.instance_eval &block
|
18
|
+
node.rewrite!
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def node
|
24
|
+
@node
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Wood::TreePattern
|
2
|
+
class TypeMatcher
|
3
|
+
def self.[](*options)
|
4
|
+
case options.first
|
5
|
+
when Hash
|
6
|
+
options = options.first
|
7
|
+
new(options[:node], options[:type])
|
8
|
+
else
|
9
|
+
new(*options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :node_pattern, :type_pattern
|
14
|
+
def initialize(node_pattern, type_pattern)
|
15
|
+
@node_pattern = node_pattern || AnyMatcher.new
|
16
|
+
@type_pattern = type_pattern
|
17
|
+
end
|
18
|
+
|
19
|
+
def === node
|
20
|
+
val = @node_pattern === node
|
21
|
+
if val && (@type_pattern === type(node))
|
22
|
+
return val
|
23
|
+
end
|
24
|
+
return nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def == node
|
28
|
+
val = @node_pattern == node
|
29
|
+
if val && (@type_pattern == type(node))
|
30
|
+
return val
|
31
|
+
end
|
32
|
+
return nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def sexp
|
36
|
+
node_pat_sexp = if @node_pattern.respond_to?(:sexp)
|
37
|
+
@node_pattern.sexp
|
38
|
+
else
|
39
|
+
@node_pattern.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
[:type_matcher, node_pat_sexp, @type_pattern.sexp]
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
sexp.inspect
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def type(node)
|
52
|
+
if node.respond_to?(:type)
|
53
|
+
node.type
|
54
|
+
else
|
55
|
+
node.instance_variable_get(:@type)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Wood::TreePattern
|
2
|
+
PatternVariable = Struct.new(:name, :value)
|
3
|
+
|
4
|
+
class VariableMatcher
|
5
|
+
def initialize(pattern_builder, var_name)
|
6
|
+
@pattern_builder = pattern_builder
|
7
|
+
@var_name = var_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def === node
|
11
|
+
@pattern_builder.add_var(PatternVariable.new(@var_name, node))
|
12
|
+
return true
|
13
|
+
end
|
14
|
+
|
15
|
+
def == node
|
16
|
+
return true
|
17
|
+
end
|
18
|
+
|
19
|
+
def sexp
|
20
|
+
[:variable_matcher, @var_name]
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
sexp.inspect
|
25
|
+
end
|
26
|
+
|
27
|
+
def with_type(type_pattern)
|
28
|
+
Wood::TreePattern::TypeMatcher.new(self, type_pattern)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|