prism 2.0.0.rc1-x86_64-linux-gnu → 2.0.0.rc2-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/src/prism.c CHANGED
@@ -700,17 +700,38 @@ pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size
700
700
  /* Scope-related functions */
701
701
  /******************************************************************************/
702
702
 
703
+ /**
704
+ * The largest locals table that a popped scope hands to the next pushed scope.
705
+ */
706
+ #define PM_LOCALS_REUSE_CAPACITY 16
707
+
703
708
  /**
704
709
  * Allocate and initialize a new scope. Push it onto the scope stack.
705
710
  */
706
- static bool
711
+ static void
707
712
  pm_parser_scope_push(pm_parser_t *parser, bool closed) {
708
- pm_scope_t *scope = (pm_scope_t *) xmalloc(sizeof(pm_scope_t));
709
- if (scope == NULL) return false;
713
+ pm_scope_t *scope = parser->free_scopes;
714
+ pm_locals_t locals = { 0 };
715
+
716
+ if (scope != NULL) {
717
+ parser->free_scopes = scope->previous;
718
+
719
+ /* Reuse the locals table of the popped scope when it is small, so that
720
+ * most scopes allocate no locals storage at all. Large tables are
721
+ * dropped instead, because every scope that reuses a table pays to
722
+ * clear it and to scan its full capacity when the locals are ordered. */
723
+ if (scope->locals.capacity > 0 && scope->locals.capacity <= PM_LOCALS_REUSE_CAPACITY) {
724
+ locals.capacity = scope->locals.capacity;
725
+ locals.locals = scope->locals.locals;
726
+ memset(locals.locals, 0, locals.capacity * sizeof(pm_local_t));
727
+ }
728
+ } else {
729
+ scope = (pm_scope_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_scope_t), PRISM_ALIGNOF(pm_scope_t));
730
+ }
710
731
 
711
732
  *scope = (pm_scope_t) {
712
733
  .previous = parser->current_scope,
713
- .locals = { 0 },
734
+ .locals = locals,
714
735
  .parameters = PM_SCOPE_PARAMETERS_NONE,
715
736
  .implicit_parameters = { 0 },
716
737
  .shareable_constant = parser->current_scope == NULL ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant,
@@ -718,7 +739,6 @@ pm_parser_scope_push(pm_parser_t *parser, bool closed) {
718
739
  };
719
740
 
720
741
  parser->current_scope = scope;
721
- return true;
722
742
  }
723
743
 
724
744
  /**
@@ -876,24 +896,16 @@ pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constan
876
896
  */
877
897
  #define PM_LOCALS_HASH_THRESHOLD 5
878
898
 
879
- static void
880
- pm_locals_free(pm_locals_t *locals) {
881
- if (locals->capacity > 0) {
882
- xfree_sized(locals->locals, locals->capacity * sizeof(pm_local_t));
883
- }
884
- }
885
-
886
899
  /**
887
900
  * Resize the locals list to be twice its current size. If the next capacity is
888
901
  * above the threshold for switching to a hash, then we'll switch to a hash.
889
902
  */
890
903
  static void
891
- pm_locals_resize(pm_locals_t *locals) {
904
+ pm_locals_resize(pm_arena_t *arena, pm_locals_t *locals) {
892
905
  uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2);
893
906
  assert(next_capacity > locals->capacity);
894
907
 
895
- pm_local_t *next_locals = xcalloc(next_capacity, sizeof(pm_local_t));
896
- if (next_locals == NULL) abort();
908
+ pm_local_t *next_locals = (pm_local_t *) pm_arena_zalloc(arena, next_capacity * sizeof(pm_local_t), PRISM_ALIGNOF(pm_local_t));
897
909
 
898
910
  if (next_capacity < PM_LOCALS_HASH_THRESHOLD) {
899
911
  if (locals->size > 0) {
@@ -918,7 +930,6 @@ pm_locals_resize(pm_locals_t *locals) {
918
930
  }
919
931
  }
920
932
 
921
- pm_locals_free(locals);
922
933
  locals->locals = next_locals;
923
934
  locals->capacity = next_capacity;
924
935
  }
@@ -939,9 +950,9 @@ pm_locals_resize(pm_locals_t *locals) {
939
950
  * @return True if the local was added, and false if the local already exists.
940
951
  */
941
952
  static bool
942
- pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint32_t length, uint32_t reads) {
953
+ pm_locals_write(pm_arena_t *arena, pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint32_t length, uint32_t reads) {
943
954
  if (locals->size >= (locals->capacity / 4 * 3)) {
944
- pm_locals_resize(locals);
955
+ pm_locals_resize(arena, locals);
945
956
  }
946
957
 
947
958
  locals->bloom |= (1u << (name & 31));
@@ -7268,7 +7279,7 @@ pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
7268
7279
  */
7269
7280
  static PRISM_INLINE void
7270
7281
  pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7271
- pm_locals_write(&parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
7282
+ pm_locals_write(&parser->metadata_arena, &parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
7272
7283
  }
7273
7284
 
7274
7285
  /**
@@ -7351,8 +7362,8 @@ static void
7351
7362
  pm_parser_scope_pop(pm_parser_t *parser) {
7352
7363
  pm_scope_t *scope = parser->current_scope;
7353
7364
  parser->current_scope = scope->previous;
7354
- pm_locals_free(&scope->locals);
7355
- xfree_sized(scope, sizeof(pm_scope_t));
7365
+ scope->previous = parser->free_scopes;
7366
+ parser->free_scopes = scope;
7356
7367
  }
7357
7368
 
7358
7369
  /******************************************************************************/
@@ -7420,8 +7431,7 @@ pm_do_loop_stack_p(pm_parser_t *parser) {
7420
7431
  * lexed: the do-loop stack (so a `do` inside is not read as a `while`/`until`
7421
7432
  * loop body) and the accepts-block stack (so a `do` inside is accepted as a
7422
7433
  * block rather than binding to an enclosing command). The matching close pops
7423
- * both. This mirrors parse.y's paired `COND_PUSH(0); CMDARG_PUSH(0)`. Keep the
7424
- * following in mind before touching either stack:
7434
+ * both. Keep the following in mind before touching either stack:
7425
7435
  *
7426
7436
  * - The LEXER owns the frame for delimiter-bound stuff. Every token that opens
7427
7437
  * a matched pair calls this on the open and `pm_enclosure_frame_pop` on the
@@ -7429,14 +7439,20 @@ pm_do_loop_stack_p(pm_parser_t *parser) {
7429
7439
  * `parser_lex` and the `#{` case in `lex_interpolation`; the pop sites are
7430
7440
  * the matching `)`, `]`, `}` (including `}` as `EMBEXPR_END`).
7431
7441
  * - The PARSER owns the frame for keyword-bounded blocks, where there is no
7432
- * delimiter token to hang it on: the `do`/`end` forms in `parse_block` and
7433
- * the lambda push `accepts_block` directly (and pop before consuming `end`).
7442
+ * delimiter token to hang it on. The `-> do end` lambda body pushes the full
7443
+ * frame through this helper: the lexer produces the lambda's `do` before
7444
+ * consulting the do-loop stack, so the body can open inside a loop condition
7445
+ * and must reset it. The `do`/`end` forms in `parse_block` push only
7446
+ * `accepts_block`: the lexer produces a block's `do` only when the do-loop
7447
+ * stack is already false, so another false frame would change nothing. Both
7448
+ * pop before consuming `end`.
7434
7449
  * - The `parse_arguments_list` command-args branch juggles ONLY `accepts_block`
7435
7450
  * (never through this helper), because the lexer has pushed a delimiter frame
7436
7451
  * one token early and the command-args frame must be threaded beneath it. Its
7437
7452
  * match lookahead sets must stay in sync with the lexer push sites above.
7438
- * This matches parse.y, whose `command_args` rule juggles CMDARG but not
7439
- * COND.
7453
+ * Command arguments change which call a `do` block binds to, not whether a
7454
+ * `do` closes a loop condition (`while foo bar do ... end` still loops), so
7455
+ * the do-loop stack is left alone.
7440
7456
  */
7441
7457
  static PRISM_INLINE void
7442
7458
  pm_enclosure_frame_push(pm_parser_t *parser) {
@@ -7986,8 +8002,12 @@ PM_STATIC_ASSERT(__LINE__, PM_CONTEXT_MAXIMUM <= 64, "Expected every context to
7986
8002
 
7987
8003
  static bool
7988
8004
  context_push(pm_parser_t *parser, pm_context_t context) {
7989
- pm_context_node_t *context_node = (pm_context_node_t *) xmalloc(sizeof(pm_context_node_t));
7990
- if (context_node == NULL) return false;
8005
+ pm_context_node_t *context_node = parser->free_contexts;
8006
+ if (context_node != NULL) {
8007
+ parser->free_contexts = context_node->prev;
8008
+ } else {
8009
+ context_node = (pm_context_node_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_context_node_t), PRISM_ALIGNOF(pm_context_node_t));
8010
+ }
7991
8011
 
7992
8012
  pm_context_node_t *previous = parser->current_context;
7993
8013
  *context_node = (pm_context_node_t) {
@@ -8002,9 +8022,10 @@ context_push(pm_parser_t *parser, pm_context_t context) {
8002
8022
 
8003
8023
  static void
8004
8024
  context_pop(pm_parser_t *parser) {
8005
- pm_context_node_t *prev = parser->current_context->prev;
8006
- xfree_sized(parser->current_context, sizeof(pm_context_node_t));
8007
- parser->current_context = prev;
8025
+ pm_context_node_t *context_node = parser->current_context;
8026
+ parser->current_context = context_node->prev;
8027
+ context_node->prev = parser->free_contexts;
8028
+ parser->free_contexts = context_node;
8008
8029
  }
8009
8030
 
8010
8031
  static bool
@@ -8508,82 +8529,186 @@ lex_global_variable(pm_parser_t *parser) {
8508
8529
  }
8509
8530
 
8510
8531
  /**
8511
- * This function checks if the current token matches a keyword. If it does, it
8512
- * returns the token type. Otherwise, it returns PM_TOKEN_EOF. The arguments are as follows:
8532
+ * A Ruby keyword, along with the lex state that it transitions to and the
8533
+ * token types that it produces.
8534
+ */
8535
+ typedef struct {
8536
+ /* The keyword itself. */
8537
+ const char *name;
8538
+
8539
+ /* The keyword's first, middle, and last bytes and its length, packed by
8540
+ * PM_KEYWORD_PACK. */
8541
+ uint32_t packed;
8542
+
8543
+ /* The state to transition to when the keyword is lexed. */
8544
+ pm_lex_state_t state;
8545
+
8546
+ /* The token type of the keyword. */
8547
+ pm_token_type_t type;
8548
+
8549
+ /* The token type of the keyword in modifier position, or PM_TOKEN_EOF if
8550
+ * the keyword has no modifier form. */
8551
+ pm_token_type_t modifier_type;
8552
+ } pm_keyword_t;
8553
+
8554
+ /**
8555
+ * The length of the longest keyword, __ENCODING__.
8556
+ */
8557
+ #define PM_KEYWORD_LENGTH_MAXIMUM 12
8558
+
8559
+ /**
8560
+ * Pack the first, middle (at index length / 2), and last bytes of an identifier
8561
+ * together with its length. No two keywords pack to the same value.
8562
+ */
8563
+ #define PM_KEYWORD_PACK(first, middle, last, length) \
8564
+ ((uint32_t) (first) | ((uint32_t) (middle) << 8) | ((uint32_t) (last) << 16) | ((uint32_t) (length) << 24))
8565
+
8566
+ /**
8567
+ * The number of bits in a slot index of pm_keywords, which has
8568
+ * 1 << PM_KEYWORD_SLOT_BITS slots.
8569
+ */
8570
+ #define PM_KEYWORD_SLOT_BITS 6
8571
+
8572
+ /**
8573
+ * Return the slot in pm_keywords for a packed identifier. This is
8574
+ * multiplicative hashing: the product is taken modulo 2^32, and the slot is its
8575
+ * top PM_KEYWORD_SLOT_BITS bits. The top bits are used because the low bits of
8576
+ * a product depend only on the low bits of its operands (here, the low bits of
8577
+ * the first byte), while the top bits depend on all of them.
8513
8578
  *
8514
- * * `parser` - the parser object
8515
- * * `current_start` - pointer to the start of the current token
8516
- * * `value` - the literal string that we're checking for
8517
- * * `vlen` - the length of the token
8518
- * * `state` - the state that we should transition to if the token matches
8519
- * * `type` - the expected token type
8520
- * * `modifier_type` - the expected modifier token type
8521
- */
8522
- static PRISM_INLINE pm_token_type_t
8523
- lex_keyword(pm_parser_t *parser, const uint8_t *current_start, const char *value, size_t vlen, pm_lex_state_t state, pm_token_type_t type, pm_token_type_t modifier_type) {
8524
- if (memcmp(current_start, value, vlen) == 0) {
8525
- pm_lex_state_t last_state = parser->lex_state;
8526
-
8527
- if (parser->lex_state & PM_LEX_STATE_FNAME) {
8528
- lex_state_set(parser, PM_LEX_STATE_ENDFN);
8529
- } else {
8530
- lex_state_set(parser, state);
8531
- if (state == PM_LEX_STATE_BEG) {
8532
- parser->command_start = true;
8533
- }
8579
+ * The multiplier comes from `bin/prism keywords`, which searches for one that
8580
+ * gives every keyword its own slot, making this a perfect hash over the
8581
+ * keywords. Rerun it whenever the set of keywords changes. The table below is
8582
+ * indexed with this same macro, so if two keywords ever shared a slot, the
8583
+ * compiler would reject the duplicate initializer.
8584
+ */
8585
+ #define PM_KEYWORD_SLOT(packed) ((uint32_t) ((uint32_t) (packed) * 0x75f796ddU) >> (32 - PM_KEYWORD_SLOT_BITS))
8586
+
8587
+ /**
8588
+ * Define the entry in pm_keywords for a keyword, given its first, middle, and
8589
+ * last characters.
8590
+ */
8591
+ #define PM_KEYWORD(name, first, middle, last, state, type, modifier_type) \
8592
+ [PM_KEYWORD_SLOT(PM_KEYWORD_PACK(first, middle, last, sizeof(name) - 1))] = { name, PM_KEYWORD_PACK(first, middle, last, sizeof(name) - 1), state, type, modifier_type }
8593
+
8594
+ /**
8595
+ * The keywords, indexed by PM_KEYWORD_SLOT. Unused slots are zeroed, and a
8596
+ * packed value of zero never matches an identifier because its length is at
8597
+ * least one.
8598
+ */
8599
+ static const pm_keyword_t pm_keywords[1 << PM_KEYWORD_SLOT_BITS] = {
8600
+ PM_KEYWORD("do", 'd', 'o', 'o', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF),
8601
+ PM_KEYWORD("if", 'i', 'f', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER),
8602
+ PM_KEYWORD("in", 'i', 'n', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IN, PM_TOKEN_EOF),
8603
+ PM_KEYWORD("or", 'o', 'r', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_OR, PM_TOKEN_EOF),
8604
+ PM_KEYWORD("and", 'a', 'n', 'd', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_AND, PM_TOKEN_EOF),
8605
+ PM_KEYWORD("def", 'd', 'e', 'f', PM_LEX_STATE_FNAME, PM_TOKEN_KEYWORD_DEF, PM_TOKEN_EOF),
8606
+ PM_KEYWORD("end", 'e', 'n', 'd', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END, PM_TOKEN_EOF),
8607
+ PM_KEYWORD("END", 'E', 'N', 'D', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END_UPCASE, PM_TOKEN_EOF),
8608
+ PM_KEYWORD("for", 'f', 'o', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_FOR, PM_TOKEN_EOF),
8609
+ PM_KEYWORD("nil", 'n', 'i', 'l', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_NIL, PM_TOKEN_EOF),
8610
+ PM_KEYWORD("not", 'n', 'o', 't', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_NOT, PM_TOKEN_EOF),
8611
+ PM_KEYWORD("case", 'c', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_CASE, PM_TOKEN_EOF),
8612
+ PM_KEYWORD("else", 'e', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_EOF),
8613
+ PM_KEYWORD("next", 'n', 'x', 't', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_NEXT, PM_TOKEN_EOF),
8614
+ PM_KEYWORD("redo", 'r', 'd', 'o', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_REDO, PM_TOKEN_EOF),
8615
+ PM_KEYWORD("self", 's', 'l', 'f', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_SELF, PM_TOKEN_EOF),
8616
+ PM_KEYWORD("then", 't', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_EOF),
8617
+ PM_KEYWORD("true", 't', 'u', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_TRUE, PM_TOKEN_EOF),
8618
+ PM_KEYWORD("when", 'w', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_EOF),
8619
+ PM_KEYWORD("alias", 'a', 'i', 's', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_ALIAS, PM_TOKEN_EOF),
8620
+ PM_KEYWORD("begin", 'b', 'g', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_BEGIN, PM_TOKEN_EOF),
8621
+ PM_KEYWORD("BEGIN", 'B', 'G', 'N', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_BEGIN_UPCASE, PM_TOKEN_EOF),
8622
+ PM_KEYWORD("break", 'b', 'e', 'k', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_BREAK, PM_TOKEN_EOF),
8623
+ PM_KEYWORD("class", 'c', 'a', 's', PM_LEX_STATE_CLASS, PM_TOKEN_KEYWORD_CLASS, PM_TOKEN_EOF),
8624
+ PM_KEYWORD("elsif", 'e', 's', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_EOF),
8625
+ PM_KEYWORD("false", 'f', 'l', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_FALSE, PM_TOKEN_EOF),
8626
+ PM_KEYWORD("retry", 'r', 't', 'y', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_RETRY, PM_TOKEN_EOF),
8627
+ PM_KEYWORD("super", 's', 'p', 'r', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_SUPER, PM_TOKEN_EOF),
8628
+ PM_KEYWORD("undef", 'u', 'd', 'f', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_UNDEF, PM_TOKEN_EOF),
8629
+ PM_KEYWORD("until", 'u', 't', 'l', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNTIL, PM_TOKEN_KEYWORD_UNTIL_MODIFIER),
8630
+ PM_KEYWORD("while", 'w', 'i', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHILE, PM_TOKEN_KEYWORD_WHILE_MODIFIER),
8631
+ PM_KEYWORD("yield", 'y', 'e', 'd', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_YIELD, PM_TOKEN_EOF),
8632
+ PM_KEYWORD("ensure", 'e', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_EOF),
8633
+ PM_KEYWORD("module", 'm', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_MODULE, PM_TOKEN_EOF),
8634
+ PM_KEYWORD("rescue", 'r', 'c', 'e', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_RESCUE_MODIFIER),
8635
+ PM_KEYWORD("return", 'r', 'u', 'n', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RETURN, PM_TOKEN_EOF),
8636
+ PM_KEYWORD("unless", 'u', 'e', 's', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNLESS, PM_TOKEN_KEYWORD_UNLESS_MODIFIER),
8637
+ PM_KEYWORD("defined?", 'd', 'n', '?', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF),
8638
+ PM_KEYWORD("__LINE__", '_', 'N', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___LINE__, PM_TOKEN_EOF),
8639
+ PM_KEYWORD("__FILE__", '_', 'L', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___FILE__, PM_TOKEN_EOF),
8640
+ PM_KEYWORD("__ENCODING__", '_', 'D', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___ENCODING__, PM_TOKEN_EOF)
8641
+ };
8534
8642
 
8535
- if ((modifier_type != PM_TOKEN_EOF) && !(last_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
8536
- lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
8537
- return modifier_type;
8538
- }
8539
- }
8643
+ #undef PM_KEYWORD
8540
8644
 
8541
- return type;
8542
- }
8645
+ /**
8646
+ * Return the keyword that the identifier corresponds to or NULL if it is not in
8647
+ * the table.
8648
+ */
8649
+ static PRISM_INLINE const pm_keyword_t *
8650
+ pm_keyword_find(const uint8_t *start, const uint8_t *end) {
8651
+ ptrdiff_t width = end - start;
8652
+ if (width < 2 || width > PM_KEYWORD_LENGTH_MAXIMUM) return NULL;
8543
8653
 
8544
- return PM_TOKEN_EOF;
8654
+ size_t uwidth = (size_t) width;
8655
+ uint32_t packed = PM_KEYWORD_PACK(start[0], start[uwidth >> 1], start[uwidth - 1], uwidth);
8656
+
8657
+ const pm_keyword_t *keyword = &pm_keywords[PM_KEYWORD_SLOT(packed)];
8658
+ return (keyword->packed == packed && memcmp(start + 1, keyword->name + 1, uwidth - 2) == 0) ? keyword : NULL;
8659
+ }
8660
+
8661
+ /**
8662
+ * Returns true if the bytes between start and end spell a keyword, using the
8663
+ * same lookup that the lexer does.
8664
+ */
8665
+ bool
8666
+ pm_keyword_p(const uint8_t *start, const uint8_t *end) {
8667
+ return pm_keyword_find(start, end) != NULL;
8545
8668
  }
8546
8669
 
8547
8670
  static pm_token_type_t
8548
8671
  lex_identifier(pm_parser_t *parser, bool previous_command_start) {
8549
- // Lex as far as we can into the current identifier.
8550
- size_t width;
8672
+ /* Lex as far as we can into the current identifier. */
8551
8673
  const uint8_t *end = parser->end;
8552
8674
  const uint8_t *current_start = parser->current.start;
8553
8675
  const uint8_t *current_end = parser->current.end;
8554
8676
  bool encoding_changed = parser->encoding_changed;
8555
8677
 
8556
8678
  if (encoding_changed) {
8679
+ size_t width;
8557
8680
  while ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) {
8558
8681
  current_end += width;
8559
8682
  }
8560
8683
  } else {
8561
- // Fast path: scan ASCII identifier bytes using wide operations.
8684
+ /* Fast path: scan ASCII identifier bytes using wide operations. */
8562
8685
  current_end += scan_identifier_ascii(current_end, end);
8563
8686
 
8564
- // Byte-at-a-time fallback for the tail and any UTF-8 sequences.
8687
+ /* Byte-at-a-time fallback for the tail and any UTF-8 sequences. */
8688
+ size_t width;
8565
8689
  while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) {
8566
8690
  current_end += width;
8567
8691
  }
8568
8692
  }
8693
+
8569
8694
  parser->current.end = current_end;
8570
8695
 
8571
- // Now cache the length of the identifier so that we can quickly compare it
8572
- // against known keywords.
8573
- width = (size_t) (current_end - current_start);
8696
+ /* Whether the identifier ends in ! or ?, which makes it a method name
8697
+ * unless it spells a keyword (defined?). */
8698
+ bool method_name = false;
8574
8699
 
8575
8700
  if (current_end < end) {
8576
8701
  if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
8577
- // First we'll attempt to extend the identifier by a ! or ?. Then we'll
8578
- // check if we're returning the defined? keyword or just an identifier.
8579
- width++;
8702
+ /* Extend the identifier by the ! or ?, so that the keyword lookup
8703
+ * below sees the whole name. */
8704
+ current_end++;
8580
8705
 
8581
8706
  if (
8582
8707
  ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8583
8708
  (peek(parser) == ':') && (peek_offset(parser, 1) != ':')
8584
8709
  ) {
8585
- // If we're in a position where we can accept a : at the end of an
8586
- // identifier, then we'll optionally accept it.
8710
+ /* If we are in a position where we can accept a : at the end of
8711
+ * an identifier, then we will optionally accept it. */
8587
8712
  lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8588
8713
  (void) match(parser, ':');
8589
8714
 
@@ -8593,27 +8718,17 @@ lex_identifier(pm_parser_t *parser, bool previous_command_start) {
8593
8718
  return PM_TOKEN_LABEL;
8594
8719
  }
8595
8720
 
8596
- if (parser->lex_state != PM_LEX_STATE_DOT) {
8597
- if (width == 8 && (lex_keyword(parser, current_start, "defined?", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF) != PM_TOKEN_EOF)) {
8598
- return PM_TOKEN_KEYWORD_DEFINED;
8599
- }
8600
- }
8601
-
8602
- return PM_TOKEN_METHOD_NAME;
8603
- }
8604
-
8605
- if (lex_state_p(parser, PM_LEX_STATE_FNAME) && peek_offset(parser, 1) != '~' && peek_offset(parser, 1) != '>' && (peek_offset(parser, 1) != '=' || peek_offset(parser, 2) == '>') && match(parser, '=')) {
8606
- // If we're in a position where we can accept a = at the end of an
8607
- // identifier, then we'll optionally accept it.
8721
+ method_name = true;
8722
+ } else if (lex_state_p(parser, PM_LEX_STATE_FNAME) && peek_offset(parser, 1) != '~' && peek_offset(parser, 1) != '>' && (peek_offset(parser, 1) != '=' || peek_offset(parser, 2) == '>') && match(parser, '=')) {
8723
+ /* If we are in a position where we can accept a = at the end of an
8724
+ * identifier, then we will optionally accept it. */
8608
8725
  return PM_TOKEN_IDENTIFIER;
8609
- }
8610
-
8611
- if (
8726
+ } else if (
8612
8727
  ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8613
8728
  peek(parser) == ':' && peek_offset(parser, 1) != ':'
8614
8729
  ) {
8615
- // If we're in a position where we can accept a : at the end of an
8616
- // identifier, then we'll optionally accept it.
8730
+ /* If we are in a position where we can accept a : at the end of an
8731
+ * identifier, then we will optionally accept it. */
8617
8732
  lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8618
8733
  (void) match(parser, ':');
8619
8734
 
@@ -8625,106 +8740,66 @@ lex_identifier(pm_parser_t *parser, bool previous_command_start) {
8625
8740
  }
8626
8741
 
8627
8742
  if (parser->lex_state != PM_LEX_STATE_DOT) {
8628
- pm_token_type_t type;
8629
-
8630
- /* The lex state from before lex_keyword transitions it, mirroring the
8631
- * `state = p->lex.state` capture in parse.y's keyword handling. */
8743
+ /* The lex state from before the keyword changes it, which decides
8744
+ * whether the keyword is in its modifier form and whether `do` is a
8745
+ * plain name below. */
8632
8746
  pm_lex_state_t previous_lex_state = parser->lex_state;
8747
+ const pm_keyword_t *keyword = pm_keyword_find(current_start, current_end);
8633
8748
 
8634
- switch (width) {
8635
- case 2:
8636
- if (lex_keyword(parser, current_start, "do", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF) != PM_TOKEN_EOF) {
8637
- /* In FNAME position (a symbol like `:do` or a method name
8638
- * like `def do`), `do` is a plain name rather than a
8639
- * block, loop, or lambda opener, so none of the
8640
- * discrimination below applies. This mirrors parse.y,
8641
- * whose EXPR_FNAME early-return precedes all of the
8642
- * keyword_do special-casing (and never touches
8643
- * lpar_beg). */
8644
- if (previous_lex_state & PM_LEX_STATE_FNAME) {
8645
- return PM_TOKEN_KEYWORD_DO;
8646
- }
8647
- if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
8648
- // At the bare nesting level of a lambda literal (no
8649
- // delimiter opened since `->`), a `do` opens the lambda
8650
- // body. This is a distinct token so that a command in a
8651
- // parameter default cannot consume it as its own block
8652
- // (`-> a = foo do end` is `->(a = foo) do end`). It
8653
- // mirrors CRuby's keyword_do_LAMBDA.
8654
- //
8655
- // Clear the nesting so that no token within the
8656
- // `do`/`end` body is considered to be at the beginning
8657
- // of a lambda; the parser restores the enclosing value
8658
- // once the lambda has been fully parsed. This mirrors
8659
- // parse.y setting `p->lex.lpar_beg = -1` when lexing
8660
- // keyword_do_LAMBDA.
8661
- parser->lambda_enclosure_nesting = -1;
8662
- return PM_TOKEN_KEYWORD_DO_LAMBDA;
8663
- }
8664
- if (pm_do_loop_stack_p(parser)) {
8665
- return PM_TOKEN_KEYWORD_DO_LOOP;
8666
- }
8667
- if (!pm_accepts_block_stack_p(parser)) {
8668
- return PM_TOKEN_KEYWORD_DO_BLOCK;
8669
- }
8749
+ if (keyword != NULL) {
8750
+ if (previous_lex_state & PM_LEX_STATE_FNAME) {
8751
+ lex_state_set(parser, PM_LEX_STATE_ENDFN);
8752
+ } else {
8753
+ lex_state_set(parser, keyword->state);
8754
+ if (keyword->state == PM_LEX_STATE_BEG) {
8755
+ parser->command_start = true;
8756
+ }
8757
+
8758
+ if ((keyword->modifier_type != PM_TOKEN_EOF) && !(previous_lex_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
8759
+ lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
8760
+ return keyword->modifier_type;
8761
+ }
8762
+ }
8763
+
8764
+ if (keyword->type == PM_TOKEN_KEYWORD_DO) {
8765
+ /* In FNAME position (a symbol like `:do` or a method name like
8766
+ * `def do`), `do` is a plain name rather than a block, loop, or
8767
+ * lambda opener, so none of the discrimination below applies
8768
+ * and the lambda nesting is left untouched. */
8769
+ if (previous_lex_state & PM_LEX_STATE_FNAME) {
8670
8770
  return PM_TOKEN_KEYWORD_DO;
8671
8771
  }
8772
+ if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
8773
+ /* At the bare nesting level of a lambda literal (no
8774
+ * delimiter opened since `->`), a `do` opens the lambda
8775
+ * body. This is a distinct token so that a command in a
8776
+ * parameter default cannot consume it as its own block
8777
+ * (`-> a = foo do end` is `->(a = foo) do end`).
8778
+ *
8779
+ * Clear the nesting so that no token within the `do`/`end`
8780
+ * body is considered to be at the beginning of a lambda;
8781
+ * the parser restores the enclosing value once the lambda
8782
+ * has been fully parsed. */
8783
+ parser->lambda_enclosure_nesting = -1;
8784
+ return PM_TOKEN_KEYWORD_DO_LAMBDA;
8785
+ }
8786
+ if (pm_do_loop_stack_p(parser)) {
8787
+ return PM_TOKEN_KEYWORD_DO_LOOP;
8788
+ }
8789
+ if (!pm_accepts_block_stack_p(parser)) {
8790
+ return PM_TOKEN_KEYWORD_DO_BLOCK;
8791
+ }
8792
+ return PM_TOKEN_KEYWORD_DO;
8793
+ }
8672
8794
 
8673
- if ((type = lex_keyword(parser, current_start, "if", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER)) != PM_TOKEN_EOF) return type;
8674
- if ((type = lex_keyword(parser, current_start, "in", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8675
- if ((type = lex_keyword(parser, current_start, "or", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_OR, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8676
- break;
8677
- case 3:
8678
- if ((type = lex_keyword(parser, current_start, "and", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_AND, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8679
- if ((type = lex_keyword(parser, current_start, "def", width, PM_LEX_STATE_FNAME, PM_TOKEN_KEYWORD_DEF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8680
- if ((type = lex_keyword(parser, current_start, "end", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8681
- if ((type = lex_keyword(parser, current_start, "END", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END_UPCASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8682
- if ((type = lex_keyword(parser, current_start, "for", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_FOR, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8683
- if ((type = lex_keyword(parser, current_start, "nil", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_NIL, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8684
- if ((type = lex_keyword(parser, current_start, "not", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_NOT, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8685
- break;
8686
- case 4:
8687
- if ((type = lex_keyword(parser, current_start, "case", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_CASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8688
- if ((type = lex_keyword(parser, current_start, "else", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8689
- if ((type = lex_keyword(parser, current_start, "next", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_NEXT, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8690
- if ((type = lex_keyword(parser, current_start, "redo", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_REDO, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8691
- if ((type = lex_keyword(parser, current_start, "self", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_SELF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8692
- if ((type = lex_keyword(parser, current_start, "then", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8693
- if ((type = lex_keyword(parser, current_start, "true", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_TRUE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8694
- if ((type = lex_keyword(parser, current_start, "when", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8695
- break;
8696
- case 5:
8697
- if ((type = lex_keyword(parser, current_start, "alias", width, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_ALIAS, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8698
- if ((type = lex_keyword(parser, current_start, "begin", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_BEGIN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8699
- if ((type = lex_keyword(parser, current_start, "BEGIN", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_BEGIN_UPCASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8700
- if ((type = lex_keyword(parser, current_start, "break", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_BREAK, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8701
- if ((type = lex_keyword(parser, current_start, "class", width, PM_LEX_STATE_CLASS, PM_TOKEN_KEYWORD_CLASS, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8702
- if ((type = lex_keyword(parser, current_start, "elsif", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8703
- if ((type = lex_keyword(parser, current_start, "false", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_FALSE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8704
- if ((type = lex_keyword(parser, current_start, "retry", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_RETRY, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8705
- if ((type = lex_keyword(parser, current_start, "super", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_SUPER, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8706
- if ((type = lex_keyword(parser, current_start, "undef", width, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_UNDEF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8707
- if ((type = lex_keyword(parser, current_start, "until", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNTIL, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) != PM_TOKEN_EOF) return type;
8708
- if ((type = lex_keyword(parser, current_start, "while", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHILE, PM_TOKEN_KEYWORD_WHILE_MODIFIER)) != PM_TOKEN_EOF) return type;
8709
- if ((type = lex_keyword(parser, current_start, "yield", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_YIELD, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8710
- break;
8711
- case 6:
8712
- if ((type = lex_keyword(parser, current_start, "ensure", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8713
- if ((type = lex_keyword(parser, current_start, "module", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_MODULE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8714
- if ((type = lex_keyword(parser, current_start, "rescue", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) != PM_TOKEN_EOF) return type;
8715
- if ((type = lex_keyword(parser, current_start, "return", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RETURN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8716
- if ((type = lex_keyword(parser, current_start, "unless", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNLESS, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) != PM_TOKEN_EOF) return type;
8717
- break;
8718
- case 8:
8719
- if ((type = lex_keyword(parser, current_start, "__LINE__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___LINE__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8720
- if ((type = lex_keyword(parser, current_start, "__FILE__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___FILE__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8721
- break;
8722
- case 12:
8723
- if ((type = lex_keyword(parser, current_start, "__ENCODING__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___ENCODING__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8724
- break;
8795
+ return keyword->type;
8725
8796
  }
8726
8797
  }
8727
8798
 
8799
+ if (method_name) {
8800
+ return PM_TOKEN_METHOD_NAME;
8801
+ }
8802
+
8728
8803
  if (encoding_changed) {
8729
8804
  return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8730
8805
  }
@@ -12952,6 +13027,19 @@ expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t di
12952
13027
  */
12953
13028
  #define PM_PARSE_ACCEPTS_STATEMENT ((uint8_t) 0x10)
12954
13029
 
13030
+ /**
13031
+ * Indicates that an endless method definition may have a command call as its
13032
+ * body, as in `def f = foo 1`. Such a definition is a command write, accepted
13033
+ * as a statement (including the value of a statement-level rescue modifier)
13034
+ * and as the value of a single-target write. Since Ruby 4.0 it is also
13035
+ * accepted as the sole argument of a call, where other command writes such as
13036
+ * `x = foo 1` are not. Expression positions such as conditional predicates and
13037
+ * the operands of `not`, `!`, `and`, and `or` accept a command call but not a
13038
+ * command write, so this is set alongside PM_PARSE_ACCEPTS_COMMAND_CALL only
13039
+ * where both are accepted.
13040
+ */
13041
+ #define PM_PARSE_ACCEPTS_COMMAND_DEF ((uint8_t) 0x20)
13042
+
12955
13043
  static pm_node_t *
12956
13044
  parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
12957
13045
 
@@ -13067,6 +13155,23 @@ token_begins_pattern_p(pm_token_type_t type) {
13067
13155
  );
13068
13156
  }
13069
13157
 
13158
+ /**
13159
+ * Returns true if the given token can begin an argument in an unparenthesized
13160
+ * argument list. This is the set of tokens that can begin an expression plus
13161
+ * the prefix operators that only appear in argument position — `&` (block
13162
+ * arguments), `*` (splats), and `**` (keyword splats) — which are excluded
13163
+ * from token_begins_expression_p.
13164
+ */
13165
+ static PRISM_INLINE bool
13166
+ token_begins_argument_p(pm_token_type_t type) {
13167
+ return (
13168
+ token_begins_expression_p(type) ||
13169
+ type == PM_TOKEN_UAMPERSAND ||
13170
+ type == PM_TOKEN_USTAR ||
13171
+ type == PM_TOKEN_USTAR_STAR
13172
+ );
13173
+ }
13174
+
13070
13175
  /**
13071
13176
  * Parse an expression with the given binding power that may be optionally
13072
13177
  * prefixed by the * operator.
@@ -13708,7 +13813,7 @@ parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
13708
13813
  context_push(parser, context);
13709
13814
 
13710
13815
  while (true) {
13711
- pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
13816
+ pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
13712
13817
  pm_statements_node_body_append(parser, statements, node, true);
13713
13818
 
13714
13819
  // If we're recovering from a syntax error, then we need to stop parsing
@@ -14135,6 +14240,27 @@ pm_command_call_value_p(const pm_parser_t *parser, const pm_node_t *node) {
14135
14240
  }
14136
14241
  }
14137
14242
 
14243
+ /**
14244
+ * Returns true if the given node is a command that can end before a comma
14245
+ * without consuming it: a constant-path command with a brace block
14246
+ * (`Foo::Bar { }`), or an endless method whose command body ends in a rescue
14247
+ * modifier value (`def f = foo 1 rescue z`). Other commands take a following
14248
+ * comma into their own arguments. A command can be neither one of several
14249
+ * arguments nor one of several values of a write, so a comma after one of
14250
+ * these is an error.
14251
+ */
14252
+ static bool
14253
+ pm_command_before_comma_p(const pm_parser_t *parser, const pm_node_t *node) {
14254
+ switch (PM_NODE_TYPE(node)) {
14255
+ case PM_CALL_NODE:
14256
+ return pm_constant_path_command_call_p(parser, (const pm_call_node_t *) node);
14257
+ case PM_DEF_NODE:
14258
+ return pm_command_call_value_p(parser, node);
14259
+ default:
14260
+ return false;
14261
+ }
14262
+ }
14263
+
14138
14264
  /**
14139
14265
  * Returns true if the given node is a block call: a command
14140
14266
  * with a do-block, or any call chained (via `.`, `::`, `&.`) from such a node.
@@ -14321,7 +14447,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
14321
14447
  PRISM_FALLTHROUGH
14322
14448
  default: {
14323
14449
  if (argument == NULL) {
14324
- argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (!parsed_first_argument ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0u) | PM_PARSE_ACCEPTS_LABEL), PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14450
+ /* The first argument may be a command call, and since Ruby
14451
+ * 4.0 also an endless method with a command body
14452
+ * (`private def f = foo 1`), either of which must then be
14453
+ * the sole argument. */
14454
+ uint8_t command_flags = 0;
14455
+ if (!parsed_first_argument) {
14456
+ command_flags = PM_PARSE_ACCEPTS_COMMAND_CALL;
14457
+ if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
14458
+ }
14459
+
14460
+ argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | command_flags | PM_PARSE_ACCEPTS_LABEL), PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14325
14461
  }
14326
14462
 
14327
14463
  bool contains_keywords = false;
@@ -14418,6 +14554,12 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
14418
14554
  break;
14419
14555
  }
14420
14556
  }
14557
+
14558
+ /* A command can only be the sole argument, as in
14559
+ * `foo Foo::Bar { }, y` or `foo def f = bar 1 rescue z, y`. */
14560
+ if (pm_command_before_comma_p(parser, argument)) {
14561
+ pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14562
+ }
14421
14563
  } else {
14422
14564
  // If there is no comma at the end of the argument list then we're
14423
14565
  // done parsing arguments and can break out of this loop.
@@ -15628,7 +15770,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_a
15628
15770
 
15629
15771
  arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15630
15772
  }
15631
- } else if ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
15773
+ } else if ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
15632
15774
  found |= true;
15633
15775
  parsed_command_args = true;
15634
15776
 
@@ -18556,7 +18698,7 @@ parse_class(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18556
18698
  * Parse a method definition expression (the `def` keyword).
18557
18699
  */
18558
18700
  static pm_node_t *
18559
- parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
18701
+ parse_def(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18560
18702
  pm_node_list_t current_block_exits = { 0 };
18561
18703
  pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18562
18704
 
@@ -18806,14 +18948,9 @@ parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags,
18806
18948
  pm_do_loop_stack_push(parser, false);
18807
18949
  statements = UP(pm_statements_node_create(parser));
18808
18950
 
18809
- uint8_t allow_flags;
18810
- if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
18811
- allow_flags = flags & PM_PARSE_ACCEPTS_COMMAND_CALL;
18812
- } else {
18813
- /* Allow `def foo = puts "Hello"` but not
18814
- * `private def foo = puts "Hello"` */
18815
- allow_flags = (binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
18816
- }
18951
+ /* The body accepts a command call (`def f = foo 1`) only where the
18952
+ * caller accepts an endless method with a command body. */
18953
+ uint8_t allow_flags = (flags & PM_PARSE_ACCEPTS_COMMAND_DEF) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
18817
18954
 
18818
18955
  /* Inside a def body, we push true onto the accepts_block_stack so that
18819
18956
  * `do` is lexed as PM_TOKEN_KEYWORD_DO (which can only start a block
@@ -18858,7 +18995,7 @@ parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags,
18858
18995
  pm_accepts_block_stack_pop(parser);
18859
18996
 
18860
18997
  /* A nested endless def whose body is a command call (e.g.,
18861
- * `def f = def g = foo bar`) is a command assignment and cannot appear
18998
+ * `def f = def g = foo bar`) is a command write and cannot appear
18862
18999
  * as a def body. */
18863
19000
  if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(parser, statement)) {
18864
19001
  PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
@@ -19041,7 +19178,7 @@ parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
19041
19178
  /* Otherwise, we're going to parse the first statement in the list of
19042
19179
  * statements within the parentheses. */
19043
19180
  context_push(parser, PM_CONTEXT_PARENS);
19044
- pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19181
+ pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19045
19182
  context_pop(parser);
19046
19183
 
19047
19184
  /* Determine if this statement is followed by a terminator. In the case of a
@@ -19154,7 +19291,7 @@ parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
19154
19291
 
19155
19292
  /* Parse each statement within the parentheses. */
19156
19293
  while (true) {
19157
- pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19294
+ pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19158
19295
  pm_statements_node_body_append(parser, statements, node, true);
19159
19296
 
19160
19297
  /* If we're recovering from a syntax error, then we need to stop parsing
@@ -19670,17 +19807,17 @@ parse_lambda(pm_parser_t *parser, uint16_t depth) {
19670
19807
  /* Restore the enclosing lambda's nesting now that the body has been
19671
19808
  * parsed, so that the token following the closing `}` is lexed in the
19672
19809
  * enclosing context. During the body the nesting held this lambda's own
19673
- * level, which every token inside the braces sits above. This mirrors
19674
- * parse.y restoring `p->lex.lpar_beg` after `lambda_body`. */
19810
+ * level, which every token inside the braces sits above. */
19675
19811
  parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
19676
19812
  expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
19677
19813
  } else {
19678
- /* A `-> { }` body is delimited by `{`/`}`, whose block-accepting frame
19679
- * the lexer manages. A `-> do end` body is delimited by keywords, so
19680
- * push the frame here and pop it before `end`. The push must precede
19681
- * consuming the `do`, which lexes the first token of the body; this
19682
- * matches parse.y's CMDARG_PUSH(0) before `lambda_body`. */
19683
- pm_accepts_block_stack_push(parser, true);
19814
+ /* A `-> { }` body is delimited by `{`/`}`, whose enclosure frame the
19815
+ * lexer manages. A `-> do end` body is delimited by keywords, so push
19816
+ * the frame here and pop it before `end`. The frame isolates the body
19817
+ * from an enclosing `while`/`until`/`for` condition, so a `do` inside
19818
+ * it opens a block rather than the loop body. The push must precede
19819
+ * consuming the `do`, which lexes the first token of the body. */
19820
+ pm_enclosure_frame_push(parser);
19684
19821
  expect1(parser, PM_TOKEN_KEYWORD_DO_LAMBDA, PM_ERR_LAMBDA_OPEN);
19685
19822
  opening = parser->previous;
19686
19823
 
@@ -19701,7 +19838,7 @@ parse_lambda(pm_parser_t *parser, uint16_t depth) {
19701
19838
  parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
19702
19839
  }
19703
19840
 
19704
- pm_accepts_block_stack_pop(parser);
19841
+ pm_enclosure_frame_pop(parser);
19705
19842
 
19706
19843
  /* As with the brace branch above, restore the nesting before consuming
19707
19844
  * the closing `end`, which lexes the token that follows it. */
@@ -20040,7 +20177,7 @@ parse_identifier(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
20040
20177
  // can still be a method call if it is followed by arguments or
20041
20178
  // a block, so we need to check for that here.
20042
20179
  if (
20043
- ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) ||
20180
+ ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
20044
20181
  (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
20045
20182
  match1(parser, PM_TOKEN_BRACE_LEFT)
20046
20183
  ) {
@@ -20497,7 +20634,14 @@ parse_jump(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20497
20634
 
20498
20635
  if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
20499
20636
  pm_token_t next = parser->current;
20637
+
20638
+ /* The arguments of a jump start a fresh command context, so a
20639
+ * `do` inside them binds to the nearest call within the
20640
+ * arguments rather than to an enclosing command call, as in
20641
+ * `check 1, case x when 1 then return bar do end end`. */
20642
+ pm_accepts_block_stack_push(parser, true);
20500
20643
  parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
20644
+ pm_accepts_block_stack_pop(parser);
20501
20645
 
20502
20646
  // Reject `foo && return bar`.
20503
20647
  if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) {
@@ -20590,7 +20734,16 @@ parse_not(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags,
20590
20734
  }
20591
20735
  }
20592
20736
  } else {
20593
- receiver = parse_expression(parser, PM_BINDING_POWER_NOT, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1));
20737
+ /* The operand of `not` may be a command call, except at the start of
20738
+ * an endless method body, where it may be one only if the body may
20739
+ * be: `def f = not foo 1` has a command body, so it is rejected where
20740
+ * that is not accepted (`if def f = not foo 1; end`). A nested `not`
20741
+ * is still at the start of the body, so the operand carries the
20742
+ * endless method context on (`def f = not not foo 1`). */
20743
+ uint8_t operand_flags = (flags & PM_PARSE_IN_ENDLESS_DEF)
20744
+ ? (uint8_t) (flags & (PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_IN_ENDLESS_DEF))
20745
+ : PM_PARSE_ACCEPTS_COMMAND_CALL;
20746
+ receiver = parse_expression(parser, PM_BINDING_POWER_NOT, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | operand_flags), PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1));
20594
20747
  }
20595
20748
 
20596
20749
  return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
@@ -20736,7 +20889,7 @@ parse_constant(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t fl
20736
20889
  * fact a method call, not a constant read. */
20737
20890
  if (
20738
20891
  match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
20739
- ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) ||
20892
+ ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
20740
20893
  (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
20741
20894
  match1(parser, PM_TOKEN_BRACE_LEFT)
20742
20895
  ) {
@@ -20996,7 +21149,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, u
20996
21149
  case PM_TOKEN_KEYWORD_CLASS:
20997
21150
  return parse_class(parser, flags, depth);
20998
21151
  case PM_TOKEN_KEYWORD_DEF:
20999
- return parse_def(parser, binding_power, flags, depth);
21152
+ return parse_def(parser, flags, depth);
21000
21153
  case PM_TOKEN_KEYWORD_DEFINED:
21001
21154
  return UP(parse_defined(parser, flags, depth));
21002
21155
  case PM_TOKEN_KEYWORD_END_UPCASE:
@@ -21138,6 +21291,18 @@ parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t de
21138
21291
  }
21139
21292
  }
21140
21293
 
21294
+ /**
21295
+ * A command is a complete value of a write: it cannot begin a multi-value
21296
+ * right-hand side (`x = Foo::Bar { }, y`), nor stand as the first element of
21297
+ * one when nested in another write (`x = y = def f = foo 1 rescue z, w`).
21298
+ */
21299
+ static void
21300
+ parse_write_value_comma_check(pm_parser_t *parser, const pm_node_t *value) {
21301
+ if (match1(parser, PM_TOKEN_COMMA) && pm_command_before_comma_p(parser, value)) {
21302
+ pm_parser_err_current(parser, PM_ERR_INVALID_COMMA);
21303
+ }
21304
+ }
21305
+
21141
21306
  /**
21142
21307
  * Parse a value that is going to be written to some kind of variable or method
21143
21308
  * call. We need to handle this separately because the rescue modifier is
@@ -21149,7 +21314,21 @@ parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t de
21149
21314
  */
21150
21315
  static pm_node_t *
21151
21316
  parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
21152
- pm_node_t *value = parse_value_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) : (previous_binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0))), diag_id, (uint16_t) (depth + 1));
21317
+ /* Where an operator write accepts a command call it also accepts an
21318
+ * endless method with a command body (`x += def f = foo 1`). */
21319
+ uint8_t command_flags = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
21320
+ ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21321
+ : (previous_binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21322
+ if (command_flags) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
21323
+
21324
+ pm_node_t *value = parse_value_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | command_flags), diag_id, (uint16_t) (depth + 1));
21325
+
21326
+ /* An operator write never takes a multi-value right-hand side, so a comma
21327
+ * after a statement-level one is already reported by the statement that
21328
+ * contains it. */
21329
+ if (previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
21330
+ parse_write_value_comma_check(parser, value);
21331
+ }
21153
21332
 
21154
21333
  // Assignments whose value is a command call (e.g., a = b c) can only
21155
21334
  // be followed by modifiers (if/unless/while/until/rescue) and not by
@@ -21253,12 +21432,21 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding
21253
21432
  ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21254
21433
  : ((previous_binding_power < PM_BINDING_POWER_MODIFIER || statement_level) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21255
21434
 
21435
+ /* Where a single-target write accepts a command call it also accepts an
21436
+ * endless method with a command body (`x = def f = foo 1`), but a
21437
+ * multi-write accepts only the command call. */
21438
+ if (command_call_flag && binding_power != PM_BINDING_POWER_MULTI_ASSIGNMENT + 1) {
21439
+ command_call_flag |= PM_PARSE_ACCEPTS_COMMAND_DEF;
21440
+ }
21441
+
21256
21442
  pm_node_t *value = parse_starred_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | command_call_flag), diag_id, (uint16_t) (depth + 1));
21257
21443
  if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
21258
21444
 
21259
21445
  parse_assignment_value_local(parser, value);
21260
21446
  bool single_value = true;
21261
21447
 
21448
+ parse_write_value_comma_check(parser, value);
21449
+
21262
21450
  // Block calls (command call + do block, e.g., `foo bar do end`) cannot
21263
21451
  // be followed by a comma to form a multi-value RHS because each element
21264
21452
  // of a multi-value assignment must be an `arg`, not a `block_call`.
@@ -21360,7 +21548,7 @@ parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement,
21360
21548
  // splat right-hand side (`b = c, d` / `b = *c`); a comma _before_ an
21361
21549
  // `=`, or a parenthesized target list (`(b, c), d = 1`), instead
21362
21550
  // promotes to a multiple assignment target list below.
21363
- value = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, flags | PM_PARSE_ACCEPTS_STATEMENT, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21551
+ value = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, flags | PM_PARSE_ACCEPTS_STATEMENT | PM_PARSE_ACCEPTS_COMMAND_DEF, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21364
21552
  multiple = match1(parser, PM_TOKEN_COMMA) || PM_NODE_TYPE_P(value, PM_MULTI_TARGET_NODE);
21365
21553
  }
21366
21554
 
@@ -21735,7 +21923,7 @@ parse_constant_path_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power
21735
21923
 
21736
21924
  if (
21737
21925
  (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
21738
- ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)))
21926
+ ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type))
21739
21927
  ) {
21740
21928
  /* If we have a constant immediately following a '::' operator,
21741
21929
  * then this can either be a constant path or a method call,
@@ -22709,7 +22897,7 @@ parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) {
22709
22897
  return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22710
22898
  case PM_DEF_NODE:
22711
22899
  // An endless method whose body is a command-style call (e.g.,
22712
- // `def f = foo bar`) is a command assignment and can only be
22900
+ // `def f = foo bar`) is a command write and can only be
22713
22901
  // followed by modifiers.
22714
22902
  return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(parser, node);
22715
22903
  case PM_RESCUE_MODIFIER_NODE:
@@ -22864,7 +23052,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
22864
23052
  cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
22865
23053
  )
22866
23054
  ) {
22867
- flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
23055
+ flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
22868
23056
  }
22869
23057
  break;
22870
23058
  }
@@ -22872,7 +23060,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
22872
23060
  case PM_CONSTANT_PATH_NODE:
22873
23061
  break;
22874
23062
  default:
22875
- flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
23063
+ flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
22876
23064
  break;
22877
23065
  }
22878
23066
  }
@@ -23381,14 +23569,6 @@ pm_parser_cleanup(pm_parser_t *parser) {
23381
23569
  pm_string_cleanup(&parser->filepath);
23382
23570
  pm_arena_cleanup(&parser->metadata_arena);
23383
23571
 
23384
- while (parser->current_scope != NULL) {
23385
- // Normally, popping the scope doesn't free the locals since it is
23386
- // assumed that ownership has transferred to the AST. However if we have
23387
- // scopes while we're freeing the parser, it's likely they came from
23388
- // eval scopes and we need to free them explicitly here.
23389
- pm_parser_scope_pop(parser);
23390
- }
23391
-
23392
23572
  while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
23393
23573
  lex_mode_pop(parser);
23394
23574
  }
@@ -23728,6 +23908,7 @@ pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t s
23728
23908
  pm_serialize_header(buffer);
23729
23909
  pm_serialize_encoding(parser.encoding, buffer);
23730
23910
  pm_buffer_append_varsint(buffer, parser.start_line);
23911
+ pm_buffer_append_uint64(buffer, pm_parser_checksum(&parser));
23731
23912
  pm_serialize_line_offset_list(&parser.line_offsets, buffer);
23732
23913
  pm_serialize_comment_list(&parser.comment_list, buffer);
23733
23914