diff --git a/lib/prism/translation/parser.rb b/lib/prism/translation/parser.rb index 589b33b6fe..60b62186ba 100644 --- a/lib/prism/translation/parser.rb +++ b/lib/prism/translation/parser.rb @@ -26,7 +26,7 @@ module Prism Racc_debug_parser = false # :nodoc: def version # :nodoc: - 33 + 34 end # The default encoding for Ruby files is UTF-8. @@ -42,9 +42,10 @@ module Prism @source_buffer = source_buffer source = source_buffer.source - result = unwrap(Prism.parse(source, filepath: source_buffer.name)) + offset_cache = build_offset_cache(source) + result = unwrap(Prism.parse(source, filepath: source_buffer.name), offset_cache) - build_ast(result.value, build_offset_cache(source)) + build_ast(result.value, offset_cache) ensure @source_buffer = nil end @@ -55,7 +56,7 @@ module Prism source = source_buffer.source offset_cache = build_offset_cache(source) - result = unwrap(Prism.parse(source, filepath: source_buffer.name)) + result = unwrap(Prism.parse(source, filepath: source_buffer.name), offset_cache) [ build_ast(result.value, offset_cache), @@ -72,7 +73,7 @@ module Prism source = source_buffer.source offset_cache = build_offset_cache(source) - result = unwrap(Prism.parse_lex(source, filepath: source_buffer.name)) + result = unwrap(Prism.parse_lex(source, filepath: source_buffer.name), offset_cache) program, tokens = result.value @@ -93,16 +94,23 @@ module Prism private + # This is a hook to allow consumers to disable some errors if they don't + # want them to block creating the syntax tree. + def valid_error?(error) + true + end + # 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? + def unwrap(result, offset_cache) + result.errors.each do |error| + next unless valid_error?(error) - error = result.errors.first - offset_cache = build_offset_cache(source_buffer.source) + location = build_range(error.location, offset_cache) + diagnostics.process(Diagnostic.new(error.message, location)) + end - diagnostic = Diagnostic.new(error.message, build_range(error.location, offset_cache)) - raise ::Parser::SyntaxError, diagnostic + result end # Prism deals with offsets in bytes, while the parser gem deals with diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb index a70d8152d6..3c7cd79834 100644 --- a/lib/prism/translation/parser/compiler.rb +++ b/lib/prism/translation/parser/compiler.rb @@ -241,53 +241,51 @@ module Prism block = nil end + if node.call_operator_loc.nil? + case name + when :! + return visit_block(builder.not_op(token(node.message_loc), token(node.opening_loc), visit(node.receiver), token(node.closing_loc)), block) + when :[] + return visit_block(builder.index(visit(node.receiver), token(node.opening_loc), visit_all(arguments), token(node.closing_loc)), block) + when :[]= + if node.message != "[]=" && node.arguments && block.nil? && !node.safe_navigation? + return visit_block( + builder.assign( + builder.index_asgn( + visit(node.receiver), + token(node.opening_loc), + visit_all(node.arguments.arguments[...-1]), + token(node.closing_loc), + ), + srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, ["="]), + visit(node.arguments.arguments.last) + ), + block + ) + end + end + end + + message_loc = node.message_loc + call_operator_loc = node.call_operator_loc + call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc + visit_block( - if name == :! - builder.not_op( - token(node.message_loc), - token(node.opening_loc), - visit(node.receiver), - token(node.closing_loc) + if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil? + builder.assign( + builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)), + srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]), + visit(node.arguments.arguments.last) ) - elsif name == :[] - builder.index( + else + builder.call_method( visit(node.receiver), + call_operator, + message_loc ? [node.name, srange(message_loc)] : nil, token(node.opening_loc), visit_all(arguments), token(node.closing_loc) ) - elsif name == :[]= && node.message != "[]=" && node.arguments && block.nil? - builder.assign( - builder.index_asgn( - visit(node.receiver), - token(node.opening_loc), - visit_all(node.arguments.arguments[...-1]), - token(node.closing_loc), - ), - srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, ["="]), - visit(node.arguments.arguments.last) - ) - else - message_loc = node.message_loc - call_operator_loc = node.call_operator_loc - call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc - - if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil? - builder.assign( - builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)), - srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]), - visit(node.arguments.arguments.last) - ) - else - builder.call_method( - visit(node.receiver), - call_operator, - message_loc ? [node.name, srange(message_loc)] : nil, - token(node.opening_loc), - visit_all(arguments), - token(node.closing_loc) - ) - end end, block ) @@ -519,8 +517,6 @@ module Prism # def self.foo; end # ^^^^^^^^^^^^^^^^^ def visit_def_node(node) - forwarding = find_forwarding(node.parameters) - if node.equal_loc if node.receiver builder.def_endless_singleton( @@ -530,7 +526,7 @@ module Prism token(node.name_loc), builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false), token(node.equal_loc), - node.body&.accept(copy_compiler(forwarding: forwarding)) + node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))) ) else builder.def_endless_method( @@ -538,7 +534,7 @@ module Prism token(node.name_loc), builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false), token(node.equal_loc), - node.body&.accept(copy_compiler(forwarding: forwarding)) + node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))) ) end elsif node.receiver @@ -548,7 +544,7 @@ module Prism token(node.operator_loc), token(node.name_loc), builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false), - node.body&.accept(copy_compiler(forwarding: forwarding)), + node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))), token(node.end_keyword_loc) ) else @@ -556,7 +552,7 @@ module Prism token(node.def_keyword_loc), token(node.name_loc), builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false), - node.body&.accept(copy_compiler(forwarding: forwarding)), + node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))), token(node.end_keyword_loc) ) end @@ -993,24 +989,24 @@ module Prism # -> {} def visit_lambda_node(node) + parameters = node.parameters + builder.block( builder.call_lambda(token(node.operator_loc)), [node.opening, srange(node.opening_loc)], - if node.parameters - if node.parameters.is_a?(NumberedParametersNode) - visit(node.parameters) - else - builder.args( - token(node.parameters.opening_loc), - visit(node.parameters), - token(node.parameters.closing_loc), - false - ) - end - else + if parameters.nil? builder.args(nil, [], nil, false) + elsif node.parameters.is_a?(NumberedParametersNode) + visit(node.parameters) + else + builder.args( + token(node.parameters.opening_loc), + visit(node.parameters), + token(node.parameters.closing_loc), + false + ) end, - node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters&.parameters))), + node.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))), [node.closing, srange(node.closing_loc)] ) end @@ -1096,7 +1092,7 @@ module Prism # case of a syntax error. The parser gem doesn't have such a concept, so # we invent our own here. def visit_missing_node(node) - raise CompilationError, "Cannot compile missing nodes" + ::AST::Node.new(:missing, [], location: ::Parser::Source::Map.new(srange(node.location))) end # module Foo; end @@ -1727,29 +1723,29 @@ module Prism # Visit a block node on a call. def visit_block(call, block) if block + parameters = block.parameters + builder.block( call, token(block.opening_loc), - if (parameters = block.parameters) - if parameters.is_a?(NumberedParametersNode) - visit(parameters) - else - builder.args( - token(parameters.opening_loc), - if procarg0?(parameters.parameters) - parameter = parameters.parameters.requireds.first - [builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals)) - else - visit(parameters) - end, - token(parameters.closing_loc), - false - ) - end - else + if parameters.nil? builder.args(nil, [], nil, false) + elsif parameters.is_a?(NumberedParametersNode) + visit(parameters) + else + builder.args( + token(parameters.opening_loc), + if procarg0?(parameters.parameters) + parameter = parameters.parameters.requireds.first + [builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals)) + else + visit(parameters) + end, + token(parameters.closing_loc), + false + ) end, - block.body&.accept(copy_compiler(forwarding: find_forwarding(block.parameters&.parameters))), + block.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))), token(block.closing_loc) ) else @@ -1762,9 +1758,9 @@ module Prism children = [] node.parts.each do |part| pushing = - if part.is_a?(StringNode) && part.unescaped.count("\n") > 1 - unescaped = part.unescaped.split("\n") - escaped = part.content.split("\n") + if part.is_a?(StringNode) && part.unescaped.include?("\n") + unescaped = part.unescaped.lines(chomp: true) + escaped = part.content.lines(chomp: true) escaped_lengths = if node.opening.end_with?("'") @@ -1779,7 +1775,6 @@ module Prism unescaped.zip(escaped_lengths).map do |unescaped_line, escaped_length| end_offset = start_offset + (escaped_length || 0) inner_part = builder.string_internal(["#{unescaped_line}\n", srange_offsets(start_offset, end_offset)]) - start_offset = end_offset inner_part end