[ruby/prism] Remove Ripper from public RBS, type-assert remaining issues
https://github.com/ruby/prism/commit/5fda7a0760
This commit is contained in:
parent
7556fd937c
commit
935d4fab62
@ -106,14 +106,23 @@ module Prism
|
||||
# local variable
|
||||
class DynamicPartsInConstantPathError < StandardError; end
|
||||
|
||||
# An error class raised when missing nodes are found while computing a
|
||||
# constant path's full name. For example:
|
||||
# Foo:: -> raises because the constant path is missing the last part
|
||||
class MissingNodesInConstantPathError < StandardError; end
|
||||
|
||||
# Returns the list of parts for the full name of this constant path.
|
||||
# For example: [:Foo, :Bar]
|
||||
def full_name_parts
|
||||
parts = [child.name]
|
||||
current = parent
|
||||
parts = [] #: Array[Symbol]
|
||||
current = self #: node?
|
||||
|
||||
while current.is_a?(ConstantPathNode)
|
||||
parts.unshift(current.child.name)
|
||||
child = current.child
|
||||
if child.is_a?(MissingNode)
|
||||
raise MissingNodesInConstantPathError, "Constant path contains missing nodes. Cannot compute full name"
|
||||
end
|
||||
parts.unshift(child.name)
|
||||
current = current.parent
|
||||
end
|
||||
|
||||
@ -134,14 +143,19 @@ module Prism
|
||||
# Returns the list of parts for the full name of this constant path.
|
||||
# For example: [:Foo, :Bar]
|
||||
def full_name_parts
|
||||
parts = case parent
|
||||
when ConstantPathNode, ConstantReadNode
|
||||
parent.full_name_parts
|
||||
when nil
|
||||
[:""]
|
||||
else
|
||||
raise ConstantPathNode::DynamicPartsInConstantPathError,
|
||||
"Constant path target contains dynamic parts. Cannot compute full name"
|
||||
parts =
|
||||
case parent
|
||||
when ConstantPathNode, ConstantReadNode
|
||||
parent.full_name_parts
|
||||
when nil
|
||||
[:""]
|
||||
else
|
||||
# e.g. self::Foo, (var)::Bar = baz
|
||||
raise ConstantPathNode::DynamicPartsInConstantPathError, "Constant target path contains dynamic parts. Cannot compute full name"
|
||||
end
|
||||
|
||||
if child.is_a?(MissingNode)
|
||||
raise ConstantPathNode::MissingNodesInConstantPathError, "Constant target path contains missing nodes. Cannot compute full name"
|
||||
end
|
||||
|
||||
parts.push(child.name)
|
||||
@ -169,7 +183,7 @@ module Prism
|
||||
class ParametersNode < Node
|
||||
# Mirrors the Method#parameters method.
|
||||
def signature
|
||||
names = [] #: Array[[:req | :opt | :rest | :keyreq | :key | :keyrest | :block, Symbol] | [:req | :rest | :keyrest | :nokey]]
|
||||
names = [] #: Array[[Symbol, Symbol] | [Symbol]]
|
||||
|
||||
requireds.each do |param|
|
||||
names << (param.is_a?(MultiTargetNode) ? [:req] : [:req, param.name])
|
||||
@ -182,7 +196,14 @@ module Prism
|
||||
end
|
||||
|
||||
posts.each do |param|
|
||||
names << (param.is_a?(MultiTargetNode) ? [:req] : [:req, param.name])
|
||||
if param.is_a?(MultiTargetNode)
|
||||
names << [:req]
|
||||
elsif param.is_a?(NoKeywordsParameterNode)
|
||||
# Invalid syntax, e.g. "def f(**nil, ...)" moves the NoKeywordsParameterNode to posts
|
||||
raise "Invalid syntax"
|
||||
else
|
||||
names << [:req, param.name]
|
||||
end
|
||||
end
|
||||
|
||||
# Regardless of the order in which the keywords were defined, the required
|
||||
|
@ -281,7 +281,8 @@ module Prism
|
||||
# the beginning of the file. Useful for when you want a location object but
|
||||
# do not care where it points.
|
||||
def self.null
|
||||
new(nil, 0, 0)
|
||||
source = nil #: Source
|
||||
new(source, 0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -188,7 +188,12 @@ module Prism
|
||||
|
||||
# Attach the list of comments to their respective locations in the tree.
|
||||
def attach_comments!
|
||||
Comments.new(self).attach!
|
||||
if ProgramNode === value
|
||||
this = self #: ParseResult[ProgramNode]
|
||||
Comments.new(this).attach!
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -122,7 +122,6 @@ Gem::Specification.new do |spec|
|
||||
"src/options.c",
|
||||
"src/prism.c",
|
||||
"prism.gemspec",
|
||||
"sig/manifest.yaml",
|
||||
"sig/prism.rbs",
|
||||
"sig/prism/compiler.rbs",
|
||||
"sig/prism/dispatcher.rbs",
|
||||
|
208
lib/prism/ripper_compat.rb
Normal file
208
lib/prism/ripper_compat.rb
Normal file
@ -0,0 +1,208 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "ripper"
|
||||
|
||||
module Prism
|
||||
# Note: This integration is not finished, and therefore still has many
|
||||
# inconsistencies with Ripper. If you'd like to help out, pull requests would
|
||||
# be greatly appreciated!
|
||||
#
|
||||
# This class is meant to provide a compatibility layer between prism 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 prism. It is also
|
||||
# meant as a test harness for the prism parser.
|
||||
#
|
||||
# To use this class, you treat `Prism::RipperCompat` effectively as you would
|
||||
# treat the `Ripper` class.
|
||||
class RipperCompat < Visitor
|
||||
# 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 # :nodoc:
|
||||
[]
|
||||
end
|
||||
|
||||
def _dispatch_event_push(list, item) # :nodoc:
|
||||
list << item
|
||||
list
|
||||
end
|
||||
|
||||
Ripper::PARSER_EVENT_TABLE.each do |event, arity|
|
||||
case event
|
||||
when /_new\z/
|
||||
alias_method :"on_#{event}", :_dispatch_event_new if arity == 0
|
||||
when /_add\z/
|
||||
alias_method :"on_#{event}", :_dispatch_event_push
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The source that is being parsed.
|
||||
attr_reader :source
|
||||
|
||||
# The current line number of the parser.
|
||||
attr_reader :lineno
|
||||
|
||||
# The current column number of the parser.
|
||||
attr_reader :column
|
||||
|
||||
# Create a new RipperCompat object with the given source.
|
||||
def initialize(source)
|
||||
@source = source
|
||||
@result = nil
|
||||
@lineno = nil
|
||||
@column = nil
|
||||
end
|
||||
|
||||
############################################################################
|
||||
# Public interface
|
||||
############################################################################
|
||||
|
||||
# True if the parser encountered an error during parsing.
|
||||
def error?
|
||||
result.failure?
|
||||
end
|
||||
|
||||
# Parse the source and return the result.
|
||||
def parse
|
||||
result.magic_comments.each do |magic_comment|
|
||||
on_magic_comment(magic_comment.key, magic_comment.value)
|
||||
end
|
||||
|
||||
if error?
|
||||
result.errors.each do |error|
|
||||
on_parse_error(error.message)
|
||||
end
|
||||
else
|
||||
result.value.accept(self)
|
||||
end
|
||||
end
|
||||
|
||||
############################################################################
|
||||
# Visitor methods
|
||||
############################################################################
|
||||
|
||||
# Visit a CallNode node.
|
||||
def visit_call_node(node)
|
||||
message = node.message
|
||||
if message && message.match?(/^[[:alpha:]_]/) && node.opening_loc.nil? && node.arguments && node.arguments.arguments && node.arguments.arguments.length == 1
|
||||
left = visit(node.receiver)
|
||||
right = visit(node.arguments.arguments.first)
|
||||
|
||||
bounds(node.location)
|
||||
on_binary(left, node.name, right)
|
||||
else
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
||||
# Visit a FloatNode node.
|
||||
def visit_float_node(node)
|
||||
bounds(node.location)
|
||||
on_float(node.slice)
|
||||
end
|
||||
|
||||
# Visit a ImaginaryNode node.
|
||||
def visit_imaginary_node(node)
|
||||
bounds(node.location)
|
||||
on_imaginary(node.slice)
|
||||
end
|
||||
|
||||
# Visit an IntegerNode node.
|
||||
def visit_integer_node(node)
|
||||
bounds(node.location)
|
||||
on_int(node.slice)
|
||||
end
|
||||
|
||||
# Visit a RationalNode node.
|
||||
def visit_rational_node(node)
|
||||
bounds(node.location)
|
||||
on_rational(node.slice)
|
||||
end
|
||||
|
||||
# Visit a StatementsNode node.
|
||||
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
|
||||
|
||||
# Visit a ProgramNode node.
|
||||
def visit_program_node(node)
|
||||
statements = visit(node.statements)
|
||||
bounds(node.location)
|
||||
on_program(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)
|
||||
@lineno = location.start_line
|
||||
@column = location.start_column
|
||||
end
|
||||
|
||||
# Lazily initialize the parse result.
|
||||
def result
|
||||
@result ||= Prism.parse(source)
|
||||
end
|
||||
|
||||
def _dispatch0; end # :nodoc:
|
||||
def _dispatch1(_); end # :nodoc:
|
||||
def _dispatch2(_, _); end # :nodoc:
|
||||
def _dispatch3(_, _, _); end # :nodoc:
|
||||
def _dispatch4(_, _, _, _); end # :nodoc:
|
||||
def _dispatch5(_, _, _, _, _); end # :nodoc:
|
||||
def _dispatch7(_, _, _, _, _, _, _); end # :nodoc:
|
||||
|
||||
alias_method :on_parse_error, :_dispatch1
|
||||
alias_method :on_magic_comment, :_dispatch2
|
||||
|
||||
(Ripper::SCANNER_EVENT_TABLE.merge(Ripper::PARSER_EVENT_TABLE)).each do |event, arity|
|
||||
alias_method :"on_#{event}", :"_dispatch#{arity}"
|
||||
end
|
||||
end
|
||||
end
|
@ -36,6 +36,7 @@ module Prism
|
||||
|
||||
# Create a new Location object
|
||||
def Location(source = nil, start_offset = 0, length = 0)
|
||||
# @type var source: Source
|
||||
Location.new(source, start_offset, length)
|
||||
end
|
||||
<%- nodes.each do |node| -%>
|
||||
|
@ -56,6 +56,7 @@ module Prism
|
||||
|
||||
# Convert this node into a graphviz dot graph string.
|
||||
def to_dot
|
||||
# @type self: node
|
||||
DotVisitor.new.tap { |visitor| accept(visitor) }.to_dot
|
||||
end
|
||||
|
||||
|
@ -7,16 +7,19 @@ module Prism
|
||||
# Calls `accept` on the given node if it is not `nil`, which in turn should
|
||||
# call back into this visitor by calling the appropriate `visit_*` method.
|
||||
def visit(node)
|
||||
# @type self: _Visitor
|
||||
node&.accept(self)
|
||||
end
|
||||
|
||||
# Visits each node in `nodes` by calling `accept` on each one.
|
||||
def visit_all(nodes)
|
||||
# @type self: _Visitor
|
||||
nodes.each { |node| node&.accept(self) }
|
||||
end
|
||||
|
||||
# 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) }
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user