Skip to content

Commit

Permalink
Merge pull request #2291 from ruby/raise-diagnostics
Browse files Browse the repository at this point in the history
Raise diagnostics for parser
  • Loading branch information
kddnewton authored Jan 29, 2024
2 parents 6e44d64 + 102b4a1 commit a938fd0
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 17 deletions.
16 changes: 16 additions & 0 deletions bin/parser
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler/setup"
require "prism"
require "parser/current"

source = (ARGV[0] == "-e") ? ARGV[1] : File.read(ARGV[0] || "test.rb")
prism = Prism::Translation::Parser.parse(source)
parser = Parser::CurrentRuby.parse(source)

puts "Prism:"
pp prism

puts "Parser:"
pp parser
57 changes: 42 additions & 15 deletions lib/prism/translation/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ module Translation
# the parser gem, and overrides the parse* methods to parse with prism and
# then translate.
class Parser < ::Parser::Base
# The parser gem has a list of diagnostics with a hard-coded set of error
# messages. We create our own diagnostic class in order to set our own
# error messages.
class Diagnostic < ::Parser::Diagnostic
# The message generated by prism.
attr_reader :message

# Initialize a new diagnostic with the given message and location.
def initialize(message, location)
@message = message
super(:error, :prism_error, {}, location, [])
end
end

Racc_debug_parser = false # :nodoc:

def version # :nodoc:
Expand All @@ -28,10 +42,9 @@ def parse(source_buffer)
@source_buffer = source_buffer
source = source_buffer.source

build_ast(
Prism.parse(source, filepath: source_buffer.name).value,
build_offset_cache(source)
)
result = unwrap(Prism.parse(source, filepath: source_buffer.name))

build_ast(result.value, build_offset_cache(source))
ensure
@source_buffer = nil
end
Expand All @@ -42,7 +55,7 @@ def parse_with_comments(source_buffer)
source = source_buffer.source

offset_cache = build_offset_cache(source)
result = Prism.parse(source, filepath: source_buffer.name)
result = unwrap(Prism.parse(source, filepath: source_buffer.name))

[
build_ast(result.value, offset_cache),
Expand All @@ -59,7 +72,8 @@ def tokenize(source_buffer, _recover = false)
source = source_buffer.source

offset_cache = build_offset_cache(source)
result = Prism.parse_lex(source, filepath: source_buffer.name)
result = unwrap(Prism.parse_lex(source, filepath: source_buffer.name))

program, tokens = result.value

[
Expand All @@ -79,6 +93,18 @@ def try_declare_numparam(node)

private

# If there was a error generated during the parse, then raise an
# appropriate syntax error. Otherwise return the result.
def unwrap(result)
return result if result.success?

error = result.errors.first
offset_cache = build_offset_cache(source_buffer.source)

diagnostic = Diagnostic.new(error.message, build_range(error.location, offset_cache))
raise ::Parser::SyntaxError, diagnostic
end

# Prism deals with offsets in bytes, while the parser gem deals with
# offsets in characters. We need to handle this conversion in order to
# build the parser gem AST.
Expand Down Expand Up @@ -109,15 +135,7 @@ def build_ast(program, offset_cache)
# Build the parser gem comments from the prism comments.
def build_comments(comments, offset_cache)
comments.map do |comment|
location = comment.location

::Parser::Source::Comment.new(
::Parser::Source::Range.new(
source_buffer,
offset_cache[location.start_offset],
offset_cache[location.end_offset]
)
)
::Parser::Source::Comment.new(build_range(comment.location, offset_cache))
end
end

Expand All @@ -126,6 +144,15 @@ def build_tokens(tokens, offset_cache)
Lexer.new(source_buffer, tokens.map(&:first), offset_cache).to_a
end

# Build a range from a prism location.
def build_range(location, offset_cache)
::Parser::Source::Range.new(
source_buffer,
offset_cache[location.start_offset],
offset_cache[location.end_offset]
)
end

require_relative "parser/compiler"
require_relative "parser/lexer"

Expand Down
9 changes: 7 additions & 2 deletions lib/prism/translation/parser/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,16 @@ def visit_array_pattern_node(node)
elements = [*node.requireds]
elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
elements.concat(node.posts)
visited = visit_all(elements)

if node.rest.is_a?(ImplicitRestNode)
visited[-1] = builder.match_with_trailing_comma(visited[-1], token(node.rest.location))
end

if node.constant
builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visit_all(elements), nil), token(node.closing_loc))
builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visited, nil), token(node.closing_loc))
else
builder.array_pattern(token(node.opening_loc), visit_all(elements), token(node.closing_loc))
builder.array_pattern(token(node.opening_loc), visited, token(node.closing_loc))
end
end

Expand Down

0 comments on commit a938fd0

Please sign in to comment.