summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/prism/ripper_compat.rb103
1 files changed, 88 insertions, 15 deletions
diff --git a/lib/prism/ripper_compat.rb b/lib/prism/ripper_compat.rb
index 912114cfef..98dc762f62 100644
--- a/lib/prism/ripper_compat.rb
+++ b/lib/prism/ripper_compat.rb
@@ -116,32 +116,80 @@ module Prism
end
# Visit a CallNode node.
+ # Ripper distinguishes between many different method-call
+ # nodes -- unary and binary operators, "command" calls with
+ # no parentheses, and call/fcall/vcall.
def visit_call_node(node)
if node.variable_call?
- if node.message.match?(/^[[:alpha:]_]/)
- bounds(node.message_loc)
- return on_vcall(on_ident(node.message))
- end
+ raise NotImplementedError unless node.receiver.nil?
- raise NotImplementedError, "Non-alpha variable call"
+ bounds(node.message_loc)
+ return on_vcall(on_ident(node.message))
end
if node.opening_loc.nil?
- left = visit(node.receiver)
- if node.arguments&.arguments&.length == 1
- right = visit(node.arguments.arguments.first)
-
- on_binary(left, node.name, right)
- elsif !node.arguments || node.arguments.empty?
- on_unary(node.name, left)
+ # No opening_loc can mean an operator. It can also mean a
+ # method call with no parentheses.
+ if node.message.match?(/^[[:punct:]]/)
+ left = visit(node.receiver)
+ if node.arguments&.arguments&.length == 1
+ right = visit(node.arguments.arguments.first)
+
+ return on_binary(left, node.name, right)
+ elsif !node.arguments || node.arguments.empty?
+ return on_unary(node.name, left)
+ else
+ 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"
+ bounds(node.message_loc)
+ ident_val = on_ident(node.message)
+ args = args_node_to_arguments(node.arguments)
+ return on_command(ident_val, args)
else
- raise NotImplementedError, "More than two arguments for operator"
+ operator = node.call_operator_loc.slice
+ if operator == "."
+ left_val = visit(node.receiver)
+
+ bounds(node.call_operator_loc)
+ dot_val = on_period(node.call_operator)
+
+ bounds(node.message_loc)
+ right_val = on_ident(node.message)
+
+ return on_call(left_val, dot_val, right_val)
+ else
+ raise NotImplementedError, "operator other than dot for call: #{operator.inspect}"
+ end
end
+ end
+
+ # A non-operator method call with parentheses
+ args = on_arg_paren(args_node_to_arguments(node.arguments))
+
+ bounds(node.message_loc)
+ ident_val = on_ident(node.message)
+
+ bounds(node.location)
+ args_call_val = on_method_add_arg(on_fcall(ident_val), args)
+ if node.block
+ raise NotImplementedError, "Method call with a block!"
else
- raise NotImplementedError, "Non-nil opening_loc"
+ return args_call_val
end
end
+ # Visit an AndNode
+ def visit_and_node(node)
+ visit_binary_operator(node)
+ end
+
+ # Visit an AndNode
+ def visit_or_node(node)
+ visit_binary_operator(node)
+ end
+
# Visit a FloatNode node.
def visit_float_node(node)
visit_number(node) { |text| on_float(text) }
@@ -203,6 +251,24 @@ module Prism
end
end
+ private
+
+ # Ripper generates an interesting format of argument list.
+ # We'd like to convert an ArgumentsNode to one.
+ def args_node_to_arguments(args_node)
+ return nil if args_node.nil?
+
+ args = on_args_new
+ args_node.arguments.each do |arg|
+ bounds(arg.location)
+ args = on_args_add(args, visit(arg))
+ end
+
+ on_args_add_block(args, false)
+ end
+
+ public
+
############################################################################
# Entrypoints for subclasses
############################################################################
@@ -238,13 +304,20 @@ module Prism
value = yield slice[1..-1]
bounds(node.location)
- on_unary(RUBY_ENGINE == "jruby" ? :- : :-@, value)
+ on_unary(RUBY_ENGINE == "jruby" && JRUBY_VERSION < "9.4.6.0" ? :- : :-@, value)
else
bounds(location)
yield slice
end
end
+ # Visit a binary operator node like an AndNode or OrNode
+ def visit_binary_operator(node)
+ left_val = visit(node.left)
+ right_val = visit(node.right)
+ on_binary(left_val, node.operator_loc.slice.to_sym, right_val)
+ end
+
# This method is responsible for updating lineno and column information
# to reflect the current node.
#