diff --git a/config.yml b/config.yml index 876008311f..cc07b386a1 100644 --- a/config.yml +++ b/config.yml @@ -187,6 +187,7 @@ errors: - MULTI_ASSIGN_UNEXPECTED_REST - NESTING_TOO_DEEP - NO_LOCAL_VARIABLE + - NON_ASSOCIATIVE_OPERATOR - NOT_EXPRESSION - NUMBER_LITERAL_UNDERSCORE - NUMBERED_PARAMETER_INNER_BLOCK diff --git a/gemfiles/jruby/Gemfile.lock b/gemfiles/jruby/Gemfile.lock index 404e48332d..c38dc3cea5 100644 --- a/gemfiles/jruby/Gemfile.lock +++ b/gemfiles/jruby/Gemfile.lock @@ -20,8 +20,10 @@ GEM PLATFORMS universal-java-11 + universal-java-17 universal-java-20 universal-java-21 + universal-java-22 DEPENDENCIES parser diff --git a/src/prism.c b/src/prism.c index 6e3f9bce59..808915858a 100644 --- a/src/prism.c +++ b/src/prism.c @@ -21643,16 +21643,26 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc break; } + // If the operator is nonassoc and we should not be able to parse the + // upcoming infix operator, 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)) - ) { + // 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: + // + // 1.. & 2 + // 1.. * 2 + // + if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) { + if (match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_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)); + break; + } + + if (PM_BINDING_POWER_TERM <= pm_binding_powers[parser->current.type].left) { + break; + } + } else if (current_binding_powers.left <= pm_binding_powers[parser->current.type].left) { break; } } diff --git a/templates/src/diagnostic.c.erb b/templates/src/diagnostic.c.erb index d3b9cd516d..7768c7f5ed 100644 --- a/templates/src/diagnostic.c.erb +++ b/templates/src/diagnostic.c.erb @@ -269,6 +269,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = { [PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST] = { "unexpected '%.*s' resulting in multiple splats in multiple assignment", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_NESTING_TOO_DEEP] = { "nesting too deep", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_NO_LOCAL_VARIABLE] = { "%.*s: no such local variable", PM_ERROR_LEVEL_SYNTAX }, + [PM_ERR_NON_ASSOCIATIVE_OPERATOR] = { "unexpected %s; %s is a non-associative operator", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_NOT_EXPRESSION] = { "expected an expression after `not`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_NUMBER_LITERAL_UNDERSCORE] = { "number literal ending with a `_`", PM_ERROR_LEVEL_SYNTAX }, [PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK] = { "numbered parameter is already used in inner block", PM_ERROR_LEVEL_SYNTAX }, diff --git a/test/prism/errors/non_assoc_range.txt b/test/prism/errors/non_assoc_range.txt index 072cf6d3c6..12eec10594 100644 --- a/test/prism/errors/non_assoc_range.txt +++ b/test/prism/errors/non_assoc_range.txt @@ -1,4 +1,5 @@ 1....2 + ^ unexpected '.'; ... is a non-associative operator ^ unexpected '.', expecting end-of-input ^ unexpected '.', ignoring it diff --git a/test/prism/errors/range_and_bin_op_4.txt b/test/prism/errors/range_and_bin_op_4.txt index 56226480cf..ce6e79e5ff 100644 --- a/test/prism/errors/range_and_bin_op_4.txt +++ b/test/prism/errors/range_and_bin_op_4.txt @@ -1,4 +1,5 @@ 1.. & 2 + ^ unexpected '&'; .. is a non-associative operator ^ unexpected '&', expecting end-of-input ^ unexpected '&', ignoring it diff --git a/test/prism/errors/range_and_bin_op_5.txt b/test/prism/errors/range_and_bin_op_5.txt index bc8b467914..4ed91e1052 100644 --- a/test/prism/errors/range_and_bin_op_5.txt +++ b/test/prism/errors/range_and_bin_op_5.txt @@ -1,4 +1,5 @@ 1.. * 2 + ^ unexpected *; .. is a non-associative operator ^ unexpected *, expecting end-of-input ^ unexpected write target ^~~ unexpected write target