summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTSUYUSATO Kitsune <make.just.on@gmail.com>2023-11-15 23:05:08 +0900
committergit <svn-admin@ruby-lang.org>2023-11-22 14:53:28 +0000
commit8794836bf256c4bad51111b085f9d07e463432a0 (patch)
tree04e05461f87cf359ac877f03d111a31b99e04601
parent2aefbbaab9c9a86fb70f30bca86ed73411679d6d (diff)
[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
-rw-r--r--prism/prism.c13
-rw-r--r--test/prism/errors_test.rb15
-rw-r--r--test/prism/fixtures/ranges.txt2
-rw-r--r--test/prism/snapshots/ranges.txt46
4 files changed, 59 insertions, 17 deletions
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: ∅