Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions lib/prism/translation/parser/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,55 @@ class Parser
# A builder that knows how to convert more modern Ruby syntax
# into whitequark/parser gem's syntax tree.
class Builder < ::Parser::Builders::Default
# It represents the `it` block argument, which is not yet implemented in the Parser gem.
def itarg
n(:itarg, [:it], nil)
end

# The following three lines have been added to support the `it` block parameter syntax in the source code below.
#
# if args.type == :itarg
# block_type = :itblock
# args = :it
#
# https://github.com/whitequark/parser/blob/v3.3.7.1/lib/parser/builders/default.rb#L1122-L1155
def block(method_call, begin_t, args, body, end_t)
_receiver, _selector, *call_args = *method_call

if method_call.type == :yield
diagnostic :error, :block_given_to_yield, nil, method_call.loc.keyword, [loc(begin_t)]
end

last_arg = call_args.last
if last_arg && (last_arg.type == :block_pass || last_arg.type == :forwarded_args)
diagnostic :error, :block_and_blockarg, nil, last_arg.loc.expression, [loc(begin_t)]
end

if args.type == :itarg
block_type = :itblock
args = :it
elsif args.type == :numargs
block_type = :numblock
args = args.children[0]
else
block_type = :block
end

if [:send, :csend, :index, :super, :zsuper, :lambda].include?(method_call.type)
n(block_type, [ method_call, args, body ],
block_map(method_call.loc.expression, begin_t, end_t))
else
# Code like "return foo 1 do end" is reduced in a weird sequence.
# Here, method_call is actually (return).
actual_send, = *method_call
block =
n(block_type, [ actual_send, args, body ],
block_map(actual_send.loc.expression, begin_t, end_t))

n(method_call.type, [ block ],
method_call.loc.with_expression(join_exprs(method_call, block)))
end
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/prism/translation/parser/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1157,7 +1157,7 @@ def visit_it_local_variable_read_node(node)
# -> { it }
# ^^^^^^^^^
def visit_it_parameters_node(node)
builder.args(nil, [], nil, false)
builder.itarg
end

# foo(bar: baz)
Expand Down
21 changes: 21 additions & 0 deletions test/prism/ruby/parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
verbose, $VERBOSE = $VERBOSE, nil
require "parser/ruby33"
require "prism/translation/parser33"
require "prism/translation/parser34"
rescue LoadError
# In CRuby's CI, we're not going to test against the parser gem because we
# don't want to have to install it. So in this case we'll just skip this test.
Expand Down Expand Up @@ -144,6 +145,22 @@ class ParserTest < TestCase
end
end

def test_it_block_parameter_syntax
it_fixture_path = Pathname(__dir__).join("../../../test/prism/fixtures/it.txt")

buffer = Parser::Source::Buffer.new(it_fixture_path)
buffer.source = it_fixture_path.read
actual_ast = Prism::Translation::Parser34.new.tokenize(buffer)[0]

it_block_parameter_sexp = parse_sexp {
s(:itblock,
s(:send, nil, :x), :it,
s(:lvar, :it))
}

assert_equal(it_block_parameter_sexp, actual_ast.to_sexp)
end

private

def assert_equal_parses(fixture, compare_asts: true, compare_tokens: true, compare_comments: true)
Expand Down Expand Up @@ -242,5 +259,9 @@ def assert_equal_comments(expected_comments, actual_comments)
"actual: #{actual_comments.inspect}"
}
end

def parse_sexp(&block)
Class.new { extend AST::Sexp }.instance_eval(&block).to_sexp
end
end
end
Loading