Skip to content

Commit bf4a1e1

Browse files
committed
Add numbered_parameters field to BlockNode and LambdaNode
We are aware at parse time how many numbered parameters we have on a BlockNode or LambdaNode, but prior to this commit, did not store that information anywhere in its own right. The numbered parameters were stored as locals, but this does not distinguish them from other locals that have been set, for example in `a { b = 1; _1 }` there is nothing on the AST that distinguishes b from _1. Consumers such as the compiler need to know information about how many numbered parameters exist to set up their own tables around parameters. Since we have this information at parse time, we should compute it here, instead of deferring the work later on.
1 parent 17b60b2 commit bf4a1e1

File tree

194 files changed

+1357
-869
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

194 files changed

+1357
-869
lines changed

config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,8 @@ nodes:
596596
type: location
597597
- name: closing_loc
598598
type: location
599+
- name: numbered_parameters
600+
type: uint32
599601
comment: |
600602
Represents a block of ruby code.
601603
@@ -1758,6 +1760,8 @@ nodes:
17581760
kind: BlockParametersNode
17591761
- name: body
17601762
type: node?
1763+
- name: numbered_parameters
1764+
type: uint32
17611765
comment: |
17621766
Represents using a lambda literal (not the lambda method call).
17631767

include/prism/parser.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -468,11 +468,12 @@ typedef struct pm_scope {
468468
bool explicit_params;
469469

470470
/**
471-
* A boolean indicating whether or not this scope has numbered parameters.
471+
* An integer indicating the number of numbered parameters on this scope.
472472
* This is necessary to determine if child blocks are allowed to use
473-
* numbered parameters.
473+
* numbered parameters, and to pass information to consumers of the AST
474+
* about how many numbered parameters exist.
474475
*/
475-
bool numbered_params;
476+
uint32_t numbered_parameters;
476477

477478
/**
478479
* A transparent scope is a scope that cannot have locals set on itself.

src/prism.c

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,7 +1465,7 @@ pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, p
14651465
* Allocate and initialize a new BlockNode node.
14661466
*/
14671467
static pm_block_node_t *
1468-
pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *opening, pm_block_parameters_node_t *parameters, pm_node_t *body, const pm_token_t *closing) {
1468+
pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *opening, pm_block_parameters_node_t *parameters, pm_node_t *body, const pm_token_t *closing, uint32_t numbered_parameters) {
14691469
pm_block_node_t *node = PM_ALLOC_NODE(parser, pm_block_node_t);
14701470

14711471
*node = (pm_block_node_t) {
@@ -1476,6 +1476,7 @@ pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const p
14761476
.locals = *locals,
14771477
.parameters = parameters,
14781478
.body = body,
1479+
.numbered_parameters = numbered_parameters,
14791480
.opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
14801481
.closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
14811482
};
@@ -3937,7 +3938,8 @@ pm_lambda_node_create(
39373938
const pm_token_t *opening,
39383939
const pm_token_t *closing,
39393940
pm_block_parameters_node_t *parameters,
3940-
pm_node_t *body
3941+
pm_node_t *body,
3942+
uint32_t numbered_parameters
39413943
) {
39423944
pm_lambda_node_t *node = PM_ALLOC_NODE(parser, pm_lambda_node_t);
39433945

@@ -3954,7 +3956,8 @@ pm_lambda_node_create(
39543956
.opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
39553957
.closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
39563958
.parameters = parameters,
3957-
.body = body
3959+
.body = body,
3960+
.numbered_parameters = numbered_parameters
39583961
};
39593962

39603963
return node;
@@ -5746,7 +5749,7 @@ pm_parser_scope_push(pm_parser_t *parser, bool closed) {
57465749
.previous = parser->current_scope,
57475750
.closed = closed,
57485751
.explicit_params = false,
5749-
.numbered_params = false,
5752+
.numbered_parameters = 0,
57505753
.transparent = false
57515754
};
57525755

@@ -5768,7 +5771,7 @@ pm_parser_scope_push_transparent(pm_parser_t *parser) {
57685771
.previous = parser->current_scope,
57695772
.closed = false,
57705773
.explicit_params = false,
5771-
.numbered_params = false,
5774+
.numbered_parameters = 0,
57725775
.transparent = true
57735776
};
57745777

@@ -5821,6 +5824,18 @@ pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id) {
58215824
}
58225825
}
58235826

5827+
/**
5828+
* Add a constant id to the local table of the current scope.
5829+
*/
5830+
static inline void
5831+
pm_parser_numbered_parameters_set(pm_parser_t *parser, uint32_t numbered_parameters) {
5832+
pm_scope_t *scope = parser->current_scope;
5833+
while (scope && scope->transparent) scope = scope->previous;
5834+
5835+
assert(scope != NULL);
5836+
scope->numbered_parameters = numbered_parameters;
5837+
}
5838+
58245839
/**
58255840
* Add a local variable from a location to the current scope.
58265841
*/
@@ -12052,9 +12067,10 @@ parse_block(pm_parser_t *parser) {
1205212067
}
1205312068

1205412069
pm_constant_id_list_t locals = parser->current_scope->locals;
12070+
uint32_t numbered_parameters = parser->current_scope->numbered_parameters;
1205512071
pm_parser_scope_pop(parser);
1205612072
pm_accepts_block_stack_pop(parser);
12057-
return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous);
12073+
return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous, numbered_parameters);
1205812074
}
1205912075

1206012076
/**
@@ -12625,9 +12641,9 @@ parse_alias_argument(pm_parser_t *parser, bool first) {
1262512641
* numbered parameters.
1262612642
*/
1262712643
static bool
12628-
outer_scope_using_numbered_params_p(pm_parser_t *parser) {
12644+
outer_scope_using_numbered_parameters_p(pm_parser_t *parser) {
1262912645
for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
12630-
if (scope->numbered_params) return true;
12646+
if (scope->numbered_parameters) return true;
1263112647
}
1263212648

1263312649
return false;
@@ -12647,25 +12663,32 @@ parse_variable_call(pm_parser_t *parser) {
1264712663
}
1264812664

1264912665
if (!parser->current_scope->closed && pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end)) {
12650-
// Indicate that this scope is using numbered params so that child
12651-
// scopes cannot.
12652-
parser->current_scope->numbered_params = true;
12653-
1265412666
// Now that we know we have a numbered parameter, we need to check
1265512667
// if it's allowed in this context. If it is, then we will create a
1265612668
// local variable read. If it's not, then we'll create a normal call
1265712669
// node but add an error.
1265812670
if (parser->current_scope->explicit_params) {
1265912671
pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_NOT_ALLOWED);
12660-
} else if (outer_scope_using_numbered_params_p(parser)) {
12672+
} else if (outer_scope_using_numbered_parameters_p(parser)) {
1266112673
pm_parser_err_previous(parser, PM_ERR_NUMBERED_PARAMETER_OUTER_SCOPE);
1266212674
} else {
12675+
// Indicate that this scope is using numbered params so that child
12676+
// scopes cannot.
12677+
uint8_t number = parser->previous.start[1];
12678+
12679+
// We subtract the value for the character '0' to get the actual
12680+
// integer value of the number (only _1 through _9 are valid)
12681+
uint32_t number_as_int = (uint32_t) (number - '0');
12682+
if (number_as_int > parser->current_scope->numbered_parameters) {
12683+
parser->current_scope->numbered_parameters = number_as_int;
12684+
pm_parser_numbered_parameters_set(parser, number_as_int);
12685+
}
12686+
1266312687
// When you use a numbered parameter, it implies the existence
1266412688
// of all of the locals that exist before it. For example,
1266512689
// referencing _2 means that _1 must exist. Therefore here we
1266612690
// loop through all of the possibilities and add them into the
1266712691
// constant pool.
12668-
uint8_t number = parser->previous.start[1];
1266912692
uint8_t current = '1';
1267012693
uint8_t *value;
1267112694

@@ -15856,9 +15879,10 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power) {
1585615879
}
1585715880

1585815881
pm_constant_id_list_t locals = parser->current_scope->locals;
15882+
uint32_t numbered_parameters = parser->current_scope->numbered_parameters;
1585915883
pm_parser_scope_pop(parser);
1586015884
pm_accepts_block_stack_pop(parser);
15861-
return (pm_node_t *) pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, params, body);
15885+
return (pm_node_t *) pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, params, body, numbered_parameters);
1586215886
}
1586315887
case PM_TOKEN_UPLUS: {
1586415888
parser_lex(parser);

test/prism/errors_test.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,8 @@ def test_module_definition_in_method_body_within_block
452452
nil,
453453
StatementsNode([ModuleNode([], Location(), ConstantReadNode(:Foo), nil, Location(), :Foo)]),
454454
Location(),
455-
Location()
455+
Location(),
456+
0
456457
),
457458
0
458459
)]
@@ -616,7 +617,8 @@ def test_do_not_allow_trailing_commas_in_lambda_parameters
616617
Location(),
617618
Location()
618619
),
619-
nil
620+
nil,
621+
0
620622
)
621623
assert_errors expected, "-> (a, b, ) {}", [
622624
["unexpected `,` in parameters", 8..9]
@@ -992,7 +994,8 @@ def test_do_not_allow_forward_arguments_in_lambda_literals
992994
Location(),
993995
Location(),
994996
BlockParametersNode(ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil), [], Location(), Location()),
995-
nil
997+
nil,
998+
0
996999
)
9971000

9981001
assert_errors expected, "->(...) {}", [
@@ -1014,7 +1017,8 @@ def test_do_not_allow_forward_arguments_in_blocks
10141017
BlockParametersNode(ParametersNode([], [], nil, [], [], ForwardingParameterNode(), nil), [], Location(), Location()),
10151018
nil,
10161019
Location(),
1017-
Location()
1020+
Location(),
1021+
0
10181022
),
10191023
0
10201024
)

test/prism/snapshots/begin_ensure.txt

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

0 commit comments

Comments
 (0)