@@ -10544,24 +10544,28 @@ parser_lex(pm_parser_t *parser) {
1054410544 pm_token_type_t type = PM_TOKEN_BRACE_LEFT;
1054510545
1054610546 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
10547- // This { begins a lambda
10547+ /* This { begins a lambda */
1054810548 parser->command_start = true;
1054910549 lex_state_set(parser, PM_LEX_STATE_BEG);
1055010550 type = PM_TOKEN_LAMBDA_BEGIN;
1055110551 } else if (lex_state_p(parser, PM_LEX_STATE_LABELED)) {
10552- // This { begins a hash literal
10552+ /* This { begins a hash literal */
1055310553 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10554+ type = PM_TOKEN_BRACE_LEFT_HASH;
1055410555 } else if (lex_state_p(parser, PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_END | PM_LEX_STATE_ENDFN)) {
10555- // This { begins a block
10556+ /* This { begins a block */
1055610557 parser->command_start = true;
1055710558 lex_state_set(parser, PM_LEX_STATE_BEG);
1055810559 } else if (lex_state_p(parser, PM_LEX_STATE_ENDARG)) {
10559- // This { begins a block on a command
10560+ /* This { begins a block following a parenthesized
10561+ * command argument */
1056010562 parser->command_start = true;
1056110563 lex_state_set(parser, PM_LEX_STATE_BEG);
10564+ type = PM_TOKEN_BRACE_LEFT_ARGUMENT;
1056210565 } else {
10563- // This { begins a hash literal
10566+ /* This { begins a hash literal */
1056410567 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10568+ type = PM_TOKEN_BRACE_LEFT_HASH;
1056510569 }
1056610570
1056710571 parser->enclosure_nesting++;
@@ -13780,7 +13784,7 @@ parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *nod
1378013784 pm_token_t operator = parser->previous;
1378113785 pm_node_t *value = NULL;
1378213786
13783- if (match1(parser, PM_TOKEN_BRACE_LEFT )) {
13787+ if (match1(parser, PM_TOKEN_BRACE_LEFT_HASH )) {
1378413788 // If we're about to parse a nested hash that is being
1378513789 // pushed into this hash directly with **, then we want the
1378613790 // inner hash to share the static literals with the outer
@@ -15398,7 +15402,7 @@ parse_block(pm_parser_t *parser, uint16_t depth) {
1539815402 * managed by the lexer. A `do`/`end` block is delimited by keywords, so we
1539915403 * push the frame here (covering the block parameters and body) and pop it
1540015404 * before consuming `end`, mirroring parse.y's `do_body` rule. */
15401- bool do_block = opening.type != PM_TOKEN_BRACE_LEFT;
15405+ bool do_block = opening.type != PM_TOKEN_BRACE_LEFT && opening.type != PM_TOKEN_BRACE_LEFT_ARGUMENT ;
1540215406 if (do_block) pm_accepts_block_stack_push(parser, true);
1540315407 pm_parser_scope_push(parser, false);
1540415408
@@ -15423,7 +15427,7 @@ parse_block(pm_parser_t *parser, uint16_t depth) {
1542315427 accept1(parser, PM_TOKEN_NEWLINE);
1542415428 pm_node_t *statements = NULL;
1542515429
15426- if (opening.type == PM_TOKEN_BRACE_LEFT ) {
15430+ if (!do_block ) {
1542715431 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
1542815432 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1)));
1542915433 }
@@ -15542,7 +15546,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_a
1554215546 * it, pop the command-args frame beneath it, and restore the block
1554315547 * frame so the block's `}` still pops it. This mirrors the `tLBRACE_ARG`
1554415548 * lookahead handling in parse.y's `command_args` rule. */
15545- bool lookahead_brace = match1 (parser, PM_TOKEN_BRACE_LEFT);
15549+ bool lookahead_brace = match2 (parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT );
1554615550 if (lookahead_brace) pm_accepts_block_stack_pop(parser);
1554715551 pm_accepts_block_stack_pop(parser);
1554815552 if (lookahead_brace) pm_accepts_block_stack_push(parser, true);
@@ -15554,7 +15558,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_a
1555415558 if (full_arguments) {
1555515559 pm_block_node_t *block = NULL;
1555615560
15557- if (accept1 (parser, PM_TOKEN_BRACE_LEFT)) {
15561+ if (accept2 (parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT )) {
1555815562 found |= true;
1555915563 block = parse_block(parser, (uint16_t) (depth + 1));
1556015564 pm_arguments_validate_block(parser, arguments, block);
@@ -17359,7 +17363,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
1735917363 pm_array_pattern_node_requireds_append(parser->arena, node, inner);
1736017364 return UP(node);
1736117365 }
17362- case PM_TOKEN_BRACE_LEFT : {
17366+ case PM_TOKEN_BRACE_LEFT_HASH : {
1736317367 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
1736417368 parser->pattern_matching_newlines = false;
1736517369
@@ -17580,7 +17584,7 @@ parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, p
1758017584 switch (parser->current.type) {
1758117585 case PM_TOKEN_IDENTIFIER:
1758217586 case PM_TOKEN_BRACKET_LEFT_ARRAY:
17583- case PM_TOKEN_BRACE_LEFT :
17587+ case PM_TOKEN_BRACE_LEFT_HASH :
1758417588 case PM_TOKEN_CARET:
1758517589 case PM_TOKEN_CONSTANT:
1758617590 case PM_TOKEN_UCOLON_COLON:
@@ -19180,6 +19184,13 @@ parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
1918019184 /* If this is the end of the file or we match a right parenthesis, then we
1918119185 * have an empty parentheses node, and we can immediately return. */
1918219186 if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
19187+ /* A command argument group sets EXPR_ENDARG before its ')' is
19188+ * consumed, even when the group is empty, so that a following '{' is
19189+ * scanned as a block brace. */
19190+ if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT) && opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19191+ lex_state_set(parser, PM_LEX_STATE_ENDARG);
19192+ }
19193+
1918319194 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
1918419195 pop_block_exits(parser, previous_block_exits);
1918519196 return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, paren_flags));
@@ -19503,7 +19514,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, u
1950319514 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
1950419515 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
1950519516 return parse_parentheses(parser, binding_power, flags, depth);
19506- case PM_TOKEN_BRACE_LEFT : {
19517+ case PM_TOKEN_BRACE_LEFT_HASH : {
1950719518 // If we were passed a current_hash_keys via the parser, then that
1950819519 // means we're already parsing a hash and we want to share the set
1950919520 // of hash keys with this inner hash we're about to parse for the
0 commit comments