[ruby/prism] Move location to second position for node initializers

https://github.com/ruby/prism/commit/4cc0eda4ca
This commit is contained in:
Kevin Newton 2024-07-02 14:27:04 -04:00
Родитель 678dd769e5
Коммит ca48fb76fb
10 изменённых файлов: 364 добавлений и 152 удалений

Просмотреть файл

@ -2,11 +2,13 @@
module Prism
class DesugarAndWriteNode # :nodoc:
attr_reader :node, :source, :read_class, :write_class, :arguments
include DSL
def initialize(node, source, read_class, write_class, *arguments)
attr_reader :node, :default_source, :read_class, :write_class, :arguments
def initialize(node, default_source, read_class, write_class, **arguments)
@node = node
@source = source
@default_source = default_source
@read_class = read_class
@write_class = write_class
@arguments = arguments
@ -14,22 +16,30 @@ module Prism
# Desugar `x &&= y` to `x && x = y`
def compile
AndNode.new(
source,
read_class.new(source, *arguments, node.name_loc),
write_class.new(source, *arguments, node.name_loc, node.value, node.operator_loc, node.location),
node.operator_loc,
node.location
and_node(
location: node.location,
left: public_send(read_class, location: node.name_loc, **arguments),
right: public_send(
write_class,
location: node.location,
**arguments,
name_loc: node.name_loc,
value: node.value,
operator_loc: node.operator_loc
),
operator_loc: node.operator_loc
)
end
end
class DesugarOrWriteDefinedNode # :nodoc:
attr_reader :node, :source, :read_class, :write_class, :arguments
include DSL
def initialize(node, source, read_class, write_class, *arguments)
attr_reader :node, :default_source, :read_class, :write_class, :arguments
def initialize(node, default_source, read_class, write_class, **arguments)
@node = node
@source = source
@default_source = default_source
@read_class = read_class
@write_class = write_class
@arguments = arguments
@ -37,35 +47,50 @@ module Prism
# Desugar `x ||= y` to `defined?(x) ? x : x = y`
def compile
IfNode.new(
source,
node.operator_loc,
DefinedNode.new(source, nil, read_class.new(source, *arguments, node.name_loc), nil, node.operator_loc, node.name_loc),
node.operator_loc,
StatementsNode.new(source, [read_class.new(source, *arguments, node.name_loc)], node.location),
ElseNode.new(
source,
node.operator_loc,
StatementsNode.new(
source,
[write_class.new(source, *arguments, node.name_loc, node.value, node.operator_loc, node.location)],
node.location
),
node.operator_loc,
node.location
if_node(
location: node.location,
if_keyword_loc: node.operator_loc,
predicate: defined_node(
location: node.name_loc,
value: public_send(read_class, location: node.name_loc, **arguments),
keyword_loc: node.operator_loc
),
node.operator_loc,
node.location
then_keyword_loc: node.operator_loc,
statements: statements_node(
location: node.location,
body: [public_send(read_class, location: node.name_loc, **arguments)]
),
consequent: else_node(
location: node.location,
else_keyword_loc: node.operator_loc,
statements: statements_node(
location: node.location,
body: [
public_send(
write_class,
location: node.location,
**arguments,
name_loc: node.name_loc,
value: node.value,
operator_loc: node.operator_loc
)
]
),
end_keyword_loc: node.operator_loc
),
end_keyword_loc: node.operator_loc
)
end
end
class DesugarOperatorWriteNode # :nodoc:
attr_reader :node, :source, :read_class, :write_class, :arguments
include DSL
def initialize(node, source, read_class, write_class, *arguments)
attr_reader :node, :default_source, :read_class, :write_class, :arguments
def initialize(node, default_source, read_class, write_class, **arguments)
@node = node
@source = source
@default_source = default_source
@read_class = read_class
@write_class = write_class
@arguments = arguments
@ -75,35 +100,41 @@ module Prism
def compile
binary_operator_loc = node.binary_operator_loc.chop
write_class.new(
source,
*arguments,
node.name_loc,
CallNode.new(
source,
0,
read_class.new(source, *arguments, node.name_loc),
nil,
binary_operator_loc.slice.to_sym,
binary_operator_loc,
nil,
ArgumentsNode.new(source, 0, [node.value], node.value.location),
nil,
nil,
node.location
public_send(
write_class,
location: node.location,
**arguments,
name_loc: node.name_loc,
value: call_node(
location: node.location,
receiver: public_send(
read_class,
location: node.name_loc,
**arguments
),
name: binary_operator_loc.slice.to_sym,
message_loc: binary_operator_loc,
arguments: arguments_node(
location: node.value.location,
arguments: [node.value]
)
),
node.binary_operator_loc.copy(start_offset: node.binary_operator_loc.end_offset - 1, length: 1),
node.location
operator_loc: node.binary_operator_loc.copy(
start_offset: node.binary_operator_loc.end_offset - 1,
length: 1
)
)
end
end
class DesugarOrWriteNode # :nodoc:
attr_reader :node, :source, :read_class, :write_class, :arguments
include DSL
def initialize(node, source, read_class, write_class, *arguments)
attr_reader :node, :default_source, :read_class, :write_class, :arguments
def initialize(node, default_source, read_class, write_class, **arguments)
@node = node
@source = source
@default_source = default_source
@read_class = read_class
@write_class = write_class
@arguments = arguments
@ -111,12 +142,18 @@ module Prism
# Desugar `x ||= y` to `x || x = y`
def compile
OrNode.new(
source,
read_class.new(source, *arguments, node.name_loc),
write_class.new(source, *arguments, node.name_loc, node.value, node.operator_loc, node.location),
node.operator_loc,
node.location
or_node(
location: node.location,
left: public_send(read_class, location: node.name_loc, **arguments),
right: public_send(
write_class,
location: node.location,
**arguments,
name_loc: node.name_loc,
value: node.value,
operator_loc: node.operator_loc
),
operator_loc: node.operator_loc
)
end
end
@ -125,91 +162,91 @@ module Prism
class ClassVariableAndWriteNode
def desugar # :nodoc:
DesugarAndWriteNode.new(self, source, ClassVariableReadNode, ClassVariableWriteNode, name).compile
DesugarAndWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile
end
end
class ClassVariableOrWriteNode
def desugar # :nodoc:
DesugarOrWriteDefinedNode.new(self, source, ClassVariableReadNode, ClassVariableWriteNode, name).compile
DesugarOrWriteDefinedNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile
end
end
class ClassVariableOperatorWriteNode
def desugar # :nodoc:
DesugarOperatorWriteNode.new(self, source, ClassVariableReadNode, ClassVariableWriteNode, name).compile
DesugarOperatorWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile
end
end
class ConstantAndWriteNode
def desugar # :nodoc:
DesugarAndWriteNode.new(self, source, ConstantReadNode, ConstantWriteNode, name).compile
DesugarAndWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile
end
end
class ConstantOrWriteNode
def desugar # :nodoc:
DesugarOrWriteDefinedNode.new(self, source, ConstantReadNode, ConstantWriteNode, name).compile
DesugarOrWriteDefinedNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile
end
end
class ConstantOperatorWriteNode
def desugar # :nodoc:
DesugarOperatorWriteNode.new(self, source, ConstantReadNode, ConstantWriteNode, name).compile
DesugarOperatorWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile
end
end
class GlobalVariableAndWriteNode
def desugar # :nodoc:
DesugarAndWriteNode.new(self, source, GlobalVariableReadNode, GlobalVariableWriteNode, name).compile
DesugarAndWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile
end
end
class GlobalVariableOrWriteNode
def desugar # :nodoc:
DesugarOrWriteDefinedNode.new(self, source, GlobalVariableReadNode, GlobalVariableWriteNode, name).compile
DesugarOrWriteDefinedNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile
end
end
class GlobalVariableOperatorWriteNode
def desugar # :nodoc:
DesugarOperatorWriteNode.new(self, source, GlobalVariableReadNode, GlobalVariableWriteNode, name).compile
DesugarOperatorWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile
end
end
class InstanceVariableAndWriteNode
def desugar # :nodoc:
DesugarAndWriteNode.new(self, source, InstanceVariableReadNode, InstanceVariableWriteNode, name).compile
DesugarAndWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile
end
end
class InstanceVariableOrWriteNode
def desugar # :nodoc:
DesugarOrWriteNode.new(self, source, InstanceVariableReadNode, InstanceVariableWriteNode, name).compile
DesugarOrWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile
end
end
class InstanceVariableOperatorWriteNode
def desugar # :nodoc:
DesugarOperatorWriteNode.new(self, source, InstanceVariableReadNode, InstanceVariableWriteNode, name).compile
DesugarOperatorWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile
end
end
class LocalVariableAndWriteNode
def desugar # :nodoc:
DesugarAndWriteNode.new(self, source, LocalVariableReadNode, LocalVariableWriteNode, name, depth).compile
DesugarAndWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile
end
end
class LocalVariableOrWriteNode
def desugar # :nodoc:
DesugarOrWriteNode.new(self, source, LocalVariableReadNode, LocalVariableWriteNode, name, depth).compile
DesugarOrWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile
end
end
class LocalVariableOperatorWriteNode
def desugar # :nodoc:
DesugarOperatorWriteNode.new(self, source, LocalVariableReadNode, LocalVariableWriteNode, name, depth).compile
DesugarOperatorWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile
end
end

Просмотреть файл

@ -69,11 +69,11 @@ module Prism
def to_interpolated
InterpolatedStringNode.new(
source,
location,
frozen? ? InterpolatedStringNodeFlags::FROZEN : 0,
opening_loc,
[copy(opening_loc: nil, closing_loc: nil, location: content_loc)],
closing_loc,
location
[copy(location: content_loc, opening_loc: nil, closing_loc: nil)],
closing_loc
)
end
end
@ -86,10 +86,10 @@ module Prism
def to_interpolated
InterpolatedXStringNode.new(
source,
location,
opening_loc,
[StringNode.new(source, 0, nil, content_loc, nil, unescaped, content_loc)],
closing_loc,
location
[StringNode.new(source, content_loc, 0, nil, content_loc, nil, unescaped)],
closing_loc
)
end
end
@ -115,9 +115,9 @@ module Prism
deprecated("value", "numerator", "denominator")
if denominator == 1
IntegerNode.new(source, flags, numerator, location.chop)
IntegerNode.new(source, location.chop, flags, numerator)
else
FloatNode.new(source, numerator.to_f / denominator, location.chop)
FloatNode.new(source, location.chop, numerator.to_f / denominator)
end
end
end
@ -195,7 +195,7 @@ module Prism
# continue to supply that API.
def child
deprecated("name", "name_loc")
name ? ConstantReadNode.new(source, name, name_loc) : MissingNode.new(source, location)
name ? ConstantReadNode.new(source, name_loc, name) : MissingNode.new(source, location)
end
end
@ -231,7 +231,7 @@ module Prism
# continue to supply that API.
def child
deprecated("name", "name_loc")
name ? ConstantReadNode.new(source, name, name_loc) : MissingNode.new(source, location)
name ? ConstantReadNode.new(source, name_loc, name) : MissingNode.new(source, location)
end
end

Просмотреть файл

@ -10,7 +10,11 @@ module Prism
# specialized and more performant `ASCIISource` if no multibyte characters
# are present in the source code.
def self.for(source, start_line = 1, offsets = [])
source.ascii_only? ? ASCIISource.new(source, start_line, offsets): new(source, start_line, offsets)
if source.ascii_only?
ASCIISource.new(source, start_line, offsets)
else
new(source, start_line, offsets)
end
end
# The source code that this source object represents.
@ -87,7 +91,12 @@ module Prism
# encodings, it is not captured here.
def code_units_offset(byte_offset, encoding)
byteslice = (source.byteslice(0, byte_offset) or raise).encode(encoding)
(encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE) ? (byteslice.bytesize / 2) : byteslice.length
if encoding == Encoding::UTF_16LE || encoding == Encoding::UTF_16BE
byteslice.bytesize / 2
else
byteslice.length
end
end
# Returns the column number in code units for the given encoding for the

Просмотреть файл

@ -4,13 +4,18 @@ require "stringio"
module Prism
class ParseResult < Result
# An object to represent the set of errors on a parse result. This object
# can be used to format the errors in a human-readable way.
class Errors
# The parse result that contains the errors.
attr_reader :parse_result
# Initialize a new set of errors from the given parse result.
def initialize(parse_result)
@parse_result = parse_result
end
# Formats the errors in a human-readable way and return them as a string.
def format
error_lines = {}
parse_result.errors.each do |error|

Просмотреть файл

@ -177,7 +177,10 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi
// source
argv[0] = source;
<%- node.fields.each.with_index(1) do |field, index| -%>
// location
argv[1] = pm_location_new(parser, node->location.start, node->location.end);
<%- node.fields.each.with_index(2) do |field, index| -%>
// <%= field.name %>
<%- case field -%>
@ -232,9 +235,6 @@ pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encodi
<%- end -%>
<%- end -%>
// location
argv[<%= node.fields.length + 1 %>] = pm_location_new(parser, node->location.start, node->location.end);
rb_ary_push(value_stack, rb_class_new_instance(<%= node.fields.length + 2 %>, argv, rb_cPrism<%= node.name %>));
break;
}

Просмотреть файл

@ -5,45 +5,118 @@ module Prism
# source = Prism::Source.for("[1]")
#
# Prism::ArrayNode.new(
# source,
# Prism::Location.new(source, 0, 3),
# 0,
# [
# Prism::IntegerNode.new(
# Prism::IntegerBaseFlags::DECIMAL,
# 1,
# source,
# Prism::Location.new(source, 1, 1),
# source
# Prism::IntegerBaseFlags::DECIMAL,
# 1
# )
# ],
# Prism::Location.new(source, 0, 1),
# Prism::Location.new(source, 2, 1),
# source
# Prism::Location.new(source, 2, 1)
# )
#
# you could instead write:
#
# source = Prism::Source.for("[1]")
# class Builder
# include Prism::DSL
#
# ArrayNode(
# IntegerNode(Prism::IntegerBaseFlags::DECIMAL, 1, Location(source, 1, 1)), source),
# Location(source, 0, 1),
# Location(source, 2, 1),
# source
# )
# attr_reader :default_source
#
# This is mostly helpful in the context of writing tests, but can also be used
# to generate trees programmatically.
# def initialize
# @default_source = source("[1]")
# end
#
# def build
# array_node(
# location: location(start_offset: 0, length: 3),
# elements: [
# integer_node(
# location: location(start_offset: 1, length: 1),
# flags: integer_base_flag(:decimal),
# value: 1
# )
# ],
# opening_loc: location(start_offset: 0, length: 1),
# closing_loc: location(start_offset: 2, length: 1)
# )
# end
# end
#
# This is mostly helpful in the context of generating trees programmatically.
module DSL
private
# Provide all of these methods as module methods as well, to allow for
# building nodes like Prism::DSL.nil_node.
extend self
# Create a new Location object
def Location(source = nil, start_offset = 0, length = 0)
Location.new(source, start_offset, length) # steep:ignore
# Create a new Source object.
def source(string)
Source.for(string)
end
# Create a new Location object.
def location(source: default_source, start_offset: 0, length: 0)
Location.new(source, start_offset, length)
end
<%- nodes.each do |node| -%>
# Create a new <%= node.name %> node
def <%= node.name %>(<%= (node.fields.map(&:name) + ["source = nil, location = Location()"]).join(", ") %>)
<%= node.name %>.new(<%= ["source", *node.fields.map(&:name), "location"].join(", ") %>)
# Create a new <%= node.name %> node.
def <%= node.human %>(<%= ["source: default_source", "location: default_location", *node.fields.map { |field|
case field
when Prism::Template::NodeField, Prism::Template::ConstantField
"#{field.name}: default_node(source, location)"
when Prism::Template::OptionalNodeField, Prism::Template::OptionalConstantField, Prism::Template::OptionalLocationField
"#{field.name}: nil"
when Prism::Template::NodeListField, Prism::Template::ConstantListField
"#{field.name}: []"
when Prism::Template::StringField
"#{field.name}: \"\""
when Prism::Template::LocationField
"#{field.name}: location"
when Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::IntegerField, Prism::Template::DoubleField
"#{field.name}: 0"
else
raise
end
}].join(", ") %>)
<%= node.name %>.new(<%= ["source", "location", *node.fields.map(&:name)].join(", ") %>)
end
<%- end -%>
<%- flags.each do |flag| -%>
# Retrieve the value of one of the <%= flag.name %> flags.
def <%= flag.human.chomp("s") %>(name)
case name
<%- flag.values.each do |value| -%>
when :<%= value.name.downcase %> then <%= flag.name %>::<%= value.name %>
<%- end -%>
else raise ArgumentError, "invalid <%= flag.name %> flag: #{name.inspect}"
end
end
<%- end -%>
private
# The default source object that gets attached to nodes and locations if no
# source is specified.
def default_source
Source.for("")
end
# The default location object that gets attached to nodes if no location is
# specified, which uses the given source.
def default_location
Location.new(default_source, 0, 0)
end
# The default node that gets attached to nodes if no node is specified for a
# required node field.
def default_node(source, location)
MissingNode.new(source, location)
end
end
end

Просмотреть файл

@ -166,8 +166,8 @@ module Prism
#<%= line %>
<%- end -%>
class <%= node.name -%> < Node
# def initialize: (<%= (node.fields.map { |field| "#{field.rbs_class} #{field.name}" } + ["Location location"]).join(", ") %>) -> void
def initialize(source, <%= (node.fields.map(&:name) + ["location"]).join(", ") %>)
# Initialize a new <%= node.name %> node.
def initialize(<%= ["source", "location", *node.fields.map(&:name)].join(", ") %>)
@source = source
@location = location
<%- node.fields.each do |field| -%>
@ -228,9 +228,9 @@ module Prism
}.compact.join(", ") %>] #: Array[Prism::node | Location]
end
# def copy: (<%= (node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" } + ["?location: Location"]).join(", ") %>) -> <%= node.name %>
def copy(<%= (node.fields.map(&:name) + ["location"]).map { |field| "#{field}: self.#{field}" }.join(", ") %>)
<%= node.name %>.new(<%= ["source", *node.fields.map(&:name), "location"].join(", ") %>)
# def copy: (<%= (["?location: Location"] + node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" }).join(", ") %>) -> <%= node.name %>
def copy(<%= (["location"] + node.fields.map(&:name)).map { |field| "#{field}: self.#{field}" }.join(", ") %>)
<%= node.name %>.new(<%= ["source", "location", *node.fields.map(&:name)].join(", ") %>)
end
# def deconstruct: () -> Array[nil | Node]

Просмотреть файл

@ -319,7 +319,7 @@ module Prism
end
end
if RUBY_ENGINE == 'ruby'
if RUBY_ENGINE == "ruby"
def load_node
type = io.getbyte
location = load_location
@ -330,8 +330,7 @@ module Prism
<%- if node.needs_serialized_length? -%>
load_uint32
<%- end -%>
<%= node.name %>.new(
source, <%= (node.fields.map { |field|
<%= node.name %>.new(<%= ["source", "location", *node.fields.map { |field|
case field
when Prism::Template::NodeField then "load_node"
when Prism::Template::OptionalNodeField then "load_optional_node"
@ -348,7 +347,7 @@ module Prism
when Prism::Template::DoubleField then "load_double"
else raise
end
} + ["location"]).join(", ") -%>)
}].join(", ") -%>)
<%- end -%>
end
end
@ -367,8 +366,7 @@ module Prism
<%- if node.needs_serialized_length? -%>
load_uint32
<%- end -%>
<%= node.name %>.new(
source, <%= (node.fields.map { |field|
<%= node.name %>.new(<%= ["source", "location", *node.fields.map { |field|
case field
when Prism::Template::NodeField then "load_node"
when Prism::Template::OptionalNodeField then "load_optional_node"
@ -385,7 +383,7 @@ module Prism
when Prism::Template::DoubleField then "load_double"
else raise
end
} + ["location"]).join(", ") -%>)
}].join(", ") -%>)
},
<%- end -%>
]

Просмотреть файл

@ -1,2 +1,4 @@
<<~FOO.foo
^~~ unterminated heredoc; can't find string "FOO" anywhere before EOF
^~~ unterminated heredoc; can't find string "FOO" anywhere before EOF

Просмотреть файл

@ -1,8 +1,8 @@
@ ProgramNode (location: (1,0)-(217,5))
├── locals: [:bar, :baz, :qux, :b, :a, :foo, :x]
@ ProgramNode (location: (1,0)-(220,31))
├── locals: [:bar, :baz, :qux, :b, :a, :foo, :x, :_a]
└── statements:
@ StatementsNode (location: (1,0)-(217,5))
└── body: (length: 180)
@ StatementsNode (location: (1,0)-(220,31))
└── body: (length: 182)
├── @ MatchRequiredNode (location: (1,0)-(1,10))
│ ├── value:
│ │ @ CallNode (location: (1,0)-(1,3))
@ -4883,27 +4883,115 @@
│ │ └── operator_loc: (212,11)-(212,13) = "in"
│ ├── opening_loc: (212,7)-(212,8) = "{"
│ └── closing_loc: (212,18)-(212,19) = "}"
└── @ MultiWriteNode (location: (214,0)-(217,5))
├── lefts: (length: 2)
│ ├── @ LocalVariableTargetNode (location: (215,2)-(215,3))
│ │ ├── name: :a
│ │ └── depth: 0
│ └── @ LocalVariableTargetNode (location: (216,2)-(216,3))
│ ├── name: :b
│ └── depth: 0
├── rest: ∅
├── rights: (length: 0)
├── lparen_loc: (214,0)-(214,1) = "("
├── rparen_loc: (217,0)-(217,1) = ")"
├── operator_loc: (217,2)-(217,3) = "="
└── value:
@ CallNode (location: (217,4)-(217,5))
├── flags: variable_call, ignore_visibility
├── receiver: ∅
├── call_operator_loc: ∅
├── name: :c
├── message_loc: (217,4)-(217,5) = "c"
├── opening_loc: ∅
├── arguments: ∅
├── closing_loc: ∅
└── block: ∅
├── @ MultiWriteNode (location: (214,0)-(217,5))
│ ├── lefts: (length: 2)
│ │ ├── @ LocalVariableTargetNode (location: (215,2)-(215,3))
│ │ │ ├── name: :a
│ │ │ └── depth: 0
│ │ └── @ LocalVariableTargetNode (location: (216,2)-(216,3))
│ │ ├── name: :b
│ │ └── depth: 0
│ ├── rest: ∅
│ ├── rights: (length: 0)
│ ├── lparen_loc: (214,0)-(214,1) = "("
│ ├── rparen_loc: (217,0)-(217,1) = ")"
│ ├── operator_loc: (217,2)-(217,3) = "="
│ └── value:
│ @ CallNode (location: (217,4)-(217,5))
│ ├── flags: variable_call, ignore_visibility
│ ├── receiver: ∅
│ ├── call_operator_loc: ∅
│ ├── name: :c
│ ├── message_loc: (217,4)-(217,5) = "c"
│ ├── opening_loc: ∅
│ ├── arguments: ∅
│ ├── closing_loc: ∅
│ └── block: ∅
├── @ CaseMatchNode (location: (219,0)-(219,25))
│ ├── predicate:
│ │ @ ParenthesesNode (location: (219,5)-(219,7))
│ │ ├── body: ∅
│ │ ├── opening_loc: (219,5)-(219,6) = "("
│ │ └── closing_loc: (219,6)-(219,7) = ")"
│ ├── conditions: (length: 1)
│ │ └── @ InNode (location: (219,9)-(219,20))
│ │ ├── pattern:
│ │ │ @ ArrayPatternNode (location: (219,12)-(219,20))
│ │ │ ├── constant: ∅
│ │ │ ├── requireds: (length: 2)
│ │ │ │ ├── @ LocalVariableTargetNode (location: (219,13)-(219,15))
│ │ │ │ │ ├── name: :_a
│ │ │ │ │ └── depth: 0
│ │ │ │ └── @ LocalVariableTargetNode (location: (219,17)-(219,19))
│ │ │ │ ├── name: :_a
│ │ │ │ └── depth: 0
│ │ │ ├── rest: ∅
│ │ │ ├── posts: (length: 0)
│ │ │ ├── opening_loc: (219,12)-(219,13) = "["
│ │ │ └── closing_loc: (219,19)-(219,20) = "]"
│ │ ├── statements: ∅
│ │ ├── in_loc: (219,9)-(219,11) = "in"
│ │ └── then_loc: ∅
│ ├── consequent: ∅
│ ├── case_keyword_loc: (219,0)-(219,4) = "case"
│ └── end_keyword_loc: (219,22)-(219,25) = "end"
└── @ CaseMatchNode (location: (220,0)-(220,31))
├── predicate:
│ @ ParenthesesNode (location: (220,5)-(220,7))
│ ├── body: ∅
│ ├── opening_loc: (220,5)-(220,6) = "("
│ └── closing_loc: (220,6)-(220,7) = ")"
├── conditions: (length: 1)
│ └── @ InNode (location: (220,9)-(220,26))
│ ├── pattern:
│ │ @ ArrayPatternNode (location: (220,12)-(220,26))
│ │ ├── constant: ∅
│ │ ├── requireds: (length: 2)
│ │ │ ├── @ HashPatternNode (location: (220,13)-(220,18))
│ │ │ │ ├── constant: ∅
│ │ │ │ ├── elements: (length: 1)
│ │ │ │ │ └── @ AssocNode (location: (220,14)-(220,17))
│ │ │ │ │ ├── key:
│ │ │ │ │ │ @ SymbolNode (location: (220,14)-(220,16))
│ │ │ │ │ │ ├── flags: forced_us_ascii_encoding
│ │ │ │ │ │ ├── opening_loc: ∅
│ │ │ │ │ │ ├── value_loc: (220,14)-(220,15) = "a"
│ │ │ │ │ │ ├── closing_loc: (220,15)-(220,16) = ":"
│ │ │ │ │ │ └── unescaped: "a"
│ │ │ │ │ ├── value:
│ │ │ │ │ │ @ IntegerNode (location: (220,16)-(220,17))
│ │ │ │ │ │ ├── flags: decimal
│ │ │ │ │ │ └── value: 1
│ │ │ │ │ └── operator_loc: ∅
│ │ │ │ ├── rest: ∅
│ │ │ │ ├── opening_loc: (220,13)-(220,14) = "{"
│ │ │ │ └── closing_loc: (220,17)-(220,18) = "}"
│ │ │ └── @ HashPatternNode (location: (220,20)-(220,25))
│ │ │ ├── constant: ∅
│ │ │ ├── elements: (length: 1)
│ │ │ │ └── @ AssocNode (location: (220,21)-(220,24))
│ │ │ │ ├── key:
│ │ │ │ │ @ SymbolNode (location: (220,21)-(220,23))
│ │ │ │ │ ├── flags: forced_us_ascii_encoding
│ │ │ │ │ ├── opening_loc: ∅
│ │ │ │ │ ├── value_loc: (220,21)-(220,22) = "a"
│ │ │ │ │ ├── closing_loc: (220,22)-(220,23) = ":"
│ │ │ │ │ └── unescaped: "a"
│ │ │ │ ├── value:
│ │ │ │ │ @ IntegerNode (location: (220,23)-(220,24))
│ │ │ │ │ ├── flags: decimal
│ │ │ │ │ └── value: 2
│ │ │ │ └── operator_loc: ∅
│ │ │ ├── rest: ∅
│ │ │ ├── opening_loc: (220,20)-(220,21) = "{"
│ │ │ └── closing_loc: (220,24)-(220,25) = "}"
│ │ ├── rest: ∅
│ │ ├── posts: (length: 0)
│ │ ├── opening_loc: (220,12)-(220,13) = "["
│ │ └── closing_loc: (220,25)-(220,26) = "]"
│ ├── statements: ∅
│ ├── in_loc: (220,9)-(220,11) = "in"
│ └── then_loc: ∅
├── consequent: ∅
├── case_keyword_loc: (220,0)-(220,4) = "case"
└── end_keyword_loc: (220,28)-(220,31) = "end"