From 4cbd2ab9d462784ee44108e396177807e869ad23 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Mon, 7 Oct 2024 13:47:04 -0400 Subject: [PATCH] [ruby/prism] Properly handle non-assoc operators https://github.com/ruby/prism/commit/dbd5c929d6 --- prism/prism.c | 14 ++++++++++++-- .../errors/binary_range_with_left_unary_range.txt | 1 + test/prism/errors/non_assoc_equality.txt | 6 ++++++ test/prism/errors/range_and_bin_op.txt | 1 + test/prism/errors/range_and_bin_op_2.txt | 1 + 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index b88c99c659..7ef92ef5a5 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -21764,8 +21764,11 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc // 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; + pm_token_type_t current_token_type; + while ( - current_binding_powers = pm_binding_powers[parser->current.type], + current_token_type = parser->current.type, + current_binding_powers = pm_binding_powers[current_token_type], binding_power <= current_binding_powers.left && current_binding_powers.binary ) { @@ -21806,6 +21809,13 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc // If the operator is nonassoc and we should not be able to parse the // upcoming infix operator, break. if (current_binding_powers.nonassoc) { + // If this is a non-assoc operator and we are about to parse the + // exact same operator, then we need to add an error. + if (match1(parser, current_token_type)) { + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type)); + break; + } + // If this is an endless range, then we need to reject a couple of // additional operators because it violates the normal operator // precedence rules. Those patterns are: @@ -21815,7 +21825,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc // if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) { if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) { - PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(parser->previous.type)); + PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type)); break; } diff --git a/test/prism/errors/binary_range_with_left_unary_range.txt b/test/prism/errors/binary_range_with_left_unary_range.txt index 37e41f3971..85cf55fb80 100644 --- a/test/prism/errors/binary_range_with_left_unary_range.txt +++ b/test/prism/errors/binary_range_with_left_unary_range.txt @@ -2,6 +2,7 @@ ^~ unexpected range operator; .. and ... are non-associative and cannot be chained ...1.. ^~ unexpected range operator; .. and ... are non-associative and cannot be chained + ^~ unexpected ..; .. is a non-associative operator ^~ unexpected .., expecting end-of-input ^~ unexpected .., ignoring it diff --git a/test/prism/errors/non_assoc_equality.txt b/test/prism/errors/non_assoc_equality.txt index 6ce8da88d6..9b3f137549 100644 --- a/test/prism/errors/non_assoc_equality.txt +++ b/test/prism/errors/non_assoc_equality.txt @@ -1,19 +1,25 @@ 1 == 2 == 3 + ^~ unexpected '=='; '==' is a non-associative operator ^~ unexpected '==', expecting end-of-input ^~ unexpected '==', ignoring it 1 != 2 != 3 + ^~ unexpected '!='; '!=' is a non-associative operator ^~ unexpected '!=', expecting end-of-input ^~ unexpected '!=', ignoring it 1 === 2 === 3 + ^~~ unexpected '==='; '===' is a non-associative operator ^~~ unexpected '===', expecting end-of-input ^~~ unexpected '===', ignoring it 1 =~ 2 =~ 3 + ^~ unexpected '=~'; '=~' is a non-associative operator ^~ unexpected '=~', expecting end-of-input ^~ unexpected '=~', ignoring it 1 !~ 2 !~ 3 + ^~ unexpected '!~'; '!~' is a non-associative operator ^~ unexpected '!~', expecting end-of-input ^~ unexpected '!~', ignoring it 1 <=> 2 <=> 3 + ^~~ unexpected '<=>'; '<=>' is a non-associative operator ^~~ unexpected '<=>', expecting end-of-input ^~~ unexpected '<=>', ignoring it diff --git a/test/prism/errors/range_and_bin_op.txt b/test/prism/errors/range_and_bin_op.txt index 4a7a396d0d..55928c409b 100644 --- a/test/prism/errors/range_and_bin_op.txt +++ b/test/prism/errors/range_and_bin_op.txt @@ -1,4 +1,5 @@ 1..2..3 + ^~ unexpected ..; .. is a non-associative operator ^~ unexpected .., expecting end-of-input ^~ unexpected .., ignoring it diff --git a/test/prism/errors/range_and_bin_op_2.txt b/test/prism/errors/range_and_bin_op_2.txt index f2a31dcf82..6ca91a26eb 100644 --- a/test/prism/errors/range_and_bin_op_2.txt +++ b/test/prism/errors/range_and_bin_op_2.txt @@ -1,4 +1,5 @@ 1..2.. + ^~ unexpected ..; .. is a non-associative operator ^~ unexpected .., expecting end-of-input ^~ unexpected .., ignoring it