diff --git a/prism/prism.c b/prism/prism.c index db9c1ad7e1..885470137d 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -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; diff --git a/test/prism/errors_test.rb b/test/prism/errors_test.rb index bc3f3ebee9..5572a3254f 100644 --- a/test/prism/errors_test.rb +++ b/test/prism/errors_test.rb @@ -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") diff --git a/test/prism/fixtures/ranges.txt b/test/prism/fixtures/ranges.txt index 55c0ade965..c9bfe50399 100644 --- a/test/prism/fixtures/ranges.txt +++ b/test/prism/fixtures/ranges.txt @@ -15,3 +15,5 @@ foo[...2] { foo: ..bar } (1..) + +1 .. ..1 diff --git a/test/prism/snapshots/ranges.txt b/test/prism/snapshots/ranges.txt index 3c7892f29b..f52f93633c 100644 --- a/test/prism/snapshots/ranges.txt +++ b/test/prism/snapshots/ranges.txt @@ -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: ∅