[ruby/prism] Fix associativity of binary range with begin-less range

Fix https://github.com/ruby/prism/pull/1828

https://github.com/ruby/prism/commit/22c0640e48
This commit is contained in:
TSUYUSATO Kitsune 2023-11-15 23:05:08 +09:00 committed by git
parent 2aefbbaab9
commit 8794836bf2
4 changed files with 59 additions and 17 deletions

View File

@ -10202,6 +10202,8 @@ pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
// .. ...
[PM_TOKEN_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
[PM_TOKEN_DOT_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
[PM_TOKEN_UDOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
[PM_TOKEN_UDOT_DOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
// ||
[PM_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_OR),
@ -13956,7 +13958,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) {
pm_token_t operator = parser->current;
parser_lex(parser);
pm_node_t *right = parse_expression(parser, binding_power, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
pm_node_t *right = parse_expression(parser, pm_binding_powers[operator.type].left, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
return (pm_node_t *) pm_range_node_create(parser, NULL, &operator, right);
}
case PM_TOKEN_FLOAT:
@ -16766,6 +16768,7 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
static pm_node_t *
parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, pm_diagnostic_id_t diag_id) {
pm_token_t recovery = parser->previous;
bool is_udot = parser->current.type == PM_TOKEN_UDOT_DOT || parser->current.type == PM_TOKEN_UDOT_DOT_DOT;
pm_node_t *node = parse_expression_prefix(parser, binding_power);
switch (PM_NODE_TYPE(node)) {
@ -16789,6 +16792,14 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, pm_diagn
break;
}
// Range operators are non-associative, so that it does not associate with
// other range operators (i.e. `..1..` should be rejected.)
// For this reason, we check such a case for unary ranges here, and if so,
// it returns the node immediately,
if (is_udot && pm_binding_powers[parser->current.type].left >= PM_BINDING_POWER_RANGE) {
return node;
}
// Otherwise we'll look and see if the next token can be parsed as an infix
// operator. If it can, then we'll parse it using parse_expression_infix.
pm_binding_powers_t current_binding_powers;

View File

@ -1770,6 +1770,21 @@ module Prism
]
end
def test_binary_range_with_left_unary_range
source = <<~RUBY
..1..
...1..
RUBY
message1 = 'Expected a newline or semicolon after the statement'
message2 = 'Cannot parse the expression'
assert_errors expression(source), source, [
[message1, 3..3],
[message2, 3..3],
[message1, 10..10],
[message2, 10..10],
]
end
private
def assert_errors(expected, source, errors, compare_ripper: RUBY_ENGINE == "ruby")

View File

@ -15,3 +15,5 @@ foo[...2]
{ foo: ..bar }
(1..)
1 .. ..1

View File

@ -1,8 +1,8 @@
@ ProgramNode (location: (1,0)-(17,5))
@ ProgramNode (location: (1,0)-(19,8))
├── locals: []
└── statements:
@ StatementsNode (location: (1,0)-(17,5))
└── body: (length: 9)
@ StatementsNode (location: (1,0)-(19,8))
└── body: (length: 10)
├── @ ParenthesesNode (location: (1,0)-(1,6))
│ ├── body:
│ │ @ StatementsNode (location: (1,1)-(1,5))
@ -146,16 +146,30 @@
│ │ │ └── flags: ∅
│ │ └── operator_loc: ∅
│ └── closing_loc: (15,13)-(15,14) = "}"
└── @ ParenthesesNode (location: (17,0)-(17,5))
├── body:
│ @ StatementsNode (location: (17,1)-(17,4))
│ └── body: (length: 1)
│ └── @ RangeNode (location: (17,1)-(17,4))
│ ├── left:
│ │ @ IntegerNode (location: (17,1)-(17,2))
│ │ └── flags: decimal
│ ├── right: ∅
│ ├── operator_loc: (17,2)-(17,4) = ".."
│ └── flags: ∅
├── opening_loc: (17,0)-(17,1) = "("
└── closing_loc: (17,4)-(17,5) = ")"
├── @ ParenthesesNode (location: (17,0)-(17,5))
│ ├── body:
│ │ @ StatementsNode (location: (17,1)-(17,4))
│ │ └── body: (length: 1)
│ │ └── @ RangeNode (location: (17,1)-(17,4))
│ │ ├── left:
│ │ │ @ IntegerNode (location: (17,1)-(17,2))
│ │ │ └── flags: decimal
│ │ ├── right: ∅
│ │ ├── operator_loc: (17,2)-(17,4) = ".."
│ │ └── flags: ∅
│ ├── opening_loc: (17,0)-(17,1) = "("
│ └── closing_loc: (17,4)-(17,5) = ")"
└── @ RangeNode (location: (19,0)-(19,8))
├── left:
│ @ IntegerNode (location: (19,0)-(19,1))
│ └── flags: decimal
├── right:
│ @ RangeNode (location: (19,5)-(19,8))
│ ├── left: ∅
│ ├── right:
│ │ @ IntegerNode (location: (19,7)-(19,8))
│ │ └── flags: decimal
│ ├── operator_loc: (19,5)-(19,7) = ".."
│ └── flags: ∅
├── operator_loc: (19,2)-(19,4) = ".."
└── flags: ∅