Skip to content

Commit 4c5fd1a

Browse files
committed
Fix parsing pinned local variable pattern for numbered parameter
Fix #2094 The part of `parse_variable_call` for variables was split into a new function `parse_variable` and used it.
1 parent c790abd commit 4c5fd1a

File tree

5 files changed

+210
-117
lines changed

5 files changed

+210
-117
lines changed

src/prism.c

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -12768,6 +12768,65 @@ outer_scope_using_numbered_parameters_p(pm_parser_t *parser) {
1276812768
return false;
1276912769
}
1277012770

12771+
/**
12772+
* Parse an identifier into either a local variable read. If the local variable
12773+
* is not found, it returns NULL instead.
12774+
*/
12775+
static pm_local_variable_read_node_t *
12776+
parse_variable(pm_parser_t *parser) {
12777+
int depth;
12778+
if ((depth = pm_parser_local_depth(parser, &parser->previous)) != -1) {
12779+
return pm_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth);
12780+
}
12781+
12782+
if (!parser->current_scope->closed && pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end)) {
12783+
// Now that we know we have a numbered parameter, we need to check
12784+
// if it's allowed in this context. If it is, then we will create a
12785+
// local variable read. If it's not, then we'll create a normal call
12786+
// node but add an error.
12787+
if (parser->current_scope->explicit_params) {
12788+
pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_NOT_ALLOWED);
12789+
} else if (outer_scope_using_numbered_parameters_p(parser)) {
12790+
pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE);
12791+
} else {
12792+
// Indicate that this scope is using numbered params so that child
12793+
// scopes cannot.
12794+
uint8_t number = parser->previous.start[1];
12795+
12796+
// We subtract the value for the character '0' to get the actual
12797+
// integer value of the number (only _1 through _9 are valid)
12798+
uint8_t numbered_parameters = (uint8_t) (number - '0');
12799+
if (numbered_parameters > parser->current_scope->numbered_parameters) {
12800+
parser->current_scope->numbered_parameters = numbered_parameters;
12801+
pm_parser_numbered_parameters_set(parser, numbered_parameters);
12802+
}
12803+
12804+
// When you use a numbered parameter, it implies the existence
12805+
// of all of the locals that exist before it. For example,
12806+
// referencing _2 means that _1 must exist. Therefore here we
12807+
// loop through all of the possibilities and add them into the
12808+
// constant pool.
12809+
uint8_t current = '1';
12810+
uint8_t *value;
12811+
12812+
while (current < number) {
12813+
value = malloc(2);
12814+
value[0] = '_';
12815+
value[1] = current++;
12816+
pm_parser_local_add_owned(parser, value, 2);
12817+
}
12818+
12819+
// Now we can add the actual token that is being used. For
12820+
// this one we can add a shared version since it is directly
12821+
// referenced in the source.
12822+
pm_parser_local_add_token(parser, &parser->previous);
12823+
return pm_local_variable_read_node_create(parser, &parser->previous, 0);
12824+
}
12825+
}
12826+
12827+
return NULL;
12828+
}
12829+
1277112830
/**
1277212831
* Parse an identifier into either a local variable read or a call.
1277312832
*/
@@ -12776,56 +12835,8 @@ parse_variable_call(pm_parser_t *parser) {
1277612835
pm_node_flags_t flags = 0;
1277712836

1277812837
if (!match1(parser, PM_TOKEN_PARENTHESIS_LEFT) && (parser->previous.end[-1] != '!') && (parser->previous.end[-1] != '?')) {
12779-
int depth;
12780-
if ((depth = pm_parser_local_depth(parser, &parser->previous)) != -1) {
12781-
return (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth);
12782-
}
12783-
12784-
if (!parser->current_scope->closed && pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end)) {
12785-
// Now that we know we have a numbered parameter, we need to check
12786-
// if it's allowed in this context. If it is, then we will create a
12787-
// local variable read. If it's not, then we'll create a normal call
12788-
// node but add an error.
12789-
if (parser->current_scope->explicit_params) {
12790-
pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_NOT_ALLOWED);
12791-
} else if (outer_scope_using_numbered_parameters_p(parser)) {
12792-
pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE);
12793-
} else {
12794-
// Indicate that this scope is using numbered params so that child
12795-
// scopes cannot.
12796-
uint8_t number = parser->previous.start[1];
12797-
12798-
// We subtract the value for the character '0' to get the actual
12799-
// integer value of the number (only _1 through _9 are valid)
12800-
uint8_t numbered_parameters = (uint8_t) (number - '0');
12801-
if (numbered_parameters > parser->current_scope->numbered_parameters) {
12802-
parser->current_scope->numbered_parameters = numbered_parameters;
12803-
pm_parser_numbered_parameters_set(parser, numbered_parameters);
12804-
}
12805-
12806-
// When you use a numbered parameter, it implies the existence
12807-
// of all of the locals that exist before it. For example,
12808-
// referencing _2 means that _1 must exist. Therefore here we
12809-
// loop through all of the possibilities and add them into the
12810-
// constant pool.
12811-
uint8_t current = '1';
12812-
uint8_t *value;
12813-
12814-
while (current < number) {
12815-
value = malloc(2);
12816-
value[0] = '_';
12817-
value[1] = current++;
12818-
pm_parser_local_add_owned(parser, value, 2);
12819-
}
12820-
12821-
// Now we can add the actual token that is being used. For
12822-
// this one we can add a shared version since it is directly
12823-
// referenced in the source.
12824-
pm_parser_local_add_token(parser, &parser->previous);
12825-
return (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, 0);
12826-
}
12827-
}
12828-
12838+
pm_local_variable_read_node_t *node = parse_variable(parser);
12839+
if (node != NULL) return (pm_node_t *) node;
1282912840
flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL;
1283012841
}
1283112842

@@ -13384,15 +13395,12 @@ parse_pattern_primitive(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
1338413395
// expression to determine if it's a variable or an expression.
1338513396
switch (parser->current.type) {
1338613397
case PM_TOKEN_IDENTIFIER: {
13387-
int depth = pm_parser_local_depth(parser, &parser->current);
13388-
13389-
if (depth == -1) {
13390-
depth = 0;
13391-
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NO_LOCAL_VARIABLE, (int) (parser->current.end - parser->current.start), parser->current.start);
13392-
}
13393-
13394-
pm_node_t *variable = (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->current, (uint32_t) depth);
1339513398
parser_lex(parser);
13399+
pm_node_t *variable = (pm_node_t *) parse_variable(parser);
13400+
if (variable == NULL) {
13401+
PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_NO_LOCAL_VARIABLE, (int) (parser->previous.end - parser->previous.start), parser->previous.start);
13402+
variable = (pm_node_t *) pm_local_variable_read_node_create(parser, &parser->previous, 0);
13403+
}
1339613404

1339713405
return (pm_node_t *) pm_pinned_variable_node_create(parser, &operator, variable);
1339813406
}

test/prism/fixtures/case.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,9 @@ in 3
4242
end
4343

4444
case 1 in 2; in 3; end
45+
46+
1.then do
47+
case 1
48+
in ^_1
49+
end
50+
end

test/prism/fixtures/patterns.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,5 @@ foo do
200200
end
201201

202202
foo => Object[{x:}]
203+
204+
1.then { 1 in ^_1 }

test/prism/snapshots/case.txt

Lines changed: 66 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/prism/snapshots/patterns.txt

Lines changed: 70 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)