Skip to content

Commit c87fe8f

Browse files
committed
1 parent e31efe7 commit c87fe8f

File tree

6 files changed

+500
-51
lines changed

6 files changed

+500
-51
lines changed

src/main/c/yarp/include/prism.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,15 @@ PRISM_EXPORTED_FUNCTION bool pm_parse_success_p(const uint8_t *source, size_t si
168168
* @param token_type The token type to convert to a string.
169169
* @return A string representation of the given token type.
170170
*/
171-
PRISM_EXPORTED_FUNCTION const char * pm_token_type_to_str(pm_token_type_t token_type);
171+
PRISM_EXPORTED_FUNCTION const char * pm_token_type_name(pm_token_type_t token_type);
172+
173+
/**
174+
* Returns the human name of the given token type.
175+
*
176+
* @param token_type The token type to convert to a human name.
177+
* @return The human name of the given token type.
178+
*/
179+
const char * pm_token_type_human(pm_token_type_t token_type);
172180

173181
/**
174182
* Format the errors on the parser into the given buffer.

src/main/c/yarp/include/prism/diagnostic.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ typedef struct {
6666
* of errors between the parser and the user.
6767
*/
6868
typedef enum {
69+
// This is a special error that we can potentially replace by others. For
70+
// an example of how this is used, see parse_expression_prefix.
71+
PM_ERR_CANNOT_PARSE_EXPRESSION,
72+
73+
// These are the error codes.
6974
PM_ERR_ALIAS_ARGUMENT,
7075
PM_ERR_AMPAMPEQ_MULTI_ASSIGN,
7176
PM_ERR_ARGUMENT_AFTER_BLOCK,
@@ -100,7 +105,6 @@ typedef enum {
100105
PM_ERR_BLOCK_PARAM_PIPE_TERM,
101106
PM_ERR_BLOCK_TERM_BRACE,
102107
PM_ERR_BLOCK_TERM_END,
103-
PM_ERR_CANNOT_PARSE_EXPRESSION,
104108
PM_ERR_CANNOT_PARSE_STRING_PART,
105109
PM_ERR_CASE_EXPRESSION_AFTER_CASE,
106110
PM_ERR_CASE_EXPRESSION_AFTER_WHEN,
@@ -272,6 +276,8 @@ typedef enum {
272276
PM_ERR_UNARY_RECEIVER_MINUS,
273277
PM_ERR_UNARY_RECEIVER_PLUS,
274278
PM_ERR_UNARY_RECEIVER_TILDE,
279+
PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT,
280+
PM_ERR_UNEXPECTED_TOKEN_IGNORE,
275281
PM_ERR_UNDEF_ARGUMENT,
276282
PM_ERR_UNTIL_TERM,
277283
PM_ERR_VOID_EXPRESSION,
@@ -280,13 +286,15 @@ typedef enum {
280286
PM_ERR_WRITE_TARGET_READONLY,
281287
PM_ERR_WRITE_TARGET_UNEXPECTED,
282288
PM_ERR_XSTRING_TERM,
289+
290+
// These are the warning codes.
283291
PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS,
284292
PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS,
285293
PM_WARN_AMBIGUOUS_PREFIX_STAR,
286294
PM_WARN_AMBIGUOUS_SLASH,
287295
PM_WARN_END_IN_METHOD,
288296

289-
/* This must be the last member. */
297+
// This is the number of diagnostic codes.
290298
PM_DIAGNOSTIC_ID_LEN,
291299
} pm_diagnostic_id_t;
292300

src/main/c/yarp/include/prism/parser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ typedef struct pm_parser pm_parser_t;
259259
* token that is understood by a parent context but not by the current context.
260260
*/
261261
typedef enum {
262+
/** a null context, used for returning a value from a function */
263+
PM_CONTEXT_NONE = 0,
264+
262265
/** a begin statement */
263266
PM_CONTEXT_BEGIN,
264267

src/main/c/yarp/src/diagnostic.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ typedef struct {
7171
* * `PM_WARNING_LEVEL_VERBOSE` - Warnings that appear with `-w`, as in `ruby -w -c -e 'code'`.
7272
*/
7373
static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
74+
[PM_ERR_CANNOT_PARSE_EXPRESSION] = { "cannot parse the expression", PM_ERROR_LEVEL_FATAL },
75+
7476
// Errors
7577
[PM_ERR_ALIAS_ARGUMENT] = { "invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", PM_ERROR_LEVEL_FATAL },
7678
[PM_ERR_AMPAMPEQ_MULTI_ASSIGN] = { "unexpected `&&=` in a multiple assignment", PM_ERROR_LEVEL_FATAL },
@@ -106,7 +108,6 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
106108
[PM_ERR_BLOCK_PARAM_PIPE_TERM] = { "expected the block parameters to end with `|`", PM_ERROR_LEVEL_FATAL },
107109
[PM_ERR_BLOCK_TERM_BRACE] = { "expected a block beginning with `{` to end with `}`", PM_ERROR_LEVEL_FATAL },
108110
[PM_ERR_BLOCK_TERM_END] = { "expected a block beginning with `do` to end with `end`", PM_ERROR_LEVEL_FATAL },
109-
[PM_ERR_CANNOT_PARSE_EXPRESSION] = { "cannot parse the expression", PM_ERROR_LEVEL_FATAL },
110111
[PM_ERR_CANNOT_PARSE_STRING_PART] = { "cannot parse the string part", PM_ERROR_LEVEL_FATAL },
111112
[PM_ERR_CASE_EXPRESSION_AFTER_CASE] = { "expected an expression after `case`", PM_ERROR_LEVEL_FATAL },
112113
[PM_ERR_CASE_EXPRESSION_AFTER_WHEN] = { "expected an expression after `when`", PM_ERROR_LEVEL_FATAL },
@@ -277,6 +278,8 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
277278
[PM_ERR_UNARY_RECEIVER_BANG] = { "expected a receiver for unary `!`", PM_ERROR_LEVEL_FATAL },
278279
[PM_ERR_UNARY_RECEIVER_MINUS] = { "expected a receiver for unary `-`", PM_ERROR_LEVEL_FATAL },
279280
[PM_ERR_UNARY_RECEIVER_PLUS] = { "expected a receiver for unary `+`", PM_ERROR_LEVEL_FATAL },
281+
[PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT] = { "unexpected %s, assuming it is closing the parent %s", PM_ERROR_LEVEL_FATAL },
282+
[PM_ERR_UNEXPECTED_TOKEN_IGNORE] = { "unexpected %s, ignoring it", PM_ERROR_LEVEL_FATAL },
280283
[PM_ERR_UNARY_RECEIVER_TILDE] = { "expected a receiver for unary `~`", PM_ERROR_LEVEL_FATAL },
281284
[PM_ERR_UNTIL_TERM] = { "expected an `end` to close the `until` statement", PM_ERROR_LEVEL_FATAL },
282285
[PM_ERR_VOID_EXPRESSION] = { "unexpected void value expression", PM_ERROR_LEVEL_FATAL },

src/main/c/yarp/src/prism.c

Lines changed: 121 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ debug_state(pm_parser_t *parser) {
164164

165165
PRISM_ATTRIBUTE_UNUSED static void
166166
debug_token(pm_token_t * token) {
167-
fprintf(stderr, "%s: \"%.*s\"\n", pm_token_type_to_str(token->type), (int) (token->end - token->start), token->start);
167+
fprintf(stderr, "%s: \"%.*s\"\n", pm_token_type_human(token->type), (int) (token->end - token->start), token->start);
168168
}
169169

170170
#endif
@@ -6719,21 +6719,27 @@ context_terminator(pm_context_t context, pm_token_t *token) {
67196719
return token->type == PM_TOKEN_BRACE_RIGHT;
67206720
case PM_CONTEXT_PREDICATE:
67216721
return token->type == PM_TOKEN_KEYWORD_THEN || token->type == PM_TOKEN_NEWLINE || token->type == PM_TOKEN_SEMICOLON;
6722+
case PM_CONTEXT_NONE:
6723+
return false;
67226724
}
67236725

67246726
return false;
67256727
}
67266728

6727-
static bool
6728-
context_recoverable(pm_parser_t *parser, pm_token_t *token) {
6729+
/**
6730+
* Returns the context that the given token is found to be terminating, or
6731+
* returns PM_CONTEXT_NONE.
6732+
*/
6733+
static pm_context_t
6734+
context_recoverable(const pm_parser_t *parser, pm_token_t *token) {
67296735
pm_context_node_t *context_node = parser->current_context;
67306736

67316737
while (context_node != NULL) {
6732-
if (context_terminator(context_node->context, token)) return true;
6738+
if (context_terminator(context_node->context, token)) return context_node->context;
67336739
context_node = context_node->prev;
67346740
}
67356741

6736-
return false;
6742+
return PM_CONTEXT_NONE;
67376743
}
67386744

67396745
static bool
@@ -6761,7 +6767,7 @@ context_pop(pm_parser_t *parser) {
67616767
}
67626768

67636769
static bool
6764-
context_p(pm_parser_t *parser, pm_context_t context) {
6770+
context_p(const pm_parser_t *parser, pm_context_t context) {
67656771
pm_context_node_t *context_node = parser->current_context;
67666772

67676773
while (context_node != NULL) {
@@ -6773,7 +6779,7 @@ context_p(pm_parser_t *parser, pm_context_t context) {
67736779
}
67746780

67756781
static bool
6776-
context_def_p(pm_parser_t *parser) {
6782+
context_def_p(const pm_parser_t *parser) {
67776783
pm_context_node_t *context_node = parser->current_context;
67786784

67796785
while (context_node != NULL) {
@@ -6796,6 +6802,55 @@ context_def_p(pm_parser_t *parser) {
67966802
return false;
67976803
}
67986804

6805+
/**
6806+
* Returns a human readable string for the given context, used in error
6807+
* messages.
6808+
*/
6809+
static const char *
6810+
context_human(pm_context_t context) {
6811+
switch (context) {
6812+
case PM_CONTEXT_NONE:
6813+
assert(false && "unreachable");
6814+
return "";
6815+
case PM_CONTEXT_BEGIN: return "begin statement";
6816+
case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block";
6817+
case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block";
6818+
case PM_CONTEXT_CASE_WHEN: return "'when' clause";
6819+
case PM_CONTEXT_CASE_IN: return "'in' clause";
6820+
case PM_CONTEXT_CLASS: return "class definition";
6821+
case PM_CONTEXT_DEF: return "method definition";
6822+
case PM_CONTEXT_DEF_PARAMS: return "method parameters";
6823+
case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value";
6824+
case PM_CONTEXT_ELSE: return "'else' clause";
6825+
case PM_CONTEXT_ELSIF: return "'elsif' clause";
6826+
case PM_CONTEXT_EMBEXPR: return "embedded expression";
6827+
case PM_CONTEXT_ENSURE: return "'ensure' clause";
6828+
case PM_CONTEXT_ENSURE_DEF: return "'ensure' clause";
6829+
case PM_CONTEXT_FOR: return "for loop";
6830+
case PM_CONTEXT_FOR_INDEX: return "for loop index";
6831+
case PM_CONTEXT_IF: return "if statement";
6832+
case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block";
6833+
case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block";
6834+
case PM_CONTEXT_MAIN: return "top level context";
6835+
case PM_CONTEXT_MODULE: return "module definition";
6836+
case PM_CONTEXT_PARENS: return "parentheses";
6837+
case PM_CONTEXT_POSTEXE: return "'END' block";
6838+
case PM_CONTEXT_PREDICATE: return "predicate";
6839+
case PM_CONTEXT_PREEXE: return "'BEGIN' block";
6840+
case PM_CONTEXT_RESCUE_ELSE: return "'else' clause";
6841+
case PM_CONTEXT_RESCUE_ELSE_DEF: return "'else' clause";
6842+
case PM_CONTEXT_RESCUE: return "'rescue' clause";
6843+
case PM_CONTEXT_RESCUE_DEF: return "'rescue' clause";
6844+
case PM_CONTEXT_SCLASS: return "singleton class definition";
6845+
case PM_CONTEXT_UNLESS: return "unless statement";
6846+
case PM_CONTEXT_UNTIL: return "until statement";
6847+
case PM_CONTEXT_WHILE: return "while statement";
6848+
}
6849+
6850+
assert(false && "unreachable");
6851+
return "";
6852+
}
6853+
67996854
/******************************************************************************/
68006855
/* Specific token lexers */
68016856
/******************************************************************************/
@@ -10385,8 +10440,8 @@ parser_lex(pm_parser_t *parser) {
1038510440
typedef enum {
1038610441
PM_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator
1038710442
PM_BINDING_POWER_STATEMENT = 2,
10388-
PM_BINDING_POWER_MODIFIER = 4, // if unless until while
10389-
PM_BINDING_POWER_MODIFIER_RESCUE = 6, // rescue
10443+
PM_BINDING_POWER_MODIFIER_RESCUE = 4, // rescue
10444+
PM_BINDING_POWER_MODIFIER = 6, // if unless until while
1039010445
PM_BINDING_POWER_COMPOSITION = 8, // and or
1039110446
PM_BINDING_POWER_NOT = 10, // not
1039210447
PM_BINDING_POWER_MATCH = 12, // => in
@@ -10440,15 +10495,15 @@ typedef struct {
1044010495
#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false }
1044110496

1044210497
pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
10498+
// rescue
10499+
[PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER_RESCUE),
10500+
1044310501
// if unless until while
1044410502
[PM_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
1044510503
[PM_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
1044610504
[PM_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
1044710505
[PM_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
1044810506

10449-
// rescue
10450-
[PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER_RESCUE),
10451-
1045210507
// and or
1045310508
[PM_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
1045410509
[PM_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
@@ -14177,7 +14232,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current) {
1417714232
* Parse an expression that begins with the previous node that we just lexed.
1417814233
*/
1417914234
static inline pm_node_t *
14180-
parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call) {
14235+
parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
1418114236
switch (parser->current.type) {
1418214237
case PM_TOKEN_BRACKET_LEFT_ARRAY: {
1418314238
parser_lex(parser);
@@ -14595,30 +14650,30 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1459514650

1459614651
if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
1459714652
node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX);
14598-
}
14599-
else {
14653+
} else {
1460014654
// Check if `it` is not going to be assigned.
1460114655
switch (parser->current.type) {
14602-
case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL:
14603-
case PM_TOKEN_AMPERSAND_EQUAL:
14604-
case PM_TOKEN_CARET_EQUAL:
14605-
case PM_TOKEN_EQUAL:
14606-
case PM_TOKEN_GREATER_GREATER_EQUAL:
14607-
case PM_TOKEN_LESS_LESS_EQUAL:
14608-
case PM_TOKEN_MINUS_EQUAL:
14609-
case PM_TOKEN_PARENTHESIS_RIGHT:
14610-
case PM_TOKEN_PERCENT_EQUAL:
14611-
case PM_TOKEN_PIPE_EQUAL:
14612-
case PM_TOKEN_PIPE_PIPE_EQUAL:
14613-
case PM_TOKEN_PLUS_EQUAL:
14614-
case PM_TOKEN_SLASH_EQUAL:
14615-
case PM_TOKEN_STAR_EQUAL:
14616-
case PM_TOKEN_STAR_STAR_EQUAL:
14617-
break;
14618-
default:
14619-
// Once we know it's neither a method call nor an assignment,
14620-
// we can finally create `it` default parameter.
14621-
node = pm_node_check_it(parser, node);
14656+
case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL:
14657+
case PM_TOKEN_AMPERSAND_EQUAL:
14658+
case PM_TOKEN_CARET_EQUAL:
14659+
case PM_TOKEN_EQUAL:
14660+
case PM_TOKEN_GREATER_GREATER_EQUAL:
14661+
case PM_TOKEN_LESS_LESS_EQUAL:
14662+
case PM_TOKEN_MINUS_EQUAL:
14663+
case PM_TOKEN_PARENTHESIS_RIGHT:
14664+
case PM_TOKEN_PERCENT_EQUAL:
14665+
case PM_TOKEN_PIPE_EQUAL:
14666+
case PM_TOKEN_PIPE_PIPE_EQUAL:
14667+
case PM_TOKEN_PLUS_EQUAL:
14668+
case PM_TOKEN_SLASH_EQUAL:
14669+
case PM_TOKEN_STAR_EQUAL:
14670+
case PM_TOKEN_STAR_STAR_EQUAL:
14671+
break;
14672+
default:
14673+
// Once we know it's neither a method call nor an
14674+
// assignment, we can finally create `it` default
14675+
// parameter.
14676+
node = pm_node_check_it(parser, node);
1462214677
}
1462314678
}
1462414679

@@ -14656,6 +14711,9 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1465614711
// If we get here, then we tried to find something in the
1465714712
// heredoc but couldn't actually parse anything, so we'll just
1465814713
// return a missing node.
14714+
//
14715+
// parse_string_part handles its own errors, so there is no need
14716+
// for us to add one here.
1465914717
node = (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end);
1466014718
} else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
1466114719
// If we get here, then the part that we parsed was plain string
@@ -16301,6 +16359,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1630116359
// context of a multiple assignment. We enforce that here. We'll
1630216360
// still lex past it though and create a missing node place.
1630316361
if (binding_power != PM_BINDING_POWER_STATEMENT) {
16362+
pm_parser_err_previous(parser, diag_id);
1630416363
return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end);
1630516364
}
1630616365

@@ -16487,12 +16546,34 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
1648716546

1648816547
return parse_symbol(parser, &lex_mode, PM_LEX_STATE_END);
1648916548
}
16490-
default:
16491-
if (context_recoverable(parser, &parser->current)) {
16549+
default: {
16550+
pm_context_t recoverable = context_recoverable(parser, &parser->current);
16551+
16552+
if (recoverable != PM_CONTEXT_NONE) {
1649216553
parser->recovering = true;
16554+
16555+
// If the given error is not the generic one, then we'll add it
16556+
// here because it will provide more context in addition to the
16557+
// recoverable error that we will also add.
16558+
if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) {
16559+
pm_parser_err_previous(parser, diag_id);
16560+
}
16561+
16562+
// If we get here, then we are assuming this token is closing a
16563+
// parent context, so we'll indicate that to the user so that
16564+
// they know how we behaved.
16565+
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_type_human(parser->current.type), context_human(recoverable));
16566+
} else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) {
16567+
// We're going to make a special case here, because "cannot
16568+
// parse expression" is pretty generic, and we know here that we
16569+
// have an unexpected token.
16570+
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_type_human(parser->current.type));
16571+
} else {
16572+
pm_parser_err_previous(parser, diag_id);
1649316573
}
1649416574

1649516575
return (pm_node_t *) pm_missing_node_create(parser, parser->previous.start, parser->previous.end);
16576+
}
1649616577
}
1649716578
}
1649816579

@@ -17455,15 +17536,12 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
1745517536
*/
1745617537
static pm_node_t *
1745717538
parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id) {
17458-
pm_token_t recovery = parser->previous;
17459-
pm_node_t *node = parse_expression_prefix(parser, binding_power, accepts_command_call);
17539+
pm_node_t *node = parse_expression_prefix(parser, binding_power, accepts_command_call, diag_id);
1746017540

1746117541
switch (PM_NODE_TYPE(node)) {
1746217542
case PM_MISSING_NODE:
1746317543
// If we found a syntax error, then the type of node returned by
17464-
// parse_expression_prefix is going to be a missing node. In that
17465-
// case we need to add the error message to the parser's error list.
17466-
pm_parser_err(parser, recovery.end, recovery.end, diag_id);
17544+
// parse_expression_prefix is going to be a missing node.
1746717545
return node;
1746817546
case PM_PRE_EXECUTION_NODE:
1746917547
case PM_POST_EXECUTION_NODE:
@@ -17472,7 +17550,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
1747217550
case PM_UNDEF_NODE:
1747317551
// These expressions are statements, and cannot be followed by
1747417552
// operators (except modifiers).
17475-
if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER_RESCUE) {
17553+
if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
1747617554
return node;
1747717555
}
1747817556
break;

0 commit comments

Comments
 (0)