Skip to content

Commit

Permalink
[ruby/prism] Fix to parse a (endless-)range with binary operators
Browse files Browse the repository at this point in the history
  • Loading branch information
makenowjust authored and matzbot committed Dec 11, 2023
1 parent 1ab91b1 commit a860e36
Show file tree
Hide file tree
Showing 4 changed files with 386 additions and 17 deletions.
19 changes: 13 additions & 6 deletions prism/prism.c
Expand Up @@ -10128,7 +10128,7 @@ typedef struct {
#define BINDING_POWER_ASSIGNMENT { PM_BINDING_POWER_UNARY, PM_BINDING_POWER_ASSIGNMENT, true, false }
#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, false }
#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true, false }
#define NON_ASSOCIATIVE(precedence) { precedence + 1, precedence + 1, true, true }
#define NON_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, true }
#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false }

pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
Expand Down Expand Up @@ -16967,11 +16967,18 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
current_binding_powers.binary
) {
node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call);
if (
current_binding_powers.nonassoc &&
current_binding_powers.right <= pm_binding_powers[parser->current.type].left
) {
break;
if (current_binding_powers.nonassoc) {
bool endless_range_p = PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL;
pm_binding_power_t left = endless_range_p ? PM_BINDING_POWER_TERM : current_binding_powers.left;
if (
left <= pm_binding_powers[parser->current.type].left ||
// Exceptionally to operator precedences, '1.. & 2' is rejected.
// '1.. || 2' is also an exception, but it is handled by the lexer.
// (Here, parser->current is PM_TOKEN_PIPE, not PM_TOKEN_PIPE_PIPE).
(endless_range_p && match1(parser, PM_TOKEN_AMPERSAND))
) {
break;
}
}
if (accepts_command_call) {
// A command-style method call is only accepted on method chains.
Expand Down
17 changes: 17 additions & 0 deletions test/prism/errors_test.rb
Expand Up @@ -1971,6 +1971,23 @@ def a = b rescue c d
end
end

def test_range_and_bin_op
sources = <<~RUBY.lines
1..2..3
1..2..
1.. || 2
1.. & 2
1.. * 2
1.. / 2
1.. % 2
1.. ** 2
RUBY
sources.each do |source|
assert_nil Ripper.sexp_raw(source)
assert_false(Prism.parse(source).success?)
end
end

def test_constant_assignment_in_method
source = 'def foo();A=1;end'
assert_errors expression(source), source, [
Expand Down
30 changes: 30 additions & 0 deletions test/prism/fixtures/ranges.txt
Expand Up @@ -17,3 +17,33 @@ foo[...2]
(1..)

1 .. ..1

1.. && 2

1.. == 2

1.. != 2

1.. === 2

1.. <=> 2

1.. =~ 2

1.. !~ 2

1.. < 2

1.. > 2

1.. <= 2

1.. >= 2

1.. << 2

1.. >> 2

1.. + 2

1.. - 2

0 comments on commit a860e36

Please sign in to comment.