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,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