Repository: ruby/yarp (c)

Issue:
Bug parsing -1.zero?
```ruby
irb(main):002:0> YARP.parse('-1.zero?')
=> 
#<YARP::ParseResult:0x000055db076da398
 @comments=[],
 @errors=[],
 @source=#<YARP::Source:0x000055db076da910 @offsets=[0], @source="-1.zero?">,
 @value=
  @ ProgramNode (location: (0...8))
├── locals: []
└── statements:
    @ StatementsNode (location: (0...8))
    └── body: (length: 1)
        └── @ CallNode (location: (0...8))
            ├── receiver:
            │   @ CallNode (location: (1...8))
            │   ├── receiver:
            │   │   @ IntegerNode (location: (1...2))
            │   │   └── flags: decimal
            │   ├── call_operator_loc: (2...3) = "."
            │   ├── message_loc: (3...8) = "zero?"
            │   ├── opening_loc: ∅
            │   ├── arguments: ∅
            │   ├── closing_loc: ∅
            │   ├── block: ∅
            │   ├── flags: 
            │   └── name: "zero?"
            ├── call_operator_loc: ∅
            ├── message_loc: (0...1) = "-"
            ├── opening_loc: ∅
            ├── arguments: ∅
            ├── closing_loc: ∅
            ├── block: ∅
            ├── flags: 
            └── name: "-@",
 @warnings=[]>
```

The `-` minus sign should bind to the integer -- not to the call to `zero?`.

Here is the same thing with Whitequark:

```ruby
irb(main):001:0> Parser::CurrentRuby.parse('-1.zero?')
=> 
s(:send,
  s(:int, -1), :zero?)
```

Pull request description:
Fix up unary minus precedence
Fixes #1460

Patch:
diff --git a/src/yarp.c b/src/yarp.c
index 643f795c..13ed3a80 100644
--- a/src/yarp.c
+++ b/src/yarp.c
@@ -7940,7 +7940,7 @@ yp_binding_powers_t yp_binding_powers[YP_TOKEN_MAXIMUM] = {
 
     // -@
     [YP_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UMINUS),
-    [YP_TOKEN_UMINUS_NUM] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UMINUS),
+    [YP_TOKEN_UMINUS_NUM] = { YP_BINDING_POWER_UMINUS, YP_BINDING_POWER_MAX, false },
 
     // **
     [YP_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EXPONENT),
@@ -12790,16 +12790,23 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
             yp_token_t operator = parser->previous;
             yp_node_t *node = parse_expression(parser, yp_binding_powers[parser->previous.type].right, YP_ERR_UNARY_RECEIVER_MINUS);
 
-            switch (YP_NODE_TYPE(node)) {
-                case YP_INTEGER_NODE:
-                case YP_FLOAT_NODE:
-                case YP_RATIONAL_NODE:
-                case YP_IMAGINARY_NODE:
-                    parse_negative_numeric(node);
-                    break;
-                default:
-                    node = (yp_node_t *) yp_call_node_unary_create(parser, &operator, node, "-@");
-                    break;
+            if (accept(parser, YP_TOKEN_STAR_STAR)) {
+                yp_token_t exponent_operator = parser->previous;
+                yp_node_t *exponent = parse_expression(parser, yp_binding_powers[exponent_operator.type].right, YP_ERR_EXPECT_ARGUMENT);
+                node = (yp_node_t *) yp_call_node_binary_create(parser, node, &exponent_operator, exponent);
+                node = (yp_node_t *) yp_call_node_unary_create(parser, &operator, node, "-@");
+            } else {
+                switch (YP_NODE_TYPE(node)) {
+                    case YP_INTEGER_NODE:
+                    case YP_FLOAT_NODE:
+                    case YP_RATIONAL_NODE:
+                    case YP_IMAGINARY_NODE:
+                        parse_negative_numeric(node);
+                        break;
+                    default:
+                        node = (yp_node_t *) yp_call_node_unary_create(parser, &operator, node, "-@");
+                        break;
+                }
             }
 
             return node;


Test patch:
diff --git a/test/yarp/fixtures/arithmetic.txt b/test/yarp/fixtures/arithmetic.txt
index 47fa4ec2..b1e1267b 100644
--- a/test/yarp/fixtures/arithmetic.txt
+++ b/test/yarp/fixtures/arithmetic.txt
@@ -7,3 +7,7 @@ foo !bar
 foo ~bar
 
 foo << bar << baz
+
+-1**2
+
+-1.zero?
diff --git a/test/yarp/snapshots/arithmetic.txt b/test/yarp/snapshots/arithmetic.txt
index 71389730..408d4cc7 100644
--- a/test/yarp/snapshots/arithmetic.txt
+++ b/test/yarp/snapshots/arithmetic.txt
@@ -1,8 +1,8 @@
-@ ProgramNode (location: (0...58))
+@ ProgramNode (location: (0...75))
 ├── locals: []
 └── statements:
-    @ StatementsNode (location: (0...58))
-    └── body: (length: 5)
+    @ StatementsNode (location: (0...75))
+    └── body: (length: 7)
         ├── @ CallNode (location: (0...8))
         │   ├── receiver: ∅
         │   ├── call_operator_loc: ∅
@@ -151,57 +151,95 @@
         │   ├── block: ∅
         │   ├── flags: ∅
         │   └── name: "foo"
-        └── @ CallNode (location: (41...58))
+        ├── @ CallNode (location: (41...58))
+        │   ├── receiver:
+        │   │   @ CallNode (location: (41...51))
+        │   │   ├── receiver:
+        │   │   │   @ CallNode (location: (41...44))
+        │   │   │   ├── receiver: ∅
+        │   │   │   ├── call_operator_loc: ∅
+        │   │   │   ├── message_loc: (41...44) = "foo"
+        │   │   │   ├── opening_loc: ∅
+        │   │   │   ├── arguments: ∅
+        │   │   │   ├── closing_loc: ∅
+        │   │   │   ├── block: ∅
+        │   │   │   ├── flags: variable_call
+        │   │   │   └── name: "foo"
+        │   │   ├── call_operator_loc: ∅
+        │   │   ├── message_loc: (45...47) = "<<"
+        │   │   ├── opening_loc: ∅
+        │   │   ├── arguments:
+        │   │   │   @ ArgumentsNode (location: (48...51))
+        │   │   │   └── arguments: (length: 1)
+        │   │   │       └── @ CallNode (location: (48...51))
+        │   │   │           ├── receiver: ∅
+        │   │   │           ├── call_operator_loc: ∅
+        │   │   │           ├── message_loc: (48...51) = "bar"
+        │   │   │           ├── opening_loc: ∅
+        │   │   │           ├── arguments: ∅
+        │   │   │           ├── closing_loc: ∅
+        │   │   │           ├── block: ∅
+        │   │   │           ├── flags: variable_call
+        │   │   │           └── name: "bar"
+        │   │   ├── closing_loc: ∅
+        │   │   ├── block: ∅
+        │   │   ├── flags: ∅
+        │   │   └── name: "<<"
+        │   ├── call_operator_loc: ∅
+        │   ├── message_loc: (52...54) = "<<"
+        │   ├── opening_loc: ∅
+        │   ├── arguments:
+        │   │   @ ArgumentsNode (location: (55...58))
+        │   │   └── arguments: (length: 1)
+        │   │       └── @ CallNode (location: (55...58))
+        │   │           ├── receiver: ∅
+        │   │           ├── call_operator_loc: ∅
+        │   │           ├── message_loc: (55...58) = "baz"
+        │   │           ├── opening_loc: ∅
+        │   │           ├── arguments: ∅
+        │   │           ├── closing_loc: ∅
+        │   │           ├── block: ∅
+        │   │           ├── flags: variable_call
+        │   │           └── name: "baz"
+        │   ├── closing_loc: ∅
+        │   ├── block: ∅
+        │   ├── flags: ∅
+        │   └── name: "<<"
+        ├── @ CallNode (location: (60...65))
+        │   ├── receiver:
+        │   │   @ CallNode (location: (61...65))
+        │   │   ├── receiver:
+        │   │   │   @ IntegerNode (location: (61...62))
+        │   │   │   └── flags: decimal
+        │   │   ├── call_operator_loc: ∅
+        │   │   ├── message_loc: (62...64) = "**"
+        │   │   ├── opening_loc: ∅
+        │   │   ├── arguments:
+        │   │   │   @ ArgumentsNode (location: (64...65))
+        │   │   │   └── arguments: (length: 1)
+        │   │   │       └── @ IntegerNode (location: (64...65))
+        │   │   │           └── flags: decimal
+        │   │   ├── closing_loc: ∅
+        │   │   ├── block: ∅
+        │   │   ├── flags: ∅
+        │   │   └── name: "**"
+        │   ├── call_operator_loc: ∅
+        │   ├── message_loc: (60...61) = "-"
+        │   ├── opening_loc: ∅
+        │   ├── arguments: ∅
+        │   ├── closing_loc: ∅
+        │   ├── block: ∅
+        │   ├── flags: ∅
+        │   └── name: "-@"
+        └── @ CallNode (location: (67...75))
             ├── receiver:
-            │   @ CallNode (location: (41...51))
-            │   ├── receiver:
-            │   │   @ CallNode (location: (41...44))
-            │   │   ├── receiver: ∅
-            │   │   ├── call_operator_loc: ∅
-            │   │   ├── message_loc: (41...44) = "foo"
-            │   │   ├── opening_loc: ∅
-            │   │   ├── arguments: ∅
-            │   │   ├── closing_loc: ∅
-            │   │   ├── block: ∅
-            │   │   ├── flags: variable_call
-            │   │   └── name: "foo"
-            │   ├── call_operator_loc: ∅
-            │   ├── message_loc: (45...47) = "<<"
-            │   ├── opening_loc: ∅
-            │   ├── arguments:
-            │   │   @ ArgumentsNode (location: (48...51))
-            │   │   └── arguments: (length: 1)
-            │   │       └── @ CallNode (location: (48...51))
-            │   │           ├── receiver: ∅
-            │   │           ├── call_operator_loc: ∅
-            │   │           ├── message_loc: (48...51) = "bar"
-            │   │           ├── opening_loc: ∅
-            │   │           ├── arguments: ∅
-            │   │           ├── closing_loc: ∅
-            │   │           ├── block: ∅
-            │   │           ├── flags: variable_call
-            │   │           └── name: "bar"
-            │   ├── closing_loc: ∅
-            │   ├── block: ∅
-            │   ├── flags: ∅
-            │   └── name: "<<"
-            ├── call_operator_loc: ∅
-            ├── message_loc: (52...54) = "<<"
+            │   @ IntegerNode (location: (67...69))
+            │   └── flags: decimal
+            ├── call_operator_loc: (69...70) = "."
+            ├── message_loc: (70...75) = "zero?"
             ├── opening_loc: ∅
-            ├── arguments:
-            │   @ ArgumentsNode (location: (55...58))
-            │   └── arguments: (length: 1)
-            │       └── @ CallNode (location: (55...58))
-            │           ├── receiver: ∅
-            │           ├── call_operator_loc: ∅
-            │           ├── message_loc: (55...58) = "baz"
-            │           ├── opening_loc: ∅
-            │           ├── arguments: ∅
-            │           ├── closing_loc: ∅
-            │           ├── block: ∅
-            │           ├── flags: variable_call
-            │           └── name: "baz"
+            ├── arguments: ∅
             ├── closing_loc: ∅
             ├── block: ∅
             ├── flags: ∅
-            └── name: "<<"
+            └── name: "zero?"