@@ -48,6 +48,31 @@ module YARP
48
48
} . compact . join ( ", " ) %> ]
49
49
end
50
50
51
+ # def compact_child_nodes: () -> Array[Node]
52
+ def compact_child_nodes
53
+ <%- if node . fields . any? { |field | field . is_a? ( YARP ::OptionalNodeField ) } -%>
54
+ compact = []
55
+ <%- node . fields . each do |field | -%>
56
+ <%- case field -%>
57
+ <%- when YARP ::NodeField -%>
58
+ compact < < <%= field . name %>
59
+ <%- when YARP ::OptionalNodeField -%>
60
+ compact < < <%= field . name %> if <%= field . name %>
61
+ <%- when YARP ::NodeListField -%>
62
+ compact.concat(<%= field . name %> )
63
+ <%- end -%>
64
+ <%- end -%>
65
+ compact
66
+ <%- else -%>
67
+ [<%= node . fields . map { |field |
68
+ case field
69
+ when YARP ::NodeField then field . name
70
+ when YARP ::NodeListField then "*#{ field . name } "
71
+ end
72
+ } . compact . join ( ", " ) %> ]
73
+ <%- end -%>
74
+ end
75
+
51
76
# def comment_targets: () - > Array[Node | Location]
52
77
def comment_targets
53
78
[<%= node . fields . map { |field |
@@ -136,6 +161,13 @@ module YARP
136
161
<%- end -%>
137
162
inspector.to_str
138
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
139
171
end
140
172
141
173
<%- end -%>
@@ -157,6 +189,55 @@ module YARP
157
189
<%- end -%>
158
190
end
159
191
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
195
+ # attr_reader listeners: Hash[Symbol, Array[Listener]]
196
+ attr_reader :listeners
197
+
198
+ def initialize
199
+ @listeners = {}
200
+ end
201
+
202
+ # Register a listener for one or more events
203
+ #
204
+ # def register: (Listener, *Symbol) - > void
205
+ def register(listener, *events)
206
+ events.each { |event| (listeners[event] ||= []) < < listener }
207
+ end
208
+
209
+ # Walks `root` dispatching events to all registered listeners
210
+ #
211
+ # 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
226
+
227
+ # Dispatches a single event for `node` to all registered listeners
228
+ #
229
+ # def dispatch_once: (Node) -> void
230
+ def dispatch_once(node)
231
+ case node.human
232
+ <%- 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 -%>
237
+ end
238
+ end
239
+ end
240
+
160
241
module DSL
161
242
private
162
243
0 commit comments