Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/ruby_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Once you have nodes in hand coming out of a parse result, there are a number of
* `#accept(visitor)` - a method that will immediately call `visit_*` to specialize for the node type
* `#child_nodes` - a positional array of the child nodes of the node, with `nil` values for any missing children
* `#compact_child_nodes` - a positional array of the child nodes of the node with no `nil` values
* `#each_child_node` - with a block given yields all child nodes, without a block return an enumerator containing all child nodes
* `#copy(**keys)` - a method that allows creating a shallow copy of the node with the given keys overridden
* `#deconstruct`/`#deconstruct_keys(keys)` - the pattern matching interface for nodes
* `#inspect` - a string representation that looks like the syntax tree of the node
Expand Down
2 changes: 1 addition & 1 deletion lib/prism/translation/ruby_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1422,7 +1422,7 @@ def visit_or_node(node)
# ```
def visit_parameters_node(node)
children =
node.compact_child_nodes.map do |element|
node.each_child_node.map do |element|
if element.is_a?(MultiTargetNode)
visit_destructured_parameter(element)
else
Expand Down
4 changes: 2 additions & 2 deletions sample/prism/find_comments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def initialize(target, comments, nesting = [])
# method definition on the node.
def visit_module_node(node)
visitor = FindMethodComments.new(@target, @comments, [*@nesting, node.name])
node.compact_child_nodes.each { |child| child.accept(visitor) }
node.each_child_node { |child| child.accept(visitor) }
end

def visit_class_node(node)
Expand All @@ -26,7 +26,7 @@ def visit_class_node(node)
# because then the state is immutable and it's easier to reason about. This
# also provides for more debugging opportunity in the initializer.
visitor = FindMethodComments.new(@target, @comments, [*@nesting, node.name])
node.compact_child_nodes.each { |child| child.accept(visitor) }
node.each_child_node { |child| child.accept(visitor) }
end

def visit_def_node(node)
Expand Down
11 changes: 6 additions & 5 deletions sample/prism/locate_nodes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ def locate(node, line:, column:)
while (node = queue.shift)
result << node

# Nodes have `child_nodes` and `compact_child_nodes`. `child_nodes` have
# consistent indices but include `nil` for optional fields that are not
# present, whereas `compact_child_nodes` has inconsistent indices but does
# not include `nil` for optional fields that are not present.
node.compact_child_nodes.find do |child|
# Nodes have `child_nodes`, `compact_child_nodes`, and `each_child_node`.
# `child_nodes` have consistent indices but include `nil` for optional fields that
# are not present, whereas `compact_child_nodes` has inconsistent indices but does
# not include `nil` for optional fields that are not present. `each_child_node` is
# like `compact_child_nodes`, returning an enumerator instead of an array.
node.each_child_node.find do |child|
queue << child if covers?(child.location, line: line, column: column)
end
end
Expand Down
4 changes: 2 additions & 2 deletions templates/lib/prism/compiler.rb.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ module Prism

# Visit the child nodes of the given node.
def visit_child_nodes(node)
node.compact_child_nodes.map { |node| node.accept(self) }
node.each_child_node.map { |node| node.accept(self) }
end

<%- nodes.each_with_index do |node, index| -%>
<%= "\n" if index != 0 -%>
# Compile a <%= node.name %> node
def visit_<%= node.human %>(node)
node.compact_child_nodes.map { |node| node.accept(self) }
node.each_child_node.map { |node| node.accept(self) }
end
<%- end -%>
end
Expand Down
25 changes: 24 additions & 1 deletion templates/lib/prism/node.rb.erb
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ module Prism
while (node = queue.shift)
result << node

node.compact_child_nodes.each do |child_node|
node.each_child_node do |child_node|
child_location = child_node.location

start_line = child_location.start_line
Expand Down Expand Up @@ -259,6 +259,13 @@ module Prism

alias deconstruct child_nodes

# With a block given, yields each child node. Without a block, returns
# an enumerator that contains each child node. Excludes any `nil`s in
# the place of optional nodes that were not present.
def each_child_node
raise NoMethodError, "undefined method `each_child_node' for #{inspect}"
end

# Returns an array of child nodes, excluding any `nil`s in the place of
# optional nodes that were not present.
def compact_child_nodes
Expand Down Expand Up @@ -335,6 +342,22 @@ module Prism
}.compact.join(", ") %>]
end

# def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node]
def each_child_node
return to_enum(:each_child_node) unless block_given?

<%- node.fields.each do |field| -%>
<%- case field -%>
<%- when Prism::Template::NodeField -%>
yield <%= field.name %>
<%- when Prism::Template::OptionalNodeField -%>
yield <%= field.name %> if <%= field.name %>
<%- when Prism::Template::NodeListField -%>
<%= field.name %>.each {|node| yield node }
<%- end -%>
<%- end -%>
end

# def compact_child_nodes: () -> Array[Node]
def compact_child_nodes
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be implemented as each_child_node.to_a but performance is not so great

<%- if node.fields.any? { |field| field.is_a?(Prism::Template::OptionalNodeField) } -%>
Expand Down
4 changes: 2 additions & 2 deletions templates/lib/prism/visitor.rb.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module Prism
# Visits the child nodes of `node` by calling `accept` on each one.
def visit_child_nodes(node)
# @type self: _Visitor
node.compact_child_nodes.each { |node| node.accept(self) }
node.each_child_node { |node| node.accept(self) }
end
end

Expand Down Expand Up @@ -48,7 +48,7 @@ module Prism
<%= "\n" if index != 0 -%>
# Visit a <%= node.name %> node
def visit_<%= node.human %>(node)
node.compact_child_nodes.each { |node| node.accept(self) }
node.each_child_node { |node| node.accept(self) }
end
<%- end -%>
end
Expand Down
1 change: 1 addition & 0 deletions templates/sig/prism/node.rbs.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module Prism
def child_nodes: () -> Array[Prism::node?]
def comment_targets: () -> Array[Prism::node | Location]
def compact_child_nodes: () -> Array[Prism::node]
def each_child_node: () { (Prism::node) -> void } -> void | () -> Enumerator[Prism::node]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure if this is correct. I'm also missing sorbet signatures which I don't know how to write for this

def self.fields: () -> Array[Prism::Reflection::Field]
def type: () -> Symbol
def self.type: () -> Symbol
Expand Down