Skip to content

Commit 43ed8c1

Browse files
committed
Split up lbrace token
This also more closely matches parse.y, and makes it easier to delineate which brace belongs to which type.
1 parent 06dff54 commit 43ed8c1

7 files changed

Lines changed: 41 additions & 36 deletions

File tree

config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,10 @@ tokens:
388388
comment: "!~"
389389
- name: BRACE_LEFT
390390
comment: "{"
391+
- name: BRACE_LEFT_ARGUMENT
392+
comment: "{ for a block following a parenthesized argument"
393+
- name: BRACE_LEFT_HASH
394+
comment: "{ for a hash literal"
391395
- name: BRACKET_LEFT
392396
comment: "["
393397
- name: BRACKET_LEFT_ARRAY

lib/prism/lex_compat.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ def deconstruct_keys(keys) # :nodoc:
7878
BANG_EQUAL: :on_op,
7979
BANG_TILDE: :on_op,
8080
BRACE_LEFT: :on_lbrace,
81+
BRACE_LEFT_ARGUMENT: :on_lbrace,
82+
BRACE_LEFT_HASH: :on_lbrace,
8183
BRACE_RIGHT: :on_rbrace,
8284
BRACKET_LEFT: :on_lbracket,
8385
BRACKET_LEFT_ARRAY: :on_lbracket,

lib/prism/translation/parser/lexer.rb

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ class Lexer # :nodoc:
3333
BANG_EQUAL: :tNEQ,
3434
BANG_TILDE: :tNMATCH,
3535
BRACE_LEFT: :tLCURLY,
36+
BRACE_LEFT_ARGUMENT: :tLBRACE_ARG,
37+
BRACE_LEFT_HASH: :tLBRACE,
3638
BRACE_RIGHT: :tRCURLY,
3739
BRACKET_LEFT: :tLBRACK2,
3840
BRACKET_LEFT_ARRAY: :tLBRACK,
@@ -184,16 +186,6 @@ class Lexer # :nodoc:
184186
WORDS_SEP: :tSPACE
185187
}
186188

187-
# These constants represent flags in our lex state. We really, really
188-
# don't want to be using them and we really, really don't want to be
189-
# exposing them as part of our public API. Unfortunately, we don't have
190-
# another way of matching the exact tokens that the parser gem expects
191-
# without them. We should find another way to do this, but in the
192-
# meantime we'll hide them from the documentation and mark them as
193-
# private constants.
194-
EXPR_BEG = 0x1
195-
EXPR_LABEL = 0x400
196-
197189
# Types of tokens that are allowed to continue a method call with comments in-between.
198190
# For these, the parser gem doesn't emit a newline token after the last comment.
199191
COMMENT_CONTINUATION_TYPES = Set.new([:COMMENT, :AMPERSAND_DOT, :DOT])
@@ -202,7 +194,7 @@ class Lexer # :nodoc:
202194
# Heredocs are complex and require us to keep track of a bit of info to refer to later
203195
HeredocData = Struct.new(:identifier, :common_whitespace, keyword_init: true)
204196

205-
private_constant :TYPES, :EXPR_BEG, :EXPR_LABEL, :HeredocData
197+
private_constant :TYPES, :HeredocData
206198

207199
# The Parser::Source::Buffer that the tokens were lexed from.
208200
attr_reader :source_buffer
@@ -241,7 +233,7 @@ def to_a
241233
comment_newline_location = nil
242234

243235
while index < length
244-
token, state = lexed[index]
236+
token, _ = lexed[index]
245237
index += 1
246238
next if TYPES_ALWAYS_SKIP.include?(token.type)
247239

@@ -312,8 +304,6 @@ def to_a
312304
value.chomp!(":")
313305
when :tLABEL_END
314306
value.chomp!(":")
315-
when :tLCURLY
316-
type = :tLBRACE if state == EXPR_BEG | EXPR_LABEL
317307
when :tNTH_REF
318308
value = parse_integer(value.delete_prefix("$"))
319309
when :tOP_ASGN

src/prism.c

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

templates/src/tokens.c.erb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ pm_token_str(pm_token_type_t token_type) {
5353
return "'!~'";
5454
case PM_TOKEN_BRACE_LEFT:
5555
return "'{'";
56+
case PM_TOKEN_BRACE_LEFT_ARGUMENT:
57+
return "'{'";
58+
case PM_TOKEN_BRACE_LEFT_HASH:
59+
return "'{'";
5660
case PM_TOKEN_BRACE_RIGHT:
5761
return "'}'";
5862
case PM_TOKEN_BRACKET_LEFT:

test/prism/errors/command_calls_25.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
^ expected a `do` keyword or a `{` to open the lambda block
44
^ unexpected ')', expecting end-of-input
55
^ unexpected ')', ignoring it
6-
^ unexpected end-of-input, assuming it is closing the parent top level context
6+
^ unexpected '{', ignoring it
7+
^ unexpected '}', ignoring it
78
^~ expected a lambda block beginning with `do` to end with `end`
89

test/prism/ruby/parser_test.rb

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,8 @@ class ParserTest < TestCase
111111
skip_tokens = [
112112
"dash_heredocs.txt",
113113
"embdoc_no_newline_at_end.txt",
114-
"seattlerb/bug169.txt",
115114
"seattlerb/case_in.txt",
116115
"seattlerb/difficult4__leading_dots2.txt",
117-
"seattlerb/difficult6__7.txt",
118-
"seattlerb/difficult6__8.txt",
119116
"seattlerb/heredoc_unicode.txt",
120117
"seattlerb/parse_line_heredoc.txt",
121118
"seattlerb/pct_w_heredoc_interp_nested.txt",
@@ -128,14 +125,10 @@ class ParserTest < TestCase
128125
"whitequark/beginless_irange_after_newline.txt",
129126
"whitequark/forward_arg_with_open_args.txt",
130127
"whitequark/kwarg_no_paren.txt",
131-
"whitequark/lbrace_arg_after_command_args.txt",
132128
"whitequark/multiple_pattern_matches.txt",
133129
"whitequark/newline_in_hash_argument.txt",
134130
"whitequark/pattern_matching_hash.txt",
135-
"whitequark/ruby_bug_14690.txt",
136-
"whitequark/ruby_bug_9669.txt",
137-
"whitequark/space_args_arg_block.txt",
138-
"whitequark/space_args_block.txt"
131+
"whitequark/ruby_bug_9669.txt"
139132
]
140133

141134
Fixture.each_for_version(except: skip_syntax_error, version: "3.3") do |fixture|

0 commit comments

Comments
 (0)