mirror of
https://github.com/ruby/ruby.git
synced 2025-09-15 08:33:58 +02:00
[Feature #19741] Sync all files in yarp
This commit is the initial sync of all files from ruby/yarp into ruby/ruby. Notably, it does the following: * Sync all ruby/yarp/lib/ files to ruby/ruby/lib/yarp * Sync all ruby/yarp/src/ files to ruby/ruby/yarp/ * Sync all ruby/yarp/test/ files to ruby/ruby/test/yarp
This commit is contained in:
parent
08478fefca
commit
cc7f765f2c
Notes:
git
2023-06-21 18:26:01 +00:00
1898 changed files with 105766 additions and 0 deletions
174
lib/yarp/ripper_compat.rb
Normal file
174
lib/yarp/ripper_compat.rb
Normal file
|
@ -0,0 +1,174 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "ripper"
|
||||
|
||||
module YARP
|
||||
# This class is meant to provide a compatibility layer between YARP and
|
||||
# Ripper. It functions by parsing the entire tree first and then walking it
|
||||
# and executing each of the Ripper callbacks as it goes.
|
||||
#
|
||||
# This class is going to necessarily be slower than the native Ripper API. It
|
||||
# is meant as a stopgap until developers migrate to using YARP. It is also
|
||||
# meant as a test harness for the YARP parser.
|
||||
class RipperCompat
|
||||
# This class mirrors the ::Ripper::SexpBuilder subclass of ::Ripper that
|
||||
# returns the arrays of [type, *children].
|
||||
class SexpBuilder < RipperCompat
|
||||
private
|
||||
|
||||
Ripper::PARSER_EVENTS.each do |event|
|
||||
define_method(:"on_#{event}") do |*args|
|
||||
[event, *args]
|
||||
end
|
||||
end
|
||||
|
||||
Ripper::SCANNER_EVENTS.each do |event|
|
||||
define_method(:"on_#{event}") do |value|
|
||||
[:"@#{event}", value, [lineno, column]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This class mirrors the ::Ripper::SexpBuilderPP subclass of ::Ripper that
|
||||
# returns the same values as ::Ripper::SexpBuilder except with a couple of
|
||||
# niceties that flatten linked lists into arrays.
|
||||
class SexpBuilderPP < SexpBuilder
|
||||
private
|
||||
|
||||
def _dispatch_event_new
|
||||
[]
|
||||
end
|
||||
|
||||
def _dispatch_event_push(list, item)
|
||||
list << item
|
||||
list
|
||||
end
|
||||
|
||||
Ripper::PARSER_EVENT_TABLE.each do |event, arity|
|
||||
case event
|
||||
when /_new\z/
|
||||
alias :"on_#{event}" :_dispatch_event_new if arity == 0
|
||||
when /_add\z/
|
||||
alias :"on_#{event}" :_dispatch_event_push
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :source, :lineno, :column
|
||||
|
||||
def initialize(source)
|
||||
@source = source
|
||||
@result = nil
|
||||
@lineno = nil
|
||||
@column = nil
|
||||
end
|
||||
|
||||
############################################################################
|
||||
# Public interface
|
||||
############################################################################
|
||||
|
||||
def error?
|
||||
result.errors.any?
|
||||
end
|
||||
|
||||
def parse
|
||||
result.value.accept(self) unless error?
|
||||
end
|
||||
|
||||
############################################################################
|
||||
# Visitor methods
|
||||
############################################################################
|
||||
|
||||
def visit(node)
|
||||
node&.accept(self)
|
||||
end
|
||||
|
||||
def visit_call_node(node)
|
||||
if !node.opening_loc && node.arguments.arguments.length == 1
|
||||
bounds(node.receiver.location)
|
||||
left = visit(node.receiver)
|
||||
|
||||
bounds(node.arguments.arguments.first.location)
|
||||
right = visit(node.arguments.arguments.first)
|
||||
|
||||
on_binary(left, source[node.message_loc.start_offset...node.message_loc.end_offset].to_sym, right)
|
||||
else
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
||||
def visit_integer_node(node)
|
||||
bounds(node.location)
|
||||
on_int(source[node.location.start_offset...node.location.end_offset])
|
||||
end
|
||||
|
||||
def visit_statements_node(node)
|
||||
bounds(node.location)
|
||||
node.body.inject(on_stmts_new) do |stmts, stmt|
|
||||
on_stmts_add(stmts, visit(stmt))
|
||||
end
|
||||
end
|
||||
|
||||
def visit_token(node)
|
||||
bounds(node.location)
|
||||
|
||||
case node.type
|
||||
when :MINUS
|
||||
on_op(node.value)
|
||||
when :PLUS
|
||||
on_op(node.value)
|
||||
else
|
||||
raise NotImplementedError, "Unknown token: #{node.type}"
|
||||
end
|
||||
end
|
||||
|
||||
def visit_program_node(node)
|
||||
bounds(node.location)
|
||||
on_program(visit(node.statements))
|
||||
end
|
||||
|
||||
############################################################################
|
||||
# Entrypoints for subclasses
|
||||
############################################################################
|
||||
|
||||
# This is a convenience method that runs the SexpBuilder subclass parser.
|
||||
def self.sexp_raw(source)
|
||||
SexpBuilder.new(source).parse
|
||||
end
|
||||
|
||||
# This is a convenience method that runs the SexpBuilderPP subclass parser.
|
||||
def self.sexp(source)
|
||||
SexpBuilderPP.new(source).parse
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# This method is responsible for updating lineno and column information
|
||||
# to reflect the current node.
|
||||
#
|
||||
# This method could be drastically improved with some caching on the start
|
||||
# of every line, but for now it's good enough.
|
||||
def bounds(location)
|
||||
start_offset = location.start_offset
|
||||
|
||||
@lineno = source[0..start_offset].count("\n") + 1
|
||||
@column = start_offset - (source.rindex("\n", start_offset) || 0)
|
||||
end
|
||||
|
||||
def result
|
||||
@result ||= YARP.parse(source)
|
||||
end
|
||||
|
||||
def _dispatch0; end
|
||||
def _dispatch1(_); end
|
||||
def _dispatch2(_, _); end
|
||||
def _dispatch3(_, _, _); end
|
||||
def _dispatch4(_, _, _, _); end
|
||||
def _dispatch5(_, _, _, _, _); end
|
||||
def _dispatch7(_, _, _, _, _, _, _); end
|
||||
|
||||
(Ripper::SCANNER_EVENT_TABLE.merge(Ripper::PARSER_EVENT_TABLE)).each do |event, arity|
|
||||
alias :"on_#{event}" :"_dispatch#{arity}"
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue