Repository: ruby/prism
Pull request #2373 (2024-02-07 02:39:04)

Even more ripper compat


Patch:
diff --git a/lib/prism/ripper_compat.rb b/lib/prism/ripper_compat.rb
index 739d5875..912114cf 100644
--- a/lib/prism/ripper_compat.rb
+++ b/lib/prism/ripper_compat.rb
@@ -97,6 +97,8 @@ module Prism
         result.errors.each do |error|
           on_parse_error(error.message)
         end
+
+        nil
       else
         result.value.accept(self)
       end
@@ -106,14 +108,22 @@ module Prism
     # Visitor methods
     ############################################################################
 
-    # Visit a CallNode node.
-    def visit_call_node(node)
+    # Visit an ArrayNode node.
+    def visit_array_node(node)
+      elements = visit_elements(node.elements) unless node.elements.empty?
       bounds(node.location)
+      on_array(elements)
+    end
 
-      if node.message.match?(/^[[:alpha:]_]/)
-        val = on_ident(node.message)
+    # Visit a CallNode node.
+    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
 
-        return on_vcall(val)
+        raise NotImplementedError, "Non-alpha variable call"
       end
 
       if node.opening_loc.nil?
@@ -128,67 +138,61 @@ module Prism
           raise NotImplementedError, "More than two arguments for operator"
         end
       else
-        # This is a method call, not an operator
         raise NotImplementedError, "Non-nil opening_loc"
       end
     end
 
-    # Visit a RangeNode
-    def visit_range_node(node)
-      bounds(node.location)
-      left = visit(node.left)
-      right = visit(node.right)
-
-      if node.operator_loc.slice == "..."
-        on_dot3(left, right)
-      else
-        on_dot2(left, right)
-      end
-    end
-
-    # Visit a ParenthesesNode
-    def visit_parentheses_node(node)
-      bounds(node.location)
-      val = visit(node.body)
-      on_paren(val)
-    end
-
     # Visit a FloatNode node.
     def visit_float_node(node)
-      bounds(node.location)
-      on_float(node.slice)
+      visit_number(node) { |text| on_float(text) }
     end
 
     # Visit a ImaginaryNode node.
     def visit_imaginary_node(node)
-      bounds(node.location)
-      on_imaginary(node.slice)
+      visit_number(node) { |text| on_imaginary(text) }
     end
 
     # Visit an IntegerNode node.
     def visit_integer_node(node)
-      text = node.slice
-      loc = node.location
-      if text[0] == "-"
-        # TODO: This is ugly. We test that a newline between - and 7 doesn't give an error, but this could still be bad somehow.
-        bounds_line_col(loc.start_line, loc.start_column + 1)
-        on_int_val = on_int(text[1..-1])
-        bounds(node.location)
-        if RUBY_ENGINE == "jruby"
-          on_unary(:-, on_int_val)
+      visit_number(node) { |text| on_int(text) }
+    end
+
+    # Visit a ParenthesesNode node.
+    def visit_parentheses_node(node)
+      body =
+        if node.body.nil?
+          on_stmts_add(on_stmts_new, on_void_stmt)
         else
-          on_unary(:-@, on_int_val)
+          visit(node.body)
         end
+
+      bounds(node.location)
+      on_paren(body)
+    end
+
+    # Visit a ProgramNode node.
+    def visit_program_node(node)
+      statements = visit(node.statements)
+      bounds(node.location)
+      on_program(statements)
+    end
+
+    # Visit a RangeNode node.
+    def visit_range_node(node)
+      left = visit(node.left)
+      right = visit(node.right)
+
+      bounds(node.location)
+      if node.exclude_end?
+        on_dot3(left, right)
       else
-        bounds(node.location)
-        on_int(text)
+        on_dot2(left, right)
       end
     end
 
     # Visit a RationalNode node.
     def visit_rational_node(node)
-      bounds(node.location)
-      on_rational(node.slice)
+      visit_number(node) { |text| on_rational(text) }
     end
 
     # Visit a StatementsNode node.
@@ -199,13 +203,6 @@ module Prism
       end
     end
 
-    # Visit a ProgramNode node.
-    def visit_program_node(node)
-      statements = visit(node.statements)
-      bounds(node.location)
-      on_program(statements)
-    end
-
     ############################################################################
     # Entrypoints for subclasses
     ############################################################################
@@ -222,6 +219,32 @@ module Prism
 
     private
 
+    # Visit a list of elements, like the elements of an array or arguments.
+    def visit_elements(elements)
+      bounds(elements.first.location)
+      elements.inject(on_args_new) do |args, element|
+        on_args_add(args, visit(element))
+      end
+    end
+
+    # Visit a node that represents a number. We need to explicitly handle the
+    # unary - operator.
+    def visit_number(node)
+      slice = node.slice
+      location = node.location
+
+      if slice[0] == "-"
+        bounds_values(location.start_line, location.start_column + 1)
+        value = yield slice[1..-1]
+
+        bounds(node.location)
+        on_unary(RUBY_ENGINE == "jruby" ? :- : :-@, value)
+      else
+        bounds(location)
+        yield slice
+      end
+    end
+
     # This method is responsible for updating lineno and column information
     # to reflect the current node.
     #
@@ -234,7 +257,7 @@ module Prism
 
     # If we need to do something unusual, we can directly update the line number
     # and column to reflect the current node.
-    def bounds_line_col(lineno, column)
+    def bounds_values(lineno, column)
       @lineno = lineno
       @column = column
     end


Test patch:
diff --git a/test/prism/ripper_compat_test.rb b/test/prism/ripper_compat_test.rb
index 0bdef530..8ca8545a 100644
--- a/test/prism/ripper_compat_test.rb
+++ b/test/prism/ripper_compat_test.rb
@@ -36,10 +36,26 @@ module Prism
       assert_equivalent("(foo..-7)")
     end
 
+    def test_parentheses
+      assert_equivalent("()")
+      assert_equivalent("(1)")
+      assert_equivalent("(1; 2)")
+    end
+
+    def test_numbers
+      assert_equivalent("[1, -1, +1, 1.0, -1.0, +1.0]")
+      assert_equivalent("[1r, -1r, +1r, 1.5r, -1.5r, +1.5r]")
+      assert_equivalent("[1i, -1i, +1i, 1.5i, -1.5i, +1.5i]")
+      assert_equivalent("[1ri, -1ri, +1ri, 1.5ri, -1.5ri, +1.5ri]")
+    end
+
     private
 
     def assert_equivalent(source)
-      assert_equal Ripper.sexp_raw(source), RipperCompat.sexp_raw(source)
+      expected = Ripper.sexp_raw(source)
+
+      refute_nil expected
+      assert_equal expected, RipperCompat.sexp_raw(source)
     end
   end
 end