astrolabe 0.4.0 → 0.5.0
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 +4 -4
- data/README.md +24 -9
- data/benchmark/performance_spec.rb +12 -9
- data/lib/astrolabe/node.rb +68 -14
- data/lib/astrolabe/version.rb +1 -1
- data/spec/astrolabe/node_spec.rb +141 -41
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c17d30b9bee8cee926de9b4b2527950eb5c50763
|
4
|
+
data.tar.gz: 218e6135e0a8019b136651f4b92c0ca8648326a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e705c920bc838e45e1e0abc555e21582647c37f857e43ad36bea9057db9e9e50431c0e6eb6a9a57a7f271d8f0ec66bf45735907e1a03c159ec7016ddaf9d72b
|
7
|
+
data.tar.gz: 255f1615ab127be042ead82d9c363c06667c1e50caf9983920330313e2c9ac95fdcf09ff3b255449469a8b557e616fce7de0f02a6858b31463110ac7f4a0ceda
|
data/README.md
CHANGED
@@ -90,18 +90,33 @@ if you don't need to track context of AST.
|
|
90
90
|
|
91
91
|
```ruby
|
92
92
|
# Iterate ancestor nodes in the order from parent to root.
|
93
|
-
node.each_ancestor
|
94
|
-
p ancestor_node
|
95
|
-
end
|
93
|
+
node.each_ancestor { |ancestor_node| ... }
|
96
94
|
|
97
95
|
# This is different from `node.children.each { |child| ... }`
|
98
96
|
# which yields all children including non-node element.
|
99
|
-
node.each_child_node
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
97
|
+
node.each_child_node { |child_node| ... }
|
98
|
+
|
99
|
+
# These iteration methods can be chained by Enumerable methods.
|
100
|
+
# Find the first lvar node under the receiver node.
|
101
|
+
lvar_node = node.each_descendant.find(&:lvar_type?)
|
102
|
+
|
103
|
+
# Iterate the receiver node itself and the descendant nodes.
|
104
|
+
# This would be useful when you treat the receiver node as a root of tree
|
105
|
+
# and want to iterate all nodes in the tree.
|
106
|
+
ast.each_node { |node| ... }
|
107
|
+
|
108
|
+
# Yield only specific type nodes.
|
109
|
+
ast.each_node(:send) { |send_node| ... }
|
110
|
+
# This is equivalent to:
|
111
|
+
ast.each_node.select(&:send_type?).each { |send_node| ... }
|
112
|
+
|
113
|
+
# Yield only nodes matching any of the types.
|
114
|
+
ast.each_node(:send, :block) { |send_or_block_node| ... }
|
115
|
+
ast.each_node([:send, :block]) { |send_or_block_node| ... }
|
116
|
+
# These are equivalent to:
|
117
|
+
ast.each_node
|
118
|
+
.select { |node| [:send, :block].include?(node.type) }
|
119
|
+
.each { |send_or_block_node| ... }
|
105
120
|
```
|
106
121
|
|
107
122
|
## Compatibility
|
@@ -67,9 +67,10 @@ describe 'performance' do
|
|
67
67
|
def each_descendant
|
68
68
|
return to_enum(__method__) unless block_given?
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
children.each do |child|
|
71
|
+
next unless child.is_a?(self.class)
|
72
|
+
yield child
|
73
|
+
child.each_descendant { |node| yield node }
|
73
74
|
end
|
74
75
|
end
|
75
76
|
end
|
@@ -82,9 +83,10 @@ describe 'performance' do
|
|
82
83
|
def each_descendant(&block)
|
83
84
|
return to_enum(__method__) unless block_given?
|
84
85
|
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
children.each do |child|
|
87
|
+
next unless child.is_a?(self.class)
|
88
|
+
yield child
|
89
|
+
child.each_descendant(&block)
|
88
90
|
end
|
89
91
|
end
|
90
92
|
end
|
@@ -100,9 +102,10 @@ describe 'performance' do
|
|
100
102
|
|
101
103
|
proc_object ||= block
|
102
104
|
|
103
|
-
|
104
|
-
|
105
|
-
|
105
|
+
children.each do |child|
|
106
|
+
next unless child.is_a?(self.class)
|
107
|
+
proc_object.call(child)
|
108
|
+
child.each_descendant(proc_object)
|
106
109
|
end
|
107
110
|
end
|
108
111
|
end
|
data/lib/astrolabe/node.rb
CHANGED
@@ -16,8 +16,8 @@ module Astrolabe
|
|
16
16
|
# # Non-word characters (other than a-zA-Z0-9_) in type names are omitted.
|
17
17
|
# node.defined_type? # Equivalent to: `node.type == :defined?`
|
18
18
|
#
|
19
|
-
# #
|
20
|
-
#
|
19
|
+
# # Find the first lvar node under the receiver node.
|
20
|
+
# lvar_node = node.each_descendant.find(&:lvar_type?)
|
21
21
|
class Node < Parser::AST::Node
|
22
22
|
# @see http://rubydoc.info/gems/ast/AST/Node:initialize
|
23
23
|
def initialize(type, children = [], properties = {})
|
@@ -66,16 +66,29 @@ module Astrolabe
|
|
66
66
|
# Calls the given block for each ancestor node in the order from parent to root.
|
67
67
|
# If no block is given, an `Enumerator` is returned.
|
68
68
|
#
|
69
|
+
# @overload each_ancestor
|
70
|
+
# Yield all nodes.
|
71
|
+
# @overload each_ancestor(type)
|
72
|
+
# Yield only nodes matching the type.
|
73
|
+
# @param [Symbol] type a node type
|
74
|
+
# @overload each_ancestor(type_a, type_b, ...)
|
75
|
+
# Yield only nodes matching any of the types.
|
76
|
+
# @param [Symbol] type_a a node type
|
77
|
+
# @param [Symbol] type_b a node type
|
78
|
+
# @overload each_ancestor(types)
|
79
|
+
# Yield only nodes matching any of types in the array.
|
80
|
+
# @param [Array<Symbol>] types an array containing node types
|
69
81
|
# @yieldparam [Node] node each ancestor node
|
70
82
|
# @return [self] if a block is given
|
71
83
|
# @return [Enumerator] if no block is given
|
72
|
-
def each_ancestor
|
84
|
+
def each_ancestor(*types)
|
73
85
|
return to_enum(__method__) unless block_given?
|
74
86
|
|
87
|
+
types.flatten!
|
75
88
|
last_node = self
|
76
89
|
|
77
90
|
while (current_node = last_node.parent)
|
78
|
-
yield current_node
|
91
|
+
yield current_node if types.empty? || types.include?(current_node.type)
|
79
92
|
last_node = current_node
|
80
93
|
end
|
81
94
|
|
@@ -88,15 +101,29 @@ module Astrolabe
|
|
88
101
|
# Note that this is different from `node.children.each { |child| ... }` which yields all
|
89
102
|
# children including non-node element.
|
90
103
|
#
|
104
|
+
# @overload each_child_node
|
105
|
+
# Yield all nodes.
|
106
|
+
# @overload each_child_node(type)
|
107
|
+
# Yield only nodes matching the type.
|
108
|
+
# @param [Symbol] type a node type
|
109
|
+
# @overload each_child_node(type_a, type_b, ...)
|
110
|
+
# Yield only nodes matching any of the types.
|
111
|
+
# @param [Symbol] type_a a node type
|
112
|
+
# @param [Symbol] type_b a node type
|
113
|
+
# @overload each_child_node(types)
|
114
|
+
# Yield only nodes matching any of types in the array.
|
115
|
+
# @param [Array<Symbol>] types an array containing node types
|
91
116
|
# @yieldparam [Node] node each child node
|
92
117
|
# @return [self] if a block is given
|
93
118
|
# @return [Enumerator] if no block is given
|
94
|
-
def each_child_node
|
119
|
+
def each_child_node(*types)
|
95
120
|
return to_enum(__method__) unless block_given?
|
96
121
|
|
122
|
+
types.flatten!
|
123
|
+
|
97
124
|
children.each do |child|
|
98
125
|
next unless child.is_a?(Node)
|
99
|
-
yield child
|
126
|
+
yield child if types.empty? || types.include?(child.type)
|
100
127
|
end
|
101
128
|
|
102
129
|
self
|
@@ -105,12 +132,25 @@ module Astrolabe
|
|
105
132
|
# Calls the given block for each descendant node with depth first order.
|
106
133
|
# If no block is given, an `Enumerator` is returned.
|
107
134
|
#
|
135
|
+
# @overload each_descendant
|
136
|
+
# Yield all nodes.
|
137
|
+
# @overload each_descendant(type)
|
138
|
+
# Yield only nodes matching the type.
|
139
|
+
# @param [Symbol] type a node type
|
140
|
+
# @overload each_descendant(type_a, type_b, ...)
|
141
|
+
# Yield only nodes matching any of the types.
|
142
|
+
# @param [Symbol] type_a a node type
|
143
|
+
# @param [Symbol] type_b a node type
|
144
|
+
# @overload each_descendant(types)
|
145
|
+
# Yield only nodes matching any of types in the array.
|
146
|
+
# @param [Array<Symbol>] types an array containing node types
|
108
147
|
# @yieldparam [Node] node each descendant node
|
109
148
|
# @return [self] if a block is given
|
110
149
|
# @return [Enumerator] if no block is given
|
111
|
-
def each_descendant(&block)
|
150
|
+
def each_descendant(*types, &block)
|
112
151
|
return to_enum(__method__) unless block_given?
|
113
|
-
|
152
|
+
types.flatten!
|
153
|
+
visit_descendants(types, &block)
|
114
154
|
self
|
115
155
|
end
|
116
156
|
|
@@ -120,22 +160,36 @@ module Astrolabe
|
|
120
160
|
# This method would be useful when you treat the receiver node as a root of tree and want to
|
121
161
|
# iterate all nodes in the tree.
|
122
162
|
#
|
163
|
+
# @overload each_node
|
164
|
+
# Yield all nodes.
|
165
|
+
# @overload each_node(type)
|
166
|
+
# Yield only nodes matching the type.
|
167
|
+
# @param [Symbol] type a node type
|
168
|
+
# @overload each_node(type_a, type_b, ...)
|
169
|
+
# Yield only nodes matching any of the types.
|
170
|
+
# @param [Symbol] type_a a node type
|
171
|
+
# @param [Symbol] type_b a node type
|
172
|
+
# @overload each_node(types)
|
173
|
+
# Yield only nodes matching any of types in the array.
|
174
|
+
# @param [Array<Symbol>] types an array containing node types
|
123
175
|
# @yieldparam [Node] node each node
|
124
176
|
# @return [self] if a block is given
|
125
177
|
# @return [Enumerator] if no block is given
|
126
|
-
def each_node(&block)
|
178
|
+
def each_node(*types, &block)
|
127
179
|
return to_enum(__method__) unless block_given?
|
128
|
-
|
129
|
-
|
180
|
+
types.flatten!
|
181
|
+
yield self if types.empty? || types.include?(type)
|
182
|
+
visit_descendants(types, &block)
|
183
|
+
self
|
130
184
|
end
|
131
185
|
|
132
186
|
protected
|
133
187
|
|
134
|
-
def visit_descendants(&block)
|
188
|
+
def visit_descendants(types, &block)
|
135
189
|
children.each do |child|
|
136
190
|
next unless child.is_a?(Node)
|
137
|
-
yield child
|
138
|
-
child.visit_descendants(&block)
|
191
|
+
yield child if types.empty? || types.include?(child.type)
|
192
|
+
child.visit_descendants(types, &block)
|
139
193
|
end
|
140
194
|
end
|
141
195
|
end
|
data/lib/astrolabe/version.rb
CHANGED
data/spec/astrolabe/node_spec.rb
CHANGED
@@ -52,6 +52,26 @@ module Astrolabe
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
shared_examples 'node enumerator' do |method_name|
|
56
|
+
context 'when no block is given' do
|
57
|
+
it 'returns an enumerator' do
|
58
|
+
expect(target_node.send(method_name)).to be_a Enumerator
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'the returned enumerator' do
|
62
|
+
subject(:enumerator) { target_node.send(method_name) }
|
63
|
+
|
64
|
+
it 'enumerates ancestor nodes' do
|
65
|
+
expected_types.each do |expected_type|
|
66
|
+
expect(enumerator.next.type).to eq(expected_type)
|
67
|
+
end
|
68
|
+
|
69
|
+
expect { enumerator.next }.to raise_error(StopIteration)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
55
75
|
describe '#each_ancestor' do
|
56
76
|
let(:source) { <<-END }
|
57
77
|
class SomeClass
|
@@ -94,21 +114,41 @@ module Astrolabe
|
|
94
114
|
end
|
95
115
|
end
|
96
116
|
|
97
|
-
|
98
|
-
|
99
|
-
|
117
|
+
include_examples 'node enumerator', :each_ancestor
|
118
|
+
|
119
|
+
context 'when a node type symbol is passed' do
|
120
|
+
it 'scans all the ancestor nodes but yields only nodes matching the type' do
|
121
|
+
yielded_types = []
|
122
|
+
|
123
|
+
target_node.each_ancestor(:begin) do |node|
|
124
|
+
yielded_types << node.type
|
125
|
+
end
|
126
|
+
|
127
|
+
expect(yielded_types).to eq([:begin])
|
100
128
|
end
|
129
|
+
end
|
101
130
|
|
102
|
-
|
103
|
-
|
131
|
+
context 'when multiple node type symbols are passed' do
|
132
|
+
it 'scans all the ancestor nodes but yields only nodes matching any of the types' do
|
133
|
+
yielded_types = []
|
104
134
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
135
|
+
target_node.each_ancestor(:begin, :def) do |node|
|
136
|
+
yielded_types << node.type
|
137
|
+
end
|
109
138
|
|
110
|
-
|
139
|
+
expect(yielded_types).to eq([:def, :begin])
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'when an array including type symbols are passed' do
|
144
|
+
it 'scans all the ancestor nodes but yields only nodes matching any of the types' do
|
145
|
+
yielded_types = []
|
146
|
+
|
147
|
+
target_node.each_ancestor([:begin, :def]) do |node|
|
148
|
+
yielded_types << node.type
|
111
149
|
end
|
150
|
+
|
151
|
+
expect(yielded_types).to eq([:def, :begin])
|
112
152
|
end
|
113
153
|
end
|
114
154
|
end
|
@@ -146,21 +186,41 @@ module Astrolabe
|
|
146
186
|
end
|
147
187
|
end
|
148
188
|
|
149
|
-
|
150
|
-
|
151
|
-
|
189
|
+
include_examples 'node enumerator', :each_child_node
|
190
|
+
|
191
|
+
context 'when a node type symbol is passed' do
|
192
|
+
it 'scans all the child nodes but yields only nodes matching the type' do
|
193
|
+
yielded_types = []
|
194
|
+
|
195
|
+
target_node.each_child_node(:send) do |node|
|
196
|
+
yielded_types << node.type
|
197
|
+
end
|
198
|
+
|
199
|
+
expect(yielded_types).to eq([:send])
|
152
200
|
end
|
201
|
+
end
|
153
202
|
|
154
|
-
|
155
|
-
|
203
|
+
context 'when multiple node type symbols are passed' do
|
204
|
+
it 'scans all the child nodes but yields only nodes matching any of the types' do
|
205
|
+
yielded_types = []
|
156
206
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
207
|
+
target_node.each_child_node(:send, :args) do |node|
|
208
|
+
yielded_types << node.type
|
209
|
+
end
|
161
210
|
|
162
|
-
|
211
|
+
expect(yielded_types).to eq([:args, :send])
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'when an array including type symbols are passed' do
|
216
|
+
it 'scans all the child nodes but yields only nodes matching any of the types' do
|
217
|
+
yielded_types = []
|
218
|
+
|
219
|
+
target_node.each_child_node([:send, :args]) do |node|
|
220
|
+
yielded_types << node.type
|
163
221
|
end
|
222
|
+
|
223
|
+
expect(yielded_types).to eq([:args, :send])
|
164
224
|
end
|
165
225
|
end
|
166
226
|
end
|
@@ -207,21 +267,41 @@ module Astrolabe
|
|
207
267
|
end
|
208
268
|
end
|
209
269
|
|
210
|
-
|
211
|
-
|
212
|
-
|
270
|
+
include_examples 'node enumerator', :each_descendant
|
271
|
+
|
272
|
+
context 'when a node type symbol is passed' do
|
273
|
+
it 'scans all the descendant nodes but yields only nodes matching the type' do
|
274
|
+
yielded_types = []
|
275
|
+
|
276
|
+
target_node.each_descendant(:send) do |node|
|
277
|
+
yielded_types << node.type
|
278
|
+
end
|
279
|
+
|
280
|
+
expect(yielded_types).to eq([:send, :send])
|
213
281
|
end
|
282
|
+
end
|
214
283
|
|
215
|
-
|
216
|
-
|
284
|
+
context 'when multiple node type symbols are passed' do
|
285
|
+
it 'scans all the descendant nodes but yields only nodes matching any of the types' do
|
286
|
+
yielded_types = []
|
217
287
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
end
|
288
|
+
target_node.each_descendant(:send, :def) do |node|
|
289
|
+
yielded_types << node.type
|
290
|
+
end
|
222
291
|
|
223
|
-
|
292
|
+
expect(yielded_types).to eq([:send, :def, :send])
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context 'when an array including type symbols are passed' do
|
297
|
+
it 'scans all the descendant nodes but yields only nodes matching any of the types' do
|
298
|
+
yielded_types = []
|
299
|
+
|
300
|
+
target_node.each_descendant([:send, :def]) do |node|
|
301
|
+
yielded_types << node.type
|
224
302
|
end
|
303
|
+
|
304
|
+
expect(yielded_types).to eq([:send, :def, :send])
|
225
305
|
end
|
226
306
|
end
|
227
307
|
end
|
@@ -252,7 +332,7 @@ module Astrolabe
|
|
252
332
|
let(:expected_types) { [:class, :const, :begin, :send, :sym, :def, :args, :arg, :arg, :send] }
|
253
333
|
|
254
334
|
context 'when a block is given' do
|
255
|
-
it 'yields itself and each descendant node with depth first order' do
|
335
|
+
it 'yields the node itself and each descendant node with depth first order' do
|
256
336
|
yielded_types = []
|
257
337
|
|
258
338
|
target_node.each_node do |node|
|
@@ -268,21 +348,41 @@ module Astrolabe
|
|
268
348
|
end
|
269
349
|
end
|
270
350
|
|
271
|
-
|
272
|
-
|
273
|
-
|
351
|
+
include_examples 'node enumerator', :each_node
|
352
|
+
|
353
|
+
context 'when a node type symbol is passed' do
|
354
|
+
it 'scans all the nodes but yields only nodes matching the type' do
|
355
|
+
yielded_types = []
|
356
|
+
|
357
|
+
target_node.each_node(:send) do |node|
|
358
|
+
yielded_types << node.type
|
359
|
+
end
|
360
|
+
|
361
|
+
expect(yielded_types).to eq([:send, :send])
|
274
362
|
end
|
363
|
+
end
|
275
364
|
|
276
|
-
|
277
|
-
|
365
|
+
context 'when multiple node type symbols are passed' do
|
366
|
+
it 'scans all the nodes but yields only nodes matching any of the types' do
|
367
|
+
yielded_types = []
|
278
368
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
end
|
369
|
+
target_node.each_node(:send, :def) do |node|
|
370
|
+
yielded_types << node.type
|
371
|
+
end
|
283
372
|
|
284
|
-
|
373
|
+
expect(yielded_types).to eq([:send, :def, :send])
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
context 'when an array including type symbols are passed' do
|
378
|
+
it 'scans all the nodes but yields only nodes matching any of the types' do
|
379
|
+
yielded_types = []
|
380
|
+
|
381
|
+
target_node.each_node([:send, :def]) do |node|
|
382
|
+
yielded_types << node.type
|
285
383
|
end
|
384
|
+
|
385
|
+
expect(yielded_types).to eq([:send, :def, :send])
|
286
386
|
end
|
287
387
|
end
|
288
388
|
end
|