diff options
author | Kevin Newton <[email protected]> | 2023-09-22 10:32:34 -0400 |
---|---|---|
committer | Kevin Newton <[email protected]> | 2023-09-27 12:10:23 -0400 |
commit | af8d475281858eccdbc1e557c795d81d6badd589 (patch) | |
tree | a192ee0fea947489cf2627132be3d7e75253e302 | |
parent | be861053c5e19783d75c1d2cd66d6007b9ba5072 (diff) |
[ruby/yarp] Move dispatcher into its own autoload
https://github.com/ruby/yarp/commit/52bd001ea2
-rw-r--r-- | lib/yarp.rb | 6 | ||||
-rw-r--r-- | lib/yarp/yarp.gemspec | 1 | ||||
-rw-r--r-- | yarp/templates/lib/yarp/dispatcher.rb.erb | 88 | ||||
-rw-r--r-- | yarp/templates/lib/yarp/node.rb.erb | 84 | ||||
-rwxr-xr-x | yarp/templates/template.rb | 1 |
5 files changed, 96 insertions, 84 deletions
diff --git a/lib/yarp.rb b/lib/yarp.rb index 41b71811b6..808349206f 100644 --- a/lib/yarp.rb +++ b/lib/yarp.rb @@ -542,6 +542,12 @@ module YARP # annoying for testing since you have to const_get it to access the methods, # but at least this way it's clear it's not meant for consumers. private_constant :Debug + + # There are many files in YARP that are templated to handle every node type, + # which means the files can end up being quite large. We autoload them to make + # our require speed faster since consuming libraries are unlikely to use all + # of these features. + autoload :Dispatcher, "yarp/dispatcher" end require_relative "yarp/lex_compat" diff --git a/lib/yarp/yarp.gemspec b/lib/yarp/yarp.gemspec index 186ce7556d..8696204ccf 100644 --- a/lib/yarp/yarp.gemspec +++ b/lib/yarp/yarp.gemspec @@ -60,6 +60,7 @@ Gem::Specification.new do |spec| "include/yarp/version.h", "lib/yarp.rb", "lib/yarp/desugar_visitor.rb", + "lib/yarp/dispatcher.rb", "lib/yarp/ffi.rb", "lib/yarp/lex_compat.rb", "lib/yarp/mutation_visitor.rb", diff --git a/yarp/templates/lib/yarp/dispatcher.rb.erb b/yarp/templates/lib/yarp/dispatcher.rb.erb new file mode 100644 index 0000000000..40d66ff6f1 --- /dev/null +++ b/yarp/templates/lib/yarp/dispatcher.rb.erb @@ -0,0 +1,88 @@ +module YARP + # The dispatcher class fires events for nodes that are found while walking an + # AST to all registered listeners. It's useful for performing different types + # of analysis on the AST while only having to walk the tree once. + # + # To use the dispatcher, you would first instantiate it and register listeners + # for the events you're interested in: + # + # class OctalListener + # def on_integer_node_enter(node) + # if node.octal? && !node.slice.start_with?("0o") + # warn("Octal integers should be written with the 0o prefix") + # end + # end + # end + # + # dispatcher = Dispatcher.new + # dispatcher.register(listener, :on_integer_node_enter) + # + # Then, you can walk any number of trees and dispatch events to the listeners: + # + # result = YARP.parse("001 + 002 + 003") + # dispatcher.dispatch(result.value) + # + # Optionally, you can also use `#dispatch_once` to dispatch enter and leave + # events for a single node without recursing further down the tree. This can + # be useful in circumstances where you want to reuse the listeners you already + # have registers but want to stop walking the tree at a certain point. + # + # integer = result.value.statements.body.first.receiver.receiver + # dispatcher.dispatch_once(integer) + # + class Dispatcher < Visitor + # attr_reader listeners: Hash[Symbol, Array[Listener]] + attr_reader :listeners + + def initialize + @listeners = {} + end + + # Register a listener for one or more events + # + # def register: (Listener, *Symbol) -> void + def register(listener, *events) + events.each { |event| (listeners[event] ||= []) << listener } + end + + # Walks `root` dispatching events to all registered listeners + # + # def dispatch: (Node) -> void + alias dispatch visit + + # Dispatches a single event for `node` to all registered listeners + # + # def dispatch_once: (Node) -> void + def dispatch_once(node) + node.accept(DispatchOnce.new(listeners)) + end + <%- nodes.each do |node| -%> + + # Dispatch enter and leave events for <%= node.name %> nodes and continue + # walking the tree. + def visit_<%= node.human %>(node) + listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) } + super + listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) } + end + <%- end -%> + + class DispatchOnce < Visitor + attr_reader :listeners + + def initialize(listeners) + @listeners = listeners + end + <%- nodes.each do |node| -%> + + # Dispatch enter and leave events for <%= node.name %> nodes. + def visit_<%= node.human %>(node) + listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) } + listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) } + end + <%- end -%> + end + + private_constant :DispatchOnce + end +end diff --git a/yarp/templates/lib/yarp/node.rb.erb b/yarp/templates/lib/yarp/node.rb.erb index c6362e7197..91de6f46f4 100644 --- a/yarp/templates/lib/yarp/node.rb.erb +++ b/yarp/templates/lib/yarp/node.rb.erb @@ -182,90 +182,6 @@ module YARP <%- end -%> end - # The dispatcher class fires events for nodes that are found while walking an - # AST to all registered listeners. It's useful for performing different types - # of analysis on the AST while only having to walk the tree once. - # - # To use the dispatcher, you would first instantiate it and register listeners - # for the events you're interested in: - # - # class OctalListener - # def on_integer_node_enter(node) - # if node.octal? && !node.slice.start_with?("0o") - # warn("Octal integers should be written with the 0o prefix") - # end - # end - # end - # - # dispatcher = Dispatcher.new - # dispatcher.register(listener, :on_integer_node_enter) - # - # Then, you can walk any number of trees and dispatch events to the listeners: - # - # result = YARP.parse("001 + 002 + 003") - # dispatcher.dispatch(result.value) - # - # Optionally, you can also use `#dispatch_once` to dispatch enter and leave - # events for a single node without recursing further down the tree. This can - # be useful in circumstances where you want to reuse the listeners you already - # have registers but want to stop walking the tree at a certain point. - # - # integer = result.value.statements.body.first.receiver.receiver - # dispatcher.dispatch_once(integer) - # - class Dispatcher < Visitor - # attr_reader listeners: Hash[Symbol, Array[Listener]] - attr_reader :listeners - - def initialize - @listeners = {} - end - - # Register a listener for one or more events - # - # def register: (Listener, *Symbol) -> void - def register(listener, *events) - events.each { |event| (listeners[event] ||= []) << listener } - end - - # Walks `root` dispatching events to all registered listeners - # - # def dispatch: (Node) -> void - alias dispatch visit - - # Dispatches a single event for `node` to all registered listeners - # - # def dispatch_once: (Node) -> void - def dispatch_once(node) - node.accept(DispatchOnce.new(listeners)) - end - <%- nodes.each do |node| -%> - - def visit_<%= node.human %>(node) - listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) } - super - listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) } - end - <%- end -%> - - class DispatchOnce < Visitor - attr_reader :listeners - - def initialize(listeners) - @listeners = listeners - end - <%- nodes.each do |node| -%> - - def visit_<%= node.human %>(node) - listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) } - listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) } - end - <%- end -%> - end - - private_constant :DispatchOnce - end - module DSL private diff --git a/yarp/templates/template.rb b/yarp/templates/template.rb index 8d02b22231..17cb52a2ff 100755 --- a/yarp/templates/template.rb +++ b/yarp/templates/template.rb @@ -366,6 +366,7 @@ module YARP "java/org/yarp/Loader.java", "java/org/yarp/Nodes.java", "java/org/yarp/AbstractNodeVisitor.java", + "lib/yarp/dispatcher.rb", "lib/yarp/mutation_visitor.rb", "lib/yarp/node.rb", "lib/yarp/serialize.rb", |