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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/LICENSE.md +1 -1
- data/docs/serialization.md +1 -0
- data/ext/prism/api_node.c +1 -1
- data/ext/prism/extension.c +20 -2
- data/ext/prism/extension.h +1 -1
- data/include/prism/compiler/accel.h +3 -1
- data/include/prism/compiler/bswap.h +46 -0
- data/include/prism/compiler/endianness.h +34 -0
- data/include/prism/internal/buffer.h +3 -0
- data/include/prism/internal/parser.h +20 -0
- data/include/prism/internal/xxh3.h +16 -0
- data/include/prism/parser.h +21 -0
- data/include/prism/string_query.h +11 -0
- data/include/prism/version.h +1 -1
- data/lib/prism/3.3/prism.so +0 -0
- data/lib/prism/3.4/prism.so +0 -0
- data/lib/prism/4.0/prism.so +0 -0
- data/lib/prism/ffi.rb +6 -0
- data/lib/prism/node.rb +736 -0
- data/lib/prism/parse_result.rb +23 -8
- data/lib/prism/serialize.rb +19 -6
- data/lib/prism/string_query.rb +8 -0
- data/prism.gemspec +8 -4
- data/rbi/generated/prism/node.rbi +1706 -980
- data/rbi/generated/prism/parse_result.rbi +16 -4
- data/rbi/generated/prism/serialize.rbi +3 -0
- data/rbi/generated/prism/string_query.rbi +7 -0
- data/sig/generated/prism/node.rbs +1713 -977
- data/sig/generated/prism/parse_result.rbs +16 -4
- data/sig/generated/prism/serialize.rbs +3 -0
- data/sig/generated/prism/string_query.rbs +7 -0
- data/src/buffer.c +9 -0
- data/src/parser.c +18 -0
- data/src/prism.c +406 -225
- data/src/serialize.c +2 -1
- data/src/string_query.c +14 -0
- data/src/xxh3.c +292 -0
- metadata +7 -3
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
|
|
711
|
+
static void
|
|
707
712
|
pm_parser_scope_push(pm_parser_t *parser, bool closed) {
|
|
708
|
-
pm_scope_t *scope =
|
|
709
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
7355
|
-
|
|
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.
|
|
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
|
|
7433
|
-
*
|
|
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
|
-
*
|
|
7439
|
-
*
|
|
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 =
|
|
7990
|
-
if (context_node
|
|
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 *
|
|
8006
|
-
|
|
8007
|
-
|
|
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
|
-
*
|
|
8512
|
-
*
|
|
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
|
-
*
|
|
8515
|
-
*
|
|
8516
|
-
*
|
|
8517
|
-
*
|
|
8518
|
-
*
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8684
|
+
/* Fast path: scan ASCII identifier bytes using wide operations. */
|
|
8562
8685
|
current_end += scan_identifier_ascii(current_end, end);
|
|
8563
8686
|
|
|
8564
|
-
|
|
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
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
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
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
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
|
-
|
|
8586
|
-
|
|
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
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
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
|
-
|
|
8616
|
-
|
|
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
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
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
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) && (
|
|
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,
|
|
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, ¤t_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
|
-
|
|
18810
|
-
|
|
18811
|
-
|
|
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
|
|
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.
|
|
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
|
|
19679
|
-
*
|
|
19680
|
-
*
|
|
19681
|
-
*
|
|
19682
|
-
*
|
|
19683
|
-
|
|
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
|
-
|
|
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) && (
|
|
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
|
-
|
|
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) && (
|
|
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,
|
|
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
|
-
|
|
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) && (
|
|
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
|
|
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
|
|