diff options
| -rw-r--r-- | lib/prism/ripper_compat.rb | 154 | ||||
| -rw-r--r-- | test/prism/ripper_compat_test.rb | 30 |
2 files changed, 156 insertions, 28 deletions
diff --git a/lib/prism/ripper_compat.rb b/lib/prism/ripper_compat.rb index 44983f8596..5c3b56d01b 100644 --- a/lib/prism/ripper_compat.rb +++ b/lib/prism/ripper_compat.rb @@ -132,7 +132,12 @@ module Prism end # A non-operator method call with parentheses - args = on_arg_paren(node.arguments.nil? ? nil : args_node_to_arguments(node.arguments)) + + args = if node.arguments.nil? + on_arg_paren(nil) + else + on_arg_paren(on_args_add_block(visit_elements(node.arguments.arguments), false)) + end bounds(node.message_loc) ident_val = on_ident(node.message) @@ -142,31 +147,92 @@ module Prism if node.block block_val = visit(node.block) - return on_method_add_block(args_call_val, on_brace_block(nil, block_val)) + return on_method_add_block(args_call_val, block_val) else return args_call_val end end - # Visit a BlockNode + # Visit a LocalVariableAndWriteNode. + def visit_local_variable_and_write_node(node) + visit_binary_op_assign(node) + end + + # Visit a LocalVariableOrWriteNode. + def visit_local_variable_or_write_node(node) + visit_binary_op_assign(node) + end + + # Visit nodes for +=, *=, -=, etc., called LocalVariableOperatorWriteNodes. + def visit_local_variable_operator_write_node(node) + visit_binary_op_assign(node, operator: node.operator.to_s + "=") + end + + # Visit a LocalVariableReadNode. + def visit_local_variable_read_node(node) + bounds(node.location) + ident_val = on_ident(node.slice) + + on_var_ref(ident_val) + end + + # Visit a BlockNode. def visit_block_node(node) - if node.body.nil? - on_stmts_add(on_stmts_new, on_void_stmt) - else - visit(node.body) - end + params_val = node.parameters.nil? ? nil : visit(node.parameters) + + body_val = node.body.nil? ? on_stmts_add(on_stmts_new, on_void_stmt) : visit(node.body) + + on_brace_block(params_val, body_val) + end + + # Visit a BlockParametersNode. + def visit_block_parameters_node(node) + on_block_var(visit(node.parameters), no_block_value) + end + + # Visit a ParametersNode. + # This will require expanding as we support more kinds of parameters. + def visit_parameters_node(node) + #on_params(required, optional, nil, nil, nil, nil, nil) + on_params(node.requireds.map { |n| visit(n) }, nil, nil, nil, nil, nil, nil) end - # Visit an AndNode + # Visit a RequiredParameterNode. + def visit_required_parameter_node(node) + bounds(node.location) + on_ident(node.name.to_s) + end + + # Visit a BreakNode. + def visit_break_node(node) + return on_break(on_args_new) if node.arguments.nil? + + args_val = visit_elements(node.arguments.arguments) + on_break(on_args_add_block(args_val,false)) + end + + # Visit an AndNode. def visit_and_node(node) visit_binary_operator(node) end - # Visit an OrNode + # Visit an OrNode. def visit_or_node(node) visit_binary_operator(node) end + # Visit a TrueNode. + def visit_true_node(node) + bounds(node.location) + on_var_ref(on_kw(node.slice)) + end + + # Visit a FalseNode. + def visit_false_node(node) + bounds(node.location) + on_var_ref(on_kw(node.slice)) + end + # Visit a FloatNode node. def visit_float_node(node) visit_number(node) { |text| on_float(text) } @@ -195,6 +261,19 @@ module Prism on_paren(body) end + # Visit a BeginNode node. + # This is not at all bulletproof against different structures of begin/rescue/else/ensure/end. + def visit_begin_node(node) + rescue_val = node.rescue_clause ? on_rescue(nil, nil, visit(node.rescue_clause), nil) : nil + ensure_val = node.ensure_clause ? on_ensure(visit(node.ensure_clause.statements)) : nil + on_begin(on_bodystmt(visit(node.statements), rescue_val, nil, ensure_val)) + end + + # Visit a RescueNode node. + def visit_rescue_node(node) + visit(node.statements) + end + # Visit a ProgramNode node. def visit_program_node(node) statements = visit(node.statements) @@ -260,7 +339,7 @@ module Prism raise NotImplementedError, "More than two arguments for operator" end elsif node.call_operator_loc.nil? - # In Ripper a method call like "puts myvar" with no parenthesis is a "command". + # In Ripper a method call like "puts myvar" with no parentheses is a "command". bounds(node.message_loc) ident_val = on_ident(node.message) @@ -268,11 +347,20 @@ module Prism if node.block block_val = visit(node.block) # In these calls, even if node.arguments is nil, we still get an :args_new call. - method_args_val = on_method_add_arg(on_fcall(ident_val), args_node_to_arguments(node.arguments)) - return on_method_add_block(method_args_val, on_brace_block(nil, block_val)) + args = if node.arguments.nil? + on_args_new + else + on_args_add_block(visit_elements(node.arguments.arguments)) + end + method_args_val = on_method_add_arg(on_fcall(ident_val), args) + return on_method_add_block(method_args_val, block_val) else - args = node.arguments.nil? ? nil : args_node_to_arguments(node.arguments) - return on_command(ident_val, args) + if node.arguments.nil? + return on_command(ident_val, nil) + else + args = on_args_add_block(visit_elements(node.arguments.arguments), false) + return on_command(ident_val, args) + end end else operator = node.call_operator_loc.slice @@ -289,7 +377,7 @@ module Prism if node.block block_val = visit(node.block) - return on_method_add_block(call_val, on_brace_block(nil, block_val)) + return on_method_add_block(call_val, block_val) else return call_val end @@ -299,17 +387,6 @@ module Prism end end - # Ripper generates an interesting format of argument list. - # It seems to be very location-specific. We should get rid of - # this method and make it clearer how it's done in each place. - def args_node_to_arguments(args_node) - return on_args_new if args_node.nil? - - args = visit_elements(args_node.arguments) - - on_args_add_block(args, false) - end - # Visit a list of elements, like the elements of an array or arguments. def visit_elements(elements) bounds(elements.first.location) @@ -318,6 +395,17 @@ module Prism end end + # Visit an operation-and-assign node, such as +=. + def visit_binary_op_assign(node, operator: node.operator) + bounds(node.name_loc) + ident_val = on_ident(node.name.to_s) + + bounds(node.operator_loc) + op_val = on_op(operator) + + on_opassign(on_var_field(ident_val), op_val, visit(node.value)) + end + # Visit a node that represents a number. We need to explicitly handle the # unary - operator. def visit_number(node) @@ -348,6 +436,18 @@ module Prism end end + if RUBY_ENGINE == "jruby" + # For JRuby, "no block" in an on_block_var is nil + def no_block_value + nil + end + else + # For CRuby et al, "no block" in an on_block_var is false + def no_block_value + false + end + end + # Visit a binary operator node like an AndNode or OrNode def visit_binary_operator(node) left_val = visit(node.left) diff --git a/test/prism/ripper_compat_test.rb b/test/prism/ripper_compat_test.rb index 1aaade046f..c8fc208dff 100644 --- a/test/prism/ripper_compat_test.rb +++ b/test/prism/ripper_compat_test.rb @@ -27,6 +27,7 @@ module Prism def test_method_calls_with_variable_names assert_equivalent("foo") assert_equivalent("foo()") + assert_equivalent("foo -7") assert_equivalent("foo(-7)") assert_equivalent("foo(1, 2, 3)") assert_equivalent("foo 1") @@ -49,9 +50,16 @@ module Prism assert_equivalent("foo(1) { bar }") assert_equivalent("foo(bar)") assert_equivalent("foo(bar(1))") + assert_equivalent("foo(bar(1)) { 7 }") assert_equivalent("foo bar(1)") - # assert_equivalent("foo(bar 1)") # This succeeds for me locally but fails on CI + end + + def test_method_call_blocks + assert_equivalent("foo { |a| a }") + + # assert_equivalent("foo(bar 1)") # assert_equivalent("foo bar 1") + # assert_equivalent("foo(bar 1) { 7 }") end def test_method_calls_on_immediate_values @@ -85,6 +93,23 @@ module Prism assert_equivalent("[1ri, -1ri, +1ri, 1.5ri, -1.5ri, +1.5ri]") end + def test_begin_rescue + assert_equivalent("begin a; rescue; c; ensure b; end") + end + + def test_break + assert_equivalent("foo { break }") + assert_equivalent("foo { break 7 }") + assert_equivalent("foo { break [1, 2, 3] }") + end + + def test_op_assign + assert_equivalent("a += b") + assert_equivalent("a -= b") + assert_equivalent("a *= b") + assert_equivalent("a /= b") + end + private def assert_equivalent(source) @@ -100,6 +125,9 @@ module Prism #relatives = ENV["FOCUS"] ? [ENV["FOCUS"]] : Dir["**/*.txt", base: base] relatives = [ "arithmetic.txt", + "booleans.txt", + "boolean_operators.txt", + # "break.txt", # No longer parseable by Ripper in CRuby 3.3.0+ "comments.txt", "integer_operations.txt", ] |
