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,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,5 @@
1
+ module Wood::TreePattern
2
+ class PatternCallback < ReplacementBuilder
3
+ alias_method :call, :replacement_for
4
+ end
5
+ 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