Skip to content

Commit e346fa5

Browse files
committed
Support &. calls and calling with blocks, test with fixtures
1 parent 997f419 commit e346fa5

File tree

2 files changed

+49
-11
lines changed

2 files changed

+49
-11
lines changed

lib/prism/ripper_compat.rb

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,44 +142,71 @@ def visit_call_node(node)
142142
raise NotImplementedError, "More than two arguments for operator"
143143
end
144144
elsif node.call_operator_loc.nil?
145-
# In Ripper a method call like "puts myvar" with no parenthesis is a "command"
145+
# In Ripper a method call like "puts myvar" with no parenthesis is a "command".
146146
bounds(node.message_loc)
147147
ident_val = on_ident(node.message)
148-
args = args_node_to_arguments(node.arguments)
149-
return on_command(ident_val, args)
148+
args = node.arguments.nil? ? nil : args_node_to_arguments(node.arguments)
149+
150+
# Unless it has a block, and then it's an fcall (e.g. "foo { bar }")
151+
if node.block
152+
block_val = visit(node.block)
153+
# In these calls, even if node.arguments is nil, we still get an :args_new call.
154+
method_args_val = on_method_add_arg(on_fcall(ident_val), args_node_to_arguments(node.arguments))
155+
return on_method_add_block(method_args_val, on_brace_block(nil, block_val))
156+
else
157+
return on_command(ident_val, args)
158+
end
150159
else
151160
operator = node.call_operator_loc.slice
152-
if operator == "."
161+
if operator == "." || operator == "&."
153162
left_val = visit(node.receiver)
154163

155164
bounds(node.call_operator_loc)
156-
dot_val = on_period(node.call_operator)
165+
operator_val = operator == "." ? on_period(node.call_operator) : on_op(node.call_operator)
157166

158167
bounds(node.message_loc)
159168
right_val = on_ident(node.message)
160169

161-
return on_call(left_val, dot_val, right_val)
170+
call_val = on_call(left_val, operator_val, right_val)
171+
172+
if node.block
173+
block_val = visit(node.block)
174+
return on_method_add_block(call_val, on_brace_block(nil, block_val))
175+
else
176+
return call_val
177+
end
162178
else
163-
raise NotImplementedError, "operator other than dot for call: #{operator.inspect}"
179+
raise NotImplementedError, "operator other than . or &. for call: #{operator.inspect}"
164180
end
165181
end
166182
end
167183

168184
# A non-operator method call with parentheses
169-
args = on_arg_paren(args_node_to_arguments(node.arguments))
185+
args = on_arg_paren(node.arguments.nil? ? nil : args_node_to_arguments(node.arguments))
170186

171187
bounds(node.message_loc)
172188
ident_val = on_ident(node.message)
173189

174190
bounds(node.location)
175191
args_call_val = on_method_add_arg(on_fcall(ident_val), args)
176192
if node.block
177-
raise NotImplementedError, "Method call with a block!"
193+
block_val = visit(node.block)
194+
195+
return on_method_add_block(args_call_val, on_brace_block(nil, block_val))
178196
else
179197
return args_call_val
180198
end
181199
end
182200

201+
# Visit a BlockNode
202+
def visit_block_node(node)
203+
if node.body.nil?
204+
on_stmts_add(on_stmts_new, on_void_stmt)
205+
else
206+
visit(node.body)
207+
end
208+
end
209+
183210
# Visit an AndNode
184211
def visit_and_node(node)
185212
visit_binary_operator(node)
@@ -256,7 +283,7 @@ def visit_statements_node(node)
256283
# Ripper generates an interesting format of argument list.
257284
# We'd like to convert an ArgumentsNode to one.
258285
def args_node_to_arguments(args_node)
259-
return nil if args_node.nil?
286+
return on_args_new if args_node.nil?
260287

261288
args = on_args_new
262289
args_node.arguments.each do |arg|

test/prism/ripper_compat_test.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def test_method_calls_with_variable_names
3838
assert_equivalent("foo.🗻")
3939
assert_equivalent("🗻.😮!")
4040
assert_equivalent("🗻 🗻,🗻,🗻")
41+
assert_equivalent("foo&.bar")
42+
assert_equivalent("foo { bar }")
43+
assert_equivalent("foo.bar { 7 }")
44+
assert_equivalent("foo(1) { bar }")
4145
end
4246

4347
def test_method_calls_on_immediate_values
@@ -84,7 +88,11 @@ def assert_equivalent(source)
8488
class RipperCompatFixturesTest < TestCase
8589
#base = File.join(__dir__, "fixtures")
8690
#relatives = ENV["FOCUS"] ? [ENV["FOCUS"]] : Dir["**/*.txt", base: base]
87-
relatives = ["arithmetic.txt", "integer_operations.txt"]
91+
relatives = [
92+
"arithmetic.txt",
93+
"comments.txt",
94+
"integer_operations.txt",
95+
]
8896

8997
relatives.each do |relative|
9098
define_method "test_ripper_filepath_#{relative}" do
@@ -95,6 +103,9 @@ class RipperCompatFixturesTest < TestCase
95103
source = File.read(path, binmode: true, external_encoding: Encoding::UTF_8)
96104

97105
expected = Ripper.sexp_raw(source)
106+
if expected.nil?
107+
puts "Could not parse #{path.inspect}!"
108+
end
98109
refute_nil expected
99110
assert_equal expected, RipperCompat.sexp_raw(source)
100111
end

0 commit comments

Comments
 (0)