@@ -161,13 +161,6 @@ module YARP
161
161
<%- end -%>
162
162
inspector.to_str
163
163
end
164
-
165
- # Returns a symbol representation of the type of node.
166
- #
167
- # def human: () -> Symbol
168
- def human
169
- :<%= node . human %>
170
- end
171
164
end
172
165
173
166
<%- end -%>
@@ -189,9 +182,38 @@ module YARP
189
182
<%- end -%>
190
183
end
191
184
192
- # The dispatcher class fires events for nodes that are found while walking an AST to all registered listeners. It 's
193
- # useful for performing different types of analysis on the AST without having to repeat the same visits multiple times
194
- class Dispatcher
185
+ # The dispatcher class fires events for nodes that are found while walking an
186
+ # AST to all registered listeners. It's useful for performing different types
187
+ # of analysis on the AST while only having to walk the tree once.
188
+ #
189
+ # To use the dispatcher, you would first instantiate it and register listeners
190
+ # for the events you're interested in:
191
+ #
192
+ # class OctalListener
193
+ # def on_integer_node_enter(node)
194
+ # if node.octal? && !node.slice.start_with?("0o")
195
+ # warn("Octal integers should be written with the 0o prefix")
196
+ # end
197
+ # end
198
+ # end
199
+ #
200
+ # dispatcher = Dispatcher.new
201
+ # dispatcher.register(listener, :on_integer_node_enter)
202
+ #
203
+ # Then, you can walk any number of trees and dispatch events to the listeners:
204
+ #
205
+ # result = YARP.parse("001 + 002 + 003")
206
+ # dispatcher.dispatch(result.value)
207
+ #
208
+ # Optionally, you can also use `#dispatch_once` to dispatch enter and leave
209
+ # events for a single node without recursing further down the tree. This can
210
+ # be useful in circumstances where you want to reuse the listeners you already
211
+ # have registers but want to stop walking the tree at a certain point.
212
+ #
213
+ # integer = result.value.statements.body.first.receiver.receiver
214
+ # dispatcher.dispatch_once(integer)
215
+ #
216
+ class Dispatcher < Visitor
195
217
# attr_reader listeners: Hash[Symbol, Array[Listener]]
196
218
attr_reader :listeners
197
219
@@ -209,33 +231,39 @@ module YARP
209
231
# Walks `root` dispatching events to all registered listeners
210
232
#
211
233
# def dispatch: (Node) -> void
212
- def dispatch(root)
213
- queue = [root]
214
-
215
- while (node = queue.shift)
216
- case node.human
217
- <%- nodes . each do |node | -%>
218
- when :<%= node . human %>
219
- listeners[:<%= node . human %> _enter]&.each { |listener| listener.<%= node . human %> _enter(node) }
220
- queue = node.compact_child_nodes.concat(queue)
221
- listeners[:<%= node . human %> _leave]&.each { |listener| listener.<%= node . human %> _leave(node) }
222
- <%- end -%>
223
- end
224
- end
225
- end
234
+ alias dispatch visit
226
235
227
236
# Dispatches a single event for `node` to all registered listeners
228
237
#
229
238
# def dispatch_once: (Node) -> void
230
239
def dispatch_once(node)
231
- case node.human
240
+ node.accept(DispatchOnce.new(listeners))
241
+ end
242
+ <%- nodes . each do |node | -%>
243
+
244
+ def visit_<%= node . human %> (node)
245
+ listeners[:on_<%= node . human %> _enter]&.each { |listener| listener.on_<%= node . human %> _enter(node) }
246
+ super
247
+ listeners[:on_<%= node . human %> _leave]&.each { |listener| listener.on_<%= node . human %> _leave(node) }
248
+ end
249
+ <%- end -%>
250
+
251
+ class DispatchOnce < Visitor
252
+ attr_reader :listeners
253
+
254
+ def initialize(listeners)
255
+ @listeners = listeners
256
+ end
232
257
<%- nodes . each do |node | -%>
233
- when : <%= node . human %>
234
- listeners[: <%= node . human %> _enter]&.each { |listener| listener. <%= node . human %> _enter (node) }
235
- listeners[:<%= node . human %> _leave ]&.each { |listener| listener.<%= node . human %> _leave (node) }
236
- <%- end -%>
258
+
259
+ def visit_ <%= node . human %> (node)
260
+ listeners[:on_ <%= node . human %> _enter ]&.each { |listener| listener.on_ <%= node . human %> _enter (node) }
261
+ listeners[:on_ <%= node . human %> _leave]&.each { |listener| listener.on_ <%= node . human %> _leave(node) }
237
262
end
263
+ <%- end -%>
238
264
end
265
+
266
+ private_constant :DispatchOnce
239
267
end
240
268
241
269
module DSL
0 commit comments