prism 2.0.0.rc1-arm-linux-gnu → 2.1.0-arm-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 +61 -41
- data/CODE_OF_CONDUCT.md +1 -1
- data/LICENSE.md +1 -1
- data/Makefile +8 -8
- data/README.md +3 -1
- data/docs/fuzzing.md +5 -5
- data/docs/serialization.md +20 -6
- data/ext/prism/api_node.c +81 -81
- data/ext/prism/extension.c +54 -69
- data/ext/prism/extension.h +1 -1
- data/include/prism/arena.h +8 -0
- data/include/prism/arena.hpp +42 -0
- data/include/prism/ast.h +11 -1
- data/include/prism/ast.hpp +641 -0
- data/include/prism/buffer.h +8 -0
- data/include/prism/buffer.hpp +55 -0
- data/include/prism/comments.h +17 -0
- data/include/prism/compiler/accel.h +3 -1
- data/include/prism/compiler/branch.h +26 -0
- data/include/prism/compiler/bswap.h +46 -0
- data/include/prism/compiler/cplusplus.hpp +16 -0
- data/include/prism/compiler/endianness.h +34 -0
- data/include/prism/compiler/exceptions.hpp +17 -0
- data/include/prism/compiler/format.h +2 -0
- data/include/prism/compiler/lifetimebound.hpp +25 -0
- data/include/prism/constant_pool.h +25 -0
- data/include/prism/diagnostic.h +18 -0
- data/include/prism/errors_format.h +8 -0
- data/include/prism/internal/buffer.h +3 -0
- data/include/prism/internal/comments.h +2 -1
- data/include/prism/internal/node.h +8 -4
- data/include/prism/internal/parser.h +44 -13
- data/include/prism/internal/static_literals.h +24 -64
- data/include/prism/internal/stringy.h +1 -6
- data/include/prism/internal/xxh3.h +16 -0
- data/include/prism/json.h +8 -0
- data/include/prism/line_offset_list.h +8 -0
- data/include/prism/magic_comments.h +18 -0
- data/include/prism/node.h +55 -10
- data/include/prism/node.hpp +272 -0
- data/include/prism/node_type.hpp +38 -0
- data/include/prism/options.h +10 -1
- data/include/prism/options.hpp +43 -0
- data/include/prism/parser.h +93 -64
- data/include/prism/parser.hpp +274 -0
- data/include/prism/prettyprint.h +8 -0
- data/include/prism/serialize.h +8 -0
- data/include/prism/source.h +8 -0
- data/include/prism/stream.h +8 -0
- data/include/prism/string_query.h +19 -0
- data/include/prism/stringy.h +16 -27
- data/include/prism/stringy.hpp +31 -0
- data/include/prism/version.h +10 -2
- data/include/prism.h +0 -8
- data/include/prism.hpp +24 -0
- 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 +2568 -3287
- data/lib/prism/string_query.rb +8 -0
- data/prism.gemspec +21 -4
- data/rbi/generated/prism/node.rbi +2430 -968
- data/rbi/generated/prism/parse_result.rbi +16 -4
- data/rbi/generated/prism/serialize.rbi +8 -4
- data/rbi/generated/prism/string_query.rbi +7 -0
- data/sig/generated/prism/node.rbs +2439 -967
- data/sig/generated/prism/parse_result.rbs +16 -4
- data/sig/generated/prism/serialize.rbs +9 -4
- data/sig/generated/prism/string_query.rbs +7 -0
- data/src/arena.c +6 -1
- data/src/buffer.c +9 -0
- data/src/comments.c +11 -10
- data/src/diagnostic.c +24 -4
- data/src/integer.c +6 -1
- data/src/node.c +216 -368
- data/src/options.c +7 -13
- data/src/parser.c +70 -48
- data/src/prism.c +625 -462
- data/src/regexp.c +0 -1
- data/src/serialize.c +2301 -1950
- data/src/source.c +8 -2
- data/src/static_literals.c +280 -405
- data/src/string_query.c +14 -0
- data/src/stringy.c +3 -33
- data/src/xxh3.c +292 -0
- metadata +20 -3
data/src/prism.c
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "prism/compiler/accel.h"
|
|
2
|
+
#include "prism/compiler/branch.h"
|
|
2
3
|
#include "prism/compiler/fallthrough.h"
|
|
3
4
|
#include "prism/compiler/unused.h"
|
|
4
5
|
|
|
@@ -68,24 +69,6 @@
|
|
|
68
69
|
# define PM_STATIC_ASSERT(line, condition, message) typedef char PM_CONCATENATE(static_assert_, line)[(condition) ? 1 : -1]
|
|
69
70
|
#endif
|
|
70
71
|
|
|
71
|
-
/**
|
|
72
|
-
* Support PRISM_LIKELY and PRISM_UNLIKELY to help the compiler optimize its
|
|
73
|
-
* branch predication.
|
|
74
|
-
*/
|
|
75
|
-
#if defined(__GNUC__) || defined(__clang__)
|
|
76
|
-
/** The compiler should predicate that this branch will be taken. */
|
|
77
|
-
#define PRISM_LIKELY(x) __builtin_expect(!!(x), 1)
|
|
78
|
-
|
|
79
|
-
/** The compiler should predicate that this branch will not be taken. */
|
|
80
|
-
#define PRISM_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
|
81
|
-
#else
|
|
82
|
-
/** Void because this platform does not support branch prediction hints. */
|
|
83
|
-
#define PRISM_LIKELY(x) (x)
|
|
84
|
-
|
|
85
|
-
/** Void because this platform does not support branch prediction hints. */
|
|
86
|
-
#define PRISM_UNLIKELY(x) (x)
|
|
87
|
-
#endif
|
|
88
|
-
|
|
89
72
|
/**
|
|
90
73
|
* The prism version and the serialization format.
|
|
91
74
|
*/
|
|
@@ -201,27 +184,22 @@ lex_mode_terminator(const uint8_t start) {
|
|
|
201
184
|
}
|
|
202
185
|
|
|
203
186
|
/**
|
|
204
|
-
* Push a new lex state onto the stack
|
|
205
|
-
*
|
|
206
|
-
* allocate a new pointer and use that.
|
|
187
|
+
* Push a new lex state onto the stack, reusing a popped lex mode if there is
|
|
188
|
+
* one and allocating a new one otherwise.
|
|
207
189
|
*/
|
|
208
190
|
static void
|
|
209
191
|
lex_mode_push(pm_parser_t *parser, pm_lex_mode_t lex_mode) {
|
|
210
|
-
|
|
211
|
-
parser->lex_modes.index++;
|
|
192
|
+
pm_lex_mode_t *next = parser->lex_modes.free;
|
|
212
193
|
|
|
213
|
-
if (
|
|
214
|
-
parser->lex_modes.
|
|
215
|
-
if (PRISM_UNLIKELY(parser->lex_modes.current == NULL)) {
|
|
216
|
-
fprintf(stderr, "prism: out of memory; aborting\n");
|
|
217
|
-
abort();
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
*parser->lex_modes.current = lex_mode;
|
|
194
|
+
if (next != NULL) {
|
|
195
|
+
parser->lex_modes.free = next->prev;
|
|
221
196
|
} else {
|
|
222
|
-
parser->
|
|
223
|
-
parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index];
|
|
197
|
+
next = (pm_lex_mode_t *) pm_arena_alloc(&parser->scratch_arena, sizeof(pm_lex_mode_t), PRISM_ALIGNOF(pm_lex_mode_t));
|
|
224
198
|
}
|
|
199
|
+
|
|
200
|
+
lex_mode.prev = parser->lex_modes.current;
|
|
201
|
+
*next = lex_mode;
|
|
202
|
+
parser->lex_modes.current = next;
|
|
225
203
|
}
|
|
226
204
|
|
|
227
205
|
/**
|
|
@@ -374,22 +352,20 @@ lex_mode_push_string_eof(pm_parser_t *parser) {
|
|
|
374
352
|
}
|
|
375
353
|
|
|
376
354
|
/**
|
|
377
|
-
* Pop the current lex state off the stack
|
|
378
|
-
*
|
|
379
|
-
*
|
|
355
|
+
* Pop the current lex state off the stack, keeping it for reuse by the next
|
|
356
|
+
* push. The base lex mode is never popped, so popping it instead resets it to
|
|
357
|
+
* the default mode.
|
|
380
358
|
*/
|
|
381
359
|
static void
|
|
382
360
|
lex_mode_pop(pm_parser_t *parser) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index];
|
|
361
|
+
pm_lex_mode_t *popped = parser->lex_modes.current;
|
|
362
|
+
|
|
363
|
+
if (popped == &parser->lex_modes.base) {
|
|
364
|
+
popped->mode = PM_LEX_DEFAULT;
|
|
388
365
|
} else {
|
|
389
|
-
parser->lex_modes.
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
parser->lex_modes.current = prev;
|
|
366
|
+
parser->lex_modes.current = popped->prev;
|
|
367
|
+
popped->prev = parser->lex_modes.free;
|
|
368
|
+
parser->lex_modes.free = popped;
|
|
393
369
|
}
|
|
394
370
|
}
|
|
395
371
|
|
|
@@ -700,17 +676,38 @@ pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size
|
|
|
700
676
|
/* Scope-related functions */
|
|
701
677
|
/******************************************************************************/
|
|
702
678
|
|
|
679
|
+
/**
|
|
680
|
+
* The largest locals table that a popped scope hands to the next pushed scope.
|
|
681
|
+
*/
|
|
682
|
+
#define PM_LOCALS_REUSE_CAPACITY 16
|
|
683
|
+
|
|
703
684
|
/**
|
|
704
685
|
* Allocate and initialize a new scope. Push it onto the scope stack.
|
|
705
686
|
*/
|
|
706
|
-
static
|
|
687
|
+
static void
|
|
707
688
|
pm_parser_scope_push(pm_parser_t *parser, bool closed) {
|
|
708
|
-
pm_scope_t *scope =
|
|
709
|
-
|
|
689
|
+
pm_scope_t *scope = parser->free_scopes;
|
|
690
|
+
pm_locals_t locals = { 0 };
|
|
691
|
+
|
|
692
|
+
if (scope != NULL) {
|
|
693
|
+
parser->free_scopes = scope->previous;
|
|
694
|
+
|
|
695
|
+
/* Reuse the locals table of the popped scope when it is small, so that
|
|
696
|
+
* most scopes allocate no locals storage at all. Large tables are
|
|
697
|
+
* dropped instead, because every scope that reuses a table pays to
|
|
698
|
+
* clear it and to scan its full capacity when the locals are ordered. */
|
|
699
|
+
if (scope->locals.capacity > 0 && scope->locals.capacity <= PM_LOCALS_REUSE_CAPACITY) {
|
|
700
|
+
locals.capacity = scope->locals.capacity;
|
|
701
|
+
locals.locals = scope->locals.locals;
|
|
702
|
+
memset(locals.locals, 0, locals.capacity * sizeof(pm_local_t));
|
|
703
|
+
}
|
|
704
|
+
} else {
|
|
705
|
+
scope = (pm_scope_t *) pm_arena_alloc(&parser->scratch_arena, sizeof(pm_scope_t), PRISM_ALIGNOF(pm_scope_t));
|
|
706
|
+
}
|
|
710
707
|
|
|
711
708
|
*scope = (pm_scope_t) {
|
|
712
709
|
.previous = parser->current_scope,
|
|
713
|
-
.locals =
|
|
710
|
+
.locals = locals,
|
|
714
711
|
.parameters = PM_SCOPE_PARAMETERS_NONE,
|
|
715
712
|
.implicit_parameters = { 0 },
|
|
716
713
|
.shareable_constant = parser->current_scope == NULL ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant,
|
|
@@ -718,7 +715,6 @@ pm_parser_scope_push(pm_parser_t *parser, bool closed) {
|
|
|
718
715
|
};
|
|
719
716
|
|
|
720
717
|
parser->current_scope = scope;
|
|
721
|
-
return true;
|
|
722
718
|
}
|
|
723
719
|
|
|
724
720
|
/**
|
|
@@ -876,24 +872,16 @@ pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constan
|
|
|
876
872
|
*/
|
|
877
873
|
#define PM_LOCALS_HASH_THRESHOLD 5
|
|
878
874
|
|
|
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
875
|
/**
|
|
887
876
|
* Resize the locals list to be twice its current size. If the next capacity is
|
|
888
877
|
* above the threshold for switching to a hash, then we'll switch to a hash.
|
|
889
878
|
*/
|
|
890
879
|
static void
|
|
891
|
-
pm_locals_resize(pm_locals_t *locals) {
|
|
880
|
+
pm_locals_resize(pm_arena_t *arena, pm_locals_t *locals) {
|
|
892
881
|
uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2);
|
|
893
882
|
assert(next_capacity > locals->capacity);
|
|
894
883
|
|
|
895
|
-
pm_local_t *next_locals =
|
|
896
|
-
if (next_locals == NULL) abort();
|
|
884
|
+
pm_local_t *next_locals = (pm_local_t *) pm_arena_zalloc(arena, next_capacity * sizeof(pm_local_t), PRISM_ALIGNOF(pm_local_t));
|
|
897
885
|
|
|
898
886
|
if (next_capacity < PM_LOCALS_HASH_THRESHOLD) {
|
|
899
887
|
if (locals->size > 0) {
|
|
@@ -918,7 +906,6 @@ pm_locals_resize(pm_locals_t *locals) {
|
|
|
918
906
|
}
|
|
919
907
|
}
|
|
920
908
|
|
|
921
|
-
pm_locals_free(locals);
|
|
922
909
|
locals->locals = next_locals;
|
|
923
910
|
locals->capacity = next_capacity;
|
|
924
911
|
}
|
|
@@ -939,9 +926,9 @@ pm_locals_resize(pm_locals_t *locals) {
|
|
|
939
926
|
* @return True if the local was added, and false if the local already exists.
|
|
940
927
|
*/
|
|
941
928
|
static bool
|
|
942
|
-
pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint32_t length, uint32_t reads) {
|
|
929
|
+
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
930
|
if (locals->size >= (locals->capacity / 4 * 3)) {
|
|
944
|
-
pm_locals_resize(locals);
|
|
931
|
+
pm_locals_resize(arena, locals);
|
|
945
932
|
}
|
|
946
933
|
|
|
947
934
|
locals->bloom |= (1u << (name & 31));
|
|
@@ -2441,7 +2428,7 @@ pm_arguments_node_arguments_append(pm_arena_t *arena, pm_arguments_node_t *node,
|
|
|
2441
2428
|
PM_NODE_LENGTH_SET_NODE(node, argument);
|
|
2442
2429
|
}
|
|
2443
2430
|
|
|
2444
|
-
|
|
2431
|
+
pm_node_list_append_inline(arena, &node->arguments, argument);
|
|
2445
2432
|
|
|
2446
2433
|
if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) {
|
|
2447
2434
|
if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
|
|
@@ -2489,7 +2476,7 @@ pm_array_node_elements_append(pm_arena_t *arena, pm_array_node_t *node, pm_node_
|
|
|
2489
2476
|
PM_NODE_START_SET_NODE(node, element);
|
|
2490
2477
|
}
|
|
2491
2478
|
|
|
2492
|
-
|
|
2479
|
+
pm_node_list_append_inline(arena, &node->elements, element);
|
|
2493
2480
|
PM_NODE_LENGTH_SET_NODE(node, element);
|
|
2494
2481
|
|
|
2495
2482
|
// If the element is not a static literal, then the array is not a static
|
|
@@ -2542,9 +2529,9 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node
|
|
|
2542
2529
|
node->rest = child;
|
|
2543
2530
|
found_rest = true;
|
|
2544
2531
|
} else if (found_rest) {
|
|
2545
|
-
|
|
2532
|
+
pm_node_list_append_inline(parser->arena, &node->posts, child);
|
|
2546
2533
|
} else {
|
|
2547
|
-
|
|
2534
|
+
pm_node_list_append_inline(parser->arena, &node->requireds, child);
|
|
2548
2535
|
}
|
|
2549
2536
|
}
|
|
2550
2537
|
|
|
@@ -2612,7 +2599,7 @@ pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *openin
|
|
|
2612
2599
|
|
|
2613
2600
|
static PRISM_INLINE void
|
|
2614
2601
|
pm_array_pattern_node_requireds_append(pm_arena_t *arena, pm_array_pattern_node_t *node, pm_node_t *inner) {
|
|
2615
|
-
|
|
2602
|
+
pm_node_list_append_inline(arena, &node->requireds, inner);
|
|
2616
2603
|
}
|
|
2617
2604
|
|
|
2618
2605
|
/**
|
|
@@ -2876,7 +2863,7 @@ pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name)
|
|
|
2876
2863
|
*/
|
|
2877
2864
|
static void
|
|
2878
2865
|
pm_block_parameters_node_append_local(pm_arena_t *arena, pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) {
|
|
2879
|
-
|
|
2866
|
+
pm_node_list_append_inline(arena, &node->locals, UP(local));
|
|
2880
2867
|
|
|
2881
2868
|
if (PM_NODE_LENGTH(node) == 0) {
|
|
2882
2869
|
PM_NODE_START_SET_NODE(node, local);
|
|
@@ -3529,7 +3516,7 @@ static void
|
|
|
3529
3516
|
pm_case_node_condition_append(pm_arena_t *arena, pm_case_node_t *node, pm_node_t *condition) {
|
|
3530
3517
|
assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
|
|
3531
3518
|
|
|
3532
|
-
|
|
3519
|
+
pm_node_list_append_inline(arena, &node->conditions, condition);
|
|
3533
3520
|
PM_NODE_LENGTH_SET_NODE(node, condition);
|
|
3534
3521
|
}
|
|
3535
3522
|
|
|
@@ -3576,7 +3563,7 @@ static void
|
|
|
3576
3563
|
pm_case_match_node_condition_append(pm_arena_t *arena, pm_case_match_node_t *node, pm_node_t *condition) {
|
|
3577
3564
|
assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
|
|
3578
3565
|
|
|
3579
|
-
|
|
3566
|
+
pm_node_list_append_inline(arena, &node->conditions, condition);
|
|
3580
3567
|
PM_NODE_LENGTH_SET_NODE(node, condition);
|
|
3581
3568
|
}
|
|
3582
3569
|
|
|
@@ -4116,7 +4103,7 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) {
|
|
|
4116
4103
|
// much more efficient, as we could instead resize the node list to only point
|
|
4117
4104
|
// to 1...-1.
|
|
4118
4105
|
for (size_t index = 1; index < nodes->size - 1; index++) {
|
|
4119
|
-
|
|
4106
|
+
pm_node_list_append_inline(parser->arena, &node->requireds, nodes->nodes[index]);
|
|
4120
4107
|
}
|
|
4121
4108
|
|
|
4122
4109
|
return node;
|
|
@@ -4613,7 +4600,7 @@ pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) {
|
|
|
4613
4600
|
*/
|
|
4614
4601
|
static PRISM_INLINE void
|
|
4615
4602
|
pm_hash_node_elements_append(pm_arena_t *arena, pm_hash_node_t *hash, pm_node_t *element) {
|
|
4616
|
-
|
|
4603
|
+
pm_node_list_append_inline(arena, &hash->elements, element);
|
|
4617
4604
|
|
|
4618
4605
|
bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE);
|
|
4619
4606
|
if (static_literal) {
|
|
@@ -5038,7 +5025,7 @@ pm_interpolated_node_append(pm_arena_t *arena, pm_node_t *node, pm_node_list_t *
|
|
|
5038
5025
|
break;
|
|
5039
5026
|
}
|
|
5040
5027
|
|
|
5041
|
-
|
|
5028
|
+
pm_node_list_append_inline(arena, parts, part);
|
|
5042
5029
|
}
|
|
5043
5030
|
|
|
5044
5031
|
/**
|
|
@@ -5180,7 +5167,7 @@ pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_n
|
|
|
5180
5167
|
// These will only happen in error cases. But we want to handle it
|
|
5181
5168
|
// here so that we don't fail the assertion.
|
|
5182
5169
|
CLEAR_FLAGS(node);
|
|
5183
|
-
|
|
5170
|
+
pm_node_list_append_inline(arena, &node->parts, UP(pm_error_recovery_node_create_unexpected(parser, part)));
|
|
5184
5171
|
return;
|
|
5185
5172
|
case PM_ERROR_RECOVERY_NODE:
|
|
5186
5173
|
CLEAR_FLAGS(node);
|
|
@@ -5190,7 +5177,7 @@ pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_n
|
|
|
5190
5177
|
break;
|
|
5191
5178
|
}
|
|
5192
5179
|
|
|
5193
|
-
|
|
5180
|
+
pm_node_list_append_inline(arena, &node->parts, part);
|
|
5194
5181
|
|
|
5195
5182
|
#undef CLEAR_FLAGS
|
|
5196
5183
|
#undef MUTABLE_FLAGS
|
|
@@ -5370,7 +5357,7 @@ pm_keyword_hash_node_elements_append(pm_arena_t *arena, pm_keyword_hash_node_t *
|
|
|
5370
5357
|
pm_node_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS);
|
|
5371
5358
|
}
|
|
5372
5359
|
|
|
5373
|
-
|
|
5360
|
+
pm_node_list_append_inline(arena, &hash->elements, element);
|
|
5374
5361
|
if (PM_NODE_LENGTH(hash) == 0) {
|
|
5375
5362
|
PM_NODE_START_SET_NODE(hash, element);
|
|
5376
5363
|
}
|
|
@@ -5718,19 +5705,19 @@ pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t
|
|
|
5718
5705
|
node->rest = target;
|
|
5719
5706
|
} else {
|
|
5720
5707
|
pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
|
|
5721
|
-
|
|
5708
|
+
pm_node_list_append_inline(parser->arena, &node->rights, target);
|
|
5722
5709
|
}
|
|
5723
5710
|
} else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
|
|
5724
5711
|
if (node->rest == NULL) {
|
|
5725
5712
|
node->rest = target;
|
|
5726
5713
|
} else {
|
|
5727
5714
|
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
|
|
5728
|
-
|
|
5715
|
+
pm_node_list_append_inline(parser->arena, &node->rights, target);
|
|
5729
5716
|
}
|
|
5730
5717
|
} else if (node->rest == NULL) {
|
|
5731
|
-
|
|
5718
|
+
pm_node_list_append_inline(parser->arena, &node->lefts, target);
|
|
5732
5719
|
} else {
|
|
5733
|
-
|
|
5720
|
+
pm_node_list_append_inline(parser->arena, &node->rights, target);
|
|
5734
5721
|
}
|
|
5735
5722
|
|
|
5736
5723
|
if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_START(node) > PM_NODE_START(target))) {
|
|
@@ -6005,7 +5992,7 @@ pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param)
|
|
|
6005
5992
|
static void
|
|
6006
5993
|
pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
|
|
6007
5994
|
pm_parameters_node_location_set(params, param);
|
|
6008
|
-
|
|
5995
|
+
pm_node_list_append_inline(arena, ¶ms->requireds, param);
|
|
6009
5996
|
}
|
|
6010
5997
|
|
|
6011
5998
|
/**
|
|
@@ -6014,7 +6001,7 @@ pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *par
|
|
|
6014
6001
|
static void
|
|
6015
6002
|
pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_optional_parameter_node_t *param) {
|
|
6016
6003
|
pm_parameters_node_location_set(params, UP(param));
|
|
6017
|
-
|
|
6004
|
+
pm_node_list_append_inline(arena, ¶ms->optionals, UP(param));
|
|
6018
6005
|
}
|
|
6019
6006
|
|
|
6020
6007
|
/**
|
|
@@ -6023,7 +6010,7 @@ pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *par
|
|
|
6023
6010
|
static void
|
|
6024
6011
|
pm_parameters_node_posts_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
|
|
6025
6012
|
pm_parameters_node_location_set(params, param);
|
|
6026
|
-
|
|
6013
|
+
pm_node_list_append_inline(arena, ¶ms->posts, param);
|
|
6027
6014
|
}
|
|
6028
6015
|
|
|
6029
6016
|
/**
|
|
@@ -6041,7 +6028,7 @@ pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
|
|
|
6041
6028
|
static void
|
|
6042
6029
|
pm_parameters_node_keywords_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
|
|
6043
6030
|
pm_parameters_node_location_set(params, param);
|
|
6044
|
-
|
|
6031
|
+
pm_node_list_append_inline(arena, ¶ms->keywords, param);
|
|
6045
6032
|
}
|
|
6046
6033
|
|
|
6047
6034
|
/**
|
|
@@ -6329,7 +6316,7 @@ pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subseque
|
|
|
6329
6316
|
*/
|
|
6330
6317
|
static void
|
|
6331
6318
|
pm_rescue_node_exceptions_append(pm_arena_t *arena, pm_rescue_node_t *node, pm_node_t *exception) {
|
|
6332
|
-
|
|
6319
|
+
pm_node_list_append_inline(arena, &node->exceptions, exception);
|
|
6333
6320
|
PM_NODE_LENGTH_SET_NODE(node, exception);
|
|
6334
6321
|
}
|
|
6335
6322
|
|
|
@@ -6562,7 +6549,7 @@ pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node,
|
|
|
6562
6549
|
}
|
|
6563
6550
|
}
|
|
6564
6551
|
|
|
6565
|
-
|
|
6552
|
+
pm_node_list_append_inline(parser->arena, &node->body, statement);
|
|
6566
6553
|
if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
|
|
6567
6554
|
}
|
|
6568
6555
|
|
|
@@ -6809,7 +6796,7 @@ pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
|
|
|
6809
6796
|
((pm_string_t) { 0 })
|
|
6810
6797
|
);
|
|
6811
6798
|
|
|
6812
|
-
|
|
6799
|
+
pm_string_init(&node->unescaped, (const uint8_t *) content, strlen(content));
|
|
6813
6800
|
return node;
|
|
6814
6801
|
}
|
|
6815
6802
|
|
|
@@ -6952,7 +6939,7 @@ pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
|
|
|
6952
6939
|
static void
|
|
6953
6940
|
pm_undef_node_append(pm_arena_t *arena, pm_undef_node_t *node, pm_node_t *name) {
|
|
6954
6941
|
PM_NODE_LENGTH_SET_NODE(node, name);
|
|
6955
|
-
|
|
6942
|
+
pm_node_list_append_inline(arena, &node->names, name);
|
|
6956
6943
|
}
|
|
6957
6944
|
|
|
6958
6945
|
/**
|
|
@@ -7094,7 +7081,7 @@ pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
|
|
|
7094
7081
|
static void
|
|
7095
7082
|
pm_when_node_conditions_append(pm_arena_t *arena, pm_when_node_t *node, pm_node_t *condition) {
|
|
7096
7083
|
PM_NODE_LENGTH_SET_NODE(node, condition);
|
|
7097
|
-
|
|
7084
|
+
pm_node_list_append_inline(arena, &node->conditions, condition);
|
|
7098
7085
|
}
|
|
7099
7086
|
|
|
7100
7087
|
/**
|
|
@@ -7268,7 +7255,7 @@ pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
|
|
|
7268
7255
|
*/
|
|
7269
7256
|
static PRISM_INLINE void
|
|
7270
7257
|
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);
|
|
7258
|
+
pm_locals_write(&parser->scratch_arena, &parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
|
|
7272
7259
|
}
|
|
7273
7260
|
|
|
7274
7261
|
/**
|
|
@@ -7351,8 +7338,8 @@ static void
|
|
|
7351
7338
|
pm_parser_scope_pop(pm_parser_t *parser) {
|
|
7352
7339
|
pm_scope_t *scope = parser->current_scope;
|
|
7353
7340
|
parser->current_scope = scope->previous;
|
|
7354
|
-
|
|
7355
|
-
|
|
7341
|
+
scope->previous = parser->free_scopes;
|
|
7342
|
+
parser->free_scopes = scope;
|
|
7356
7343
|
}
|
|
7357
7344
|
|
|
7358
7345
|
/******************************************************************************/
|
|
@@ -7420,8 +7407,7 @@ pm_do_loop_stack_p(pm_parser_t *parser) {
|
|
|
7420
7407
|
* lexed: the do-loop stack (so a `do` inside is not read as a `while`/`until`
|
|
7421
7408
|
* loop body) and the accepts-block stack (so a `do` inside is accepted as a
|
|
7422
7409
|
* block rather than binding to an enclosing command). The matching close pops
|
|
7423
|
-
* both.
|
|
7424
|
-
* following in mind before touching either stack:
|
|
7410
|
+
* both. Keep the following in mind before touching either stack:
|
|
7425
7411
|
*
|
|
7426
7412
|
* - The LEXER owns the frame for delimiter-bound stuff. Every token that opens
|
|
7427
7413
|
* a matched pair calls this on the open and `pm_enclosure_frame_pop` on the
|
|
@@ -7429,14 +7415,20 @@ pm_do_loop_stack_p(pm_parser_t *parser) {
|
|
|
7429
7415
|
* `parser_lex` and the `#{` case in `lex_interpolation`; the pop sites are
|
|
7430
7416
|
* the matching `)`, `]`, `}` (including `}` as `EMBEXPR_END`).
|
|
7431
7417
|
* - The PARSER owns the frame for keyword-bounded blocks, where there is no
|
|
7432
|
-
* delimiter token to hang it on
|
|
7433
|
-
*
|
|
7418
|
+
* delimiter token to hang it on. The `-> do end` lambda body pushes the full
|
|
7419
|
+
* frame through this helper: the lexer produces the lambda's `do` before
|
|
7420
|
+
* consulting the do-loop stack, so the body can open inside a loop condition
|
|
7421
|
+
* and must reset it. The `do`/`end` forms in `parse_block` push only
|
|
7422
|
+
* `accepts_block`: the lexer produces a block's `do` only when the do-loop
|
|
7423
|
+
* stack is already false, so another false frame would change nothing. Both
|
|
7424
|
+
* pop before consuming `end`.
|
|
7434
7425
|
* - The `parse_arguments_list` command-args branch juggles ONLY `accepts_block`
|
|
7435
7426
|
* (never through this helper), because the lexer has pushed a delimiter frame
|
|
7436
7427
|
* one token early and the command-args frame must be threaded beneath it. Its
|
|
7437
7428
|
* match lookahead sets must stay in sync with the lexer push sites above.
|
|
7438
|
-
*
|
|
7439
|
-
*
|
|
7429
|
+
* Command arguments change which call a `do` block binds to, not whether a
|
|
7430
|
+
* `do` closes a loop condition (`while foo bar do ... end` still loops), so
|
|
7431
|
+
* the do-loop stack is left alone.
|
|
7440
7432
|
*/
|
|
7441
7433
|
static PRISM_INLINE void
|
|
7442
7434
|
pm_enclosure_frame_push(pm_parser_t *parser) {
|
|
@@ -7676,6 +7668,27 @@ parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor
|
|
|
7676
7668
|
return NULL;
|
|
7677
7669
|
}
|
|
7678
7670
|
|
|
7671
|
+
/**
|
|
7672
|
+
* Returns true if the given magic comment key matches the given name, which is
|
|
7673
|
+
* written in lowercase with underscores and is the same length as the key.
|
|
7674
|
+
* Keys match case-insensitively, and a dash in the key matches an underscore in
|
|
7675
|
+
* the name. Every byte of a name is ASCII, so a byte of a multibyte character
|
|
7676
|
+
* in the key never matches one.
|
|
7677
|
+
*/
|
|
7678
|
+
static bool
|
|
7679
|
+
parser_magic_comment_key_p(const uint8_t *key, size_t key_length, const char *name) {
|
|
7680
|
+
assert(name[key_length] == '\0');
|
|
7681
|
+
|
|
7682
|
+
for (size_t index = 0; index < key_length; index++) {
|
|
7683
|
+
uint8_t byte = key[index];
|
|
7684
|
+
if (byte == '-') byte = '_';
|
|
7685
|
+
if ('A' <= byte && byte <= 'Z') byte |= 0x20;
|
|
7686
|
+
if (byte != (uint8_t) name[index]) return false;
|
|
7687
|
+
}
|
|
7688
|
+
|
|
7689
|
+
return true;
|
|
7690
|
+
}
|
|
7691
|
+
|
|
7679
7692
|
/**
|
|
7680
7693
|
* Parse the current token on the parser to see if it's a magic comment and
|
|
7681
7694
|
* potentially perform some action based on that. A regular expression that this
|
|
@@ -7767,73 +7780,34 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
|
|
|
7767
7780
|
if (cursor != end) return false;
|
|
7768
7781
|
}
|
|
7769
7782
|
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
pm_string_t key;
|
|
7783
|
+
/* Finally, we can start checking the key against the list of known
|
|
7784
|
+
* magic comment keys, and potentially change state based on that. */
|
|
7773
7785
|
const size_t key_length = (size_t) (key_end - key_start);
|
|
7774
|
-
const uint8_t *dash = pm_memchr(key_start, '-', key_length, parser->encoding_changed, parser->encoding);
|
|
7775
|
-
|
|
7776
|
-
if (dash == NULL) {
|
|
7777
|
-
pm_string_shared_init(&key, key_start, key_end);
|
|
7778
|
-
} else {
|
|
7779
|
-
uint8_t *buffer = xmalloc(key_length);
|
|
7780
|
-
if (buffer == NULL) break;
|
|
7781
|
-
|
|
7782
|
-
memcpy(buffer, key_start, key_length);
|
|
7783
|
-
buffer[dash - key_start] = '_';
|
|
7784
|
-
|
|
7785
|
-
while ((dash = pm_memchr(dash + 1, '-', (size_t) (key_end - dash - 1), parser->encoding_changed, parser->encoding)) != NULL) {
|
|
7786
|
-
buffer[dash - key_start] = '_';
|
|
7787
|
-
}
|
|
7788
|
-
|
|
7789
|
-
pm_string_owned_init(&key, buffer, key_length);
|
|
7790
|
-
}
|
|
7791
|
-
|
|
7792
|
-
// Finally, we can start checking the key against the list of known
|
|
7793
|
-
// magic comment keys, and potentially change state based on that.
|
|
7794
|
-
const uint8_t *key_source = pm_string_source(&key);
|
|
7795
7786
|
uint32_t value_length = (uint32_t) (value_end - value_start);
|
|
7796
7787
|
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
(
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
}
|
|
7807
|
-
|
|
7808
|
-
if (key_length == 11) {
|
|
7809
|
-
if (pm_strncasecmp(key_source, (const uint8_t *) "warn_indent", 11) == 0) {
|
|
7810
|
-
switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
|
|
7811
|
-
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
|
|
7812
|
-
PM_PARSER_WARN_TOKEN_FORMAT(
|
|
7813
|
-
parser,
|
|
7814
|
-
&parser->current,
|
|
7815
|
-
PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
|
|
7816
|
-
(int) key_length,
|
|
7817
|
-
(const char *) key_source,
|
|
7818
|
-
(int) value_length,
|
|
7819
|
-
(const char *) value_start
|
|
7820
|
-
);
|
|
7821
|
-
break;
|
|
7822
|
-
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
|
|
7823
|
-
parser->warn_mismatched_indentation = false;
|
|
7824
|
-
break;
|
|
7825
|
-
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
|
|
7826
|
-
parser->warn_mismatched_indentation = true;
|
|
7827
|
-
break;
|
|
7788
|
+
switch (key_length) {
|
|
7789
|
+
case 6:
|
|
7790
|
+
/* We only want to attempt to compare against encoding comments
|
|
7791
|
+
* if it is the first line in the file (or the second in the
|
|
7792
|
+
* case of a shebang). */
|
|
7793
|
+
if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
|
|
7794
|
+
if (parser_magic_comment_key_p(key_start, key_length, "coding")) {
|
|
7795
|
+
result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
|
|
7796
|
+
}
|
|
7828
7797
|
}
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
if (
|
|
7835
|
-
|
|
7836
|
-
|
|
7798
|
+
break;
|
|
7799
|
+
case 8:
|
|
7800
|
+
/* We only want to attempt to compare against encoding comments
|
|
7801
|
+
* if it is the first line in the file (or the second in the
|
|
7802
|
+
* case of a shebang). */
|
|
7803
|
+
if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
|
|
7804
|
+
if (parser_magic_comment_key_p(key_start, key_length, "encoding")) {
|
|
7805
|
+
result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
|
|
7806
|
+
}
|
|
7807
|
+
}
|
|
7808
|
+
break;
|
|
7809
|
+
case 11:
|
|
7810
|
+
if (parser_magic_comment_key_p(key_start, key_length, "warn_indent")) {
|
|
7837
7811
|
switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
|
|
7838
7812
|
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
|
|
7839
7813
|
PM_PARSER_WARN_TOKEN_FORMAT(
|
|
@@ -7841,53 +7815,79 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
|
|
|
7841
7815
|
&parser->current,
|
|
7842
7816
|
PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
|
|
7843
7817
|
(int) key_length,
|
|
7844
|
-
|
|
7818
|
+
"warn_indent",
|
|
7845
7819
|
(int) value_length,
|
|
7846
7820
|
(const char *) value_start
|
|
7847
7821
|
);
|
|
7848
7822
|
break;
|
|
7849
7823
|
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
|
|
7850
|
-
parser->
|
|
7824
|
+
parser->warn_mismatched_indentation = false;
|
|
7851
7825
|
break;
|
|
7852
7826
|
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
|
|
7853
|
-
parser->
|
|
7827
|
+
parser->warn_mismatched_indentation = true;
|
|
7854
7828
|
break;
|
|
7855
7829
|
}
|
|
7856
7830
|
}
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7831
|
+
break;
|
|
7832
|
+
case 21:
|
|
7833
|
+
if (parser_magic_comment_key_p(key_start, key_length, "frozen_string_literal")) {
|
|
7834
|
+
/* We only want to handle frozen string literal comments if
|
|
7835
|
+
* it is before any semantic tokens have been seen. */
|
|
7836
|
+
if (semantic_token_seen) {
|
|
7837
|
+
pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
|
|
7838
|
+
} else {
|
|
7839
|
+
switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
|
|
7840
|
+
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
|
|
7841
|
+
PM_PARSER_WARN_TOKEN_FORMAT(
|
|
7842
|
+
parser,
|
|
7843
|
+
&parser->current,
|
|
7844
|
+
PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
|
|
7845
|
+
(int) key_length,
|
|
7846
|
+
"frozen_string_literal",
|
|
7847
|
+
(int) value_length,
|
|
7848
|
+
(const char *) value_start
|
|
7849
|
+
);
|
|
7850
|
+
break;
|
|
7851
|
+
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
|
|
7852
|
+
parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
|
|
7853
|
+
break;
|
|
7854
|
+
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
|
|
7855
|
+
parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
|
|
7856
|
+
break;
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7883
7859
|
}
|
|
7884
|
-
|
|
7860
|
+
break;
|
|
7861
|
+
case 24:
|
|
7862
|
+
if (parser_magic_comment_key_p(key_start, key_length, "shareable_constant_value")) {
|
|
7863
|
+
const uint8_t *cursor = parser->current.start;
|
|
7864
|
+
while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
|
|
7865
|
+
|
|
7866
|
+
if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
|
|
7867
|
+
pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
|
|
7868
|
+
} else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
|
|
7869
|
+
pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
|
|
7870
|
+
} else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
|
|
7871
|
+
pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
|
|
7872
|
+
} else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
|
|
7873
|
+
pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
|
|
7874
|
+
} else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
|
|
7875
|
+
pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
|
|
7876
|
+
} else {
|
|
7877
|
+
PM_PARSER_WARN_TOKEN_FORMAT(
|
|
7878
|
+
parser,
|
|
7879
|
+
&parser->current,
|
|
7880
|
+
PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
|
|
7881
|
+
(int) key_length,
|
|
7882
|
+
"shareable_constant_value",
|
|
7883
|
+
(int) value_length,
|
|
7884
|
+
(const char *) value_start
|
|
7885
|
+
);
|
|
7886
|
+
}
|
|
7887
|
+
}
|
|
7888
|
+
break;
|
|
7885
7889
|
}
|
|
7886
7890
|
|
|
7887
|
-
// When we're done, we want to free the string in case we had to
|
|
7888
|
-
// allocate memory for it.
|
|
7889
|
-
pm_string_cleanup(&key);
|
|
7890
|
-
|
|
7891
7891
|
// Allocate a new magic comment node to append to the parser's list.
|
|
7892
7892
|
pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_magic_comment_t), PRISM_ALIGNOF(pm_magic_comment_t));
|
|
7893
7893
|
magic_comment->node.next = NULL;
|
|
@@ -7986,8 +7986,12 @@ PM_STATIC_ASSERT(__LINE__, PM_CONTEXT_MAXIMUM <= 64, "Expected every context to
|
|
|
7986
7986
|
|
|
7987
7987
|
static bool
|
|
7988
7988
|
context_push(pm_parser_t *parser, pm_context_t context) {
|
|
7989
|
-
pm_context_node_t *context_node =
|
|
7990
|
-
if (context_node
|
|
7989
|
+
pm_context_node_t *context_node = parser->free_contexts;
|
|
7990
|
+
if (context_node != NULL) {
|
|
7991
|
+
parser->free_contexts = context_node->prev;
|
|
7992
|
+
} else {
|
|
7993
|
+
context_node = (pm_context_node_t *) pm_arena_alloc(&parser->scratch_arena, sizeof(pm_context_node_t), PRISM_ALIGNOF(pm_context_node_t));
|
|
7994
|
+
}
|
|
7991
7995
|
|
|
7992
7996
|
pm_context_node_t *previous = parser->current_context;
|
|
7993
7997
|
*context_node = (pm_context_node_t) {
|
|
@@ -8002,9 +8006,10 @@ context_push(pm_parser_t *parser, pm_context_t context) {
|
|
|
8002
8006
|
|
|
8003
8007
|
static void
|
|
8004
8008
|
context_pop(pm_parser_t *parser) {
|
|
8005
|
-
pm_context_node_t *
|
|
8006
|
-
|
|
8007
|
-
|
|
8009
|
+
pm_context_node_t *context_node = parser->current_context;
|
|
8010
|
+
parser->current_context = context_node->prev;
|
|
8011
|
+
context_node->prev = parser->free_contexts;
|
|
8012
|
+
parser->free_contexts = context_node;
|
|
8008
8013
|
}
|
|
8009
8014
|
|
|
8010
8015
|
static bool
|
|
@@ -8508,82 +8513,186 @@ lex_global_variable(pm_parser_t *parser) {
|
|
|
8508
8513
|
}
|
|
8509
8514
|
|
|
8510
8515
|
/**
|
|
8511
|
-
*
|
|
8512
|
-
*
|
|
8516
|
+
* A Ruby keyword, along with the lex state that it transitions to and the
|
|
8517
|
+
* token types that it produces.
|
|
8518
|
+
*/
|
|
8519
|
+
typedef struct {
|
|
8520
|
+
/* The keyword itself. */
|
|
8521
|
+
const char *name;
|
|
8522
|
+
|
|
8523
|
+
/* The keyword's first, middle, and last bytes and its length, packed by
|
|
8524
|
+
* PM_KEYWORD_PACK. */
|
|
8525
|
+
uint32_t packed;
|
|
8526
|
+
|
|
8527
|
+
/* The state to transition to when the keyword is lexed. */
|
|
8528
|
+
pm_lex_state_t state;
|
|
8529
|
+
|
|
8530
|
+
/* The token type of the keyword. */
|
|
8531
|
+
pm_token_type_t type;
|
|
8532
|
+
|
|
8533
|
+
/* The token type of the keyword in modifier position, or PM_TOKEN_EOF if
|
|
8534
|
+
* the keyword has no modifier form. */
|
|
8535
|
+
pm_token_type_t modifier_type;
|
|
8536
|
+
} pm_keyword_t;
|
|
8537
|
+
|
|
8538
|
+
/**
|
|
8539
|
+
* The length of the longest keyword, __ENCODING__.
|
|
8540
|
+
*/
|
|
8541
|
+
#define PM_KEYWORD_LENGTH_MAXIMUM 12
|
|
8542
|
+
|
|
8543
|
+
/**
|
|
8544
|
+
* Pack the first, middle (at index length / 2), and last bytes of an identifier
|
|
8545
|
+
* together with its length. No two keywords pack to the same value.
|
|
8546
|
+
*/
|
|
8547
|
+
#define PM_KEYWORD_PACK(first, middle, last, length) \
|
|
8548
|
+
((uint32_t) (first) | ((uint32_t) (middle) << 8) | ((uint32_t) (last) << 16) | ((uint32_t) (length) << 24))
|
|
8549
|
+
|
|
8550
|
+
/**
|
|
8551
|
+
* The number of bits in a slot index of pm_keywords, which has
|
|
8552
|
+
* 1 << PM_KEYWORD_SLOT_BITS slots.
|
|
8553
|
+
*/
|
|
8554
|
+
#define PM_KEYWORD_SLOT_BITS 6
|
|
8555
|
+
|
|
8556
|
+
/**
|
|
8557
|
+
* Return the slot in pm_keywords for a packed identifier. This is
|
|
8558
|
+
* multiplicative hashing: the product is taken modulo 2^32, and the slot is its
|
|
8559
|
+
* top PM_KEYWORD_SLOT_BITS bits. The top bits are used because the low bits of
|
|
8560
|
+
* a product depend only on the low bits of its operands (here, the low bits of
|
|
8561
|
+
* the first byte), while the top bits depend on all of them.
|
|
8513
8562
|
*
|
|
8514
|
-
*
|
|
8515
|
-
*
|
|
8516
|
-
*
|
|
8517
|
-
*
|
|
8518
|
-
*
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8563
|
+
* The multiplier comes from `bin/prism keywords`, which searches for one that
|
|
8564
|
+
* gives every keyword its own slot, making this a perfect hash over the
|
|
8565
|
+
* keywords. Rerun it whenever the set of keywords changes. The table below is
|
|
8566
|
+
* indexed with this same macro, so if two keywords ever shared a slot, the
|
|
8567
|
+
* compiler would reject the duplicate initializer.
|
|
8568
|
+
*/
|
|
8569
|
+
#define PM_KEYWORD_SLOT(packed) ((uint32_t) ((uint32_t) (packed) * 0x75f796ddU) >> (32 - PM_KEYWORD_SLOT_BITS))
|
|
8570
|
+
|
|
8571
|
+
/**
|
|
8572
|
+
* Define the entry in pm_keywords for a keyword, given its first, middle, and
|
|
8573
|
+
* last characters.
|
|
8574
|
+
*/
|
|
8575
|
+
#define PM_KEYWORD(name, first, middle, last, state, type, modifier_type) \
|
|
8576
|
+
[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 }
|
|
8577
|
+
|
|
8578
|
+
/**
|
|
8579
|
+
* The keywords, indexed by PM_KEYWORD_SLOT. Unused slots are zeroed, and a
|
|
8580
|
+
* packed value of zero never matches an identifier because its length is at
|
|
8581
|
+
* least one.
|
|
8582
|
+
*/
|
|
8583
|
+
static const pm_keyword_t pm_keywords[1 << PM_KEYWORD_SLOT_BITS] = {
|
|
8584
|
+
PM_KEYWORD("do", 'd', 'o', 'o', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF),
|
|
8585
|
+
PM_KEYWORD("if", 'i', 'f', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER),
|
|
8586
|
+
PM_KEYWORD("in", 'i', 'n', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IN, PM_TOKEN_EOF),
|
|
8587
|
+
PM_KEYWORD("or", 'o', 'r', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_OR, PM_TOKEN_EOF),
|
|
8588
|
+
PM_KEYWORD("and", 'a', 'n', 'd', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_AND, PM_TOKEN_EOF),
|
|
8589
|
+
PM_KEYWORD("def", 'd', 'e', 'f', PM_LEX_STATE_FNAME, PM_TOKEN_KEYWORD_DEF, PM_TOKEN_EOF),
|
|
8590
|
+
PM_KEYWORD("end", 'e', 'n', 'd', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END, PM_TOKEN_EOF),
|
|
8591
|
+
PM_KEYWORD("END", 'E', 'N', 'D', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END_UPCASE, PM_TOKEN_EOF),
|
|
8592
|
+
PM_KEYWORD("for", 'f', 'o', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_FOR, PM_TOKEN_EOF),
|
|
8593
|
+
PM_KEYWORD("nil", 'n', 'i', 'l', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_NIL, PM_TOKEN_EOF),
|
|
8594
|
+
PM_KEYWORD("not", 'n', 'o', 't', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_NOT, PM_TOKEN_EOF),
|
|
8595
|
+
PM_KEYWORD("case", 'c', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_CASE, PM_TOKEN_EOF),
|
|
8596
|
+
PM_KEYWORD("else", 'e', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_EOF),
|
|
8597
|
+
PM_KEYWORD("next", 'n', 'x', 't', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_NEXT, PM_TOKEN_EOF),
|
|
8598
|
+
PM_KEYWORD("redo", 'r', 'd', 'o', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_REDO, PM_TOKEN_EOF),
|
|
8599
|
+
PM_KEYWORD("self", 's', 'l', 'f', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_SELF, PM_TOKEN_EOF),
|
|
8600
|
+
PM_KEYWORD("then", 't', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_EOF),
|
|
8601
|
+
PM_KEYWORD("true", 't', 'u', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_TRUE, PM_TOKEN_EOF),
|
|
8602
|
+
PM_KEYWORD("when", 'w', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_EOF),
|
|
8603
|
+
PM_KEYWORD("alias", 'a', 'i', 's', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_ALIAS, PM_TOKEN_EOF),
|
|
8604
|
+
PM_KEYWORD("begin", 'b', 'g', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_BEGIN, PM_TOKEN_EOF),
|
|
8605
|
+
PM_KEYWORD("BEGIN", 'B', 'G', 'N', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_BEGIN_UPCASE, PM_TOKEN_EOF),
|
|
8606
|
+
PM_KEYWORD("break", 'b', 'e', 'k', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_BREAK, PM_TOKEN_EOF),
|
|
8607
|
+
PM_KEYWORD("class", 'c', 'a', 's', PM_LEX_STATE_CLASS, PM_TOKEN_KEYWORD_CLASS, PM_TOKEN_EOF),
|
|
8608
|
+
PM_KEYWORD("elsif", 'e', 's', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_EOF),
|
|
8609
|
+
PM_KEYWORD("false", 'f', 'l', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_FALSE, PM_TOKEN_EOF),
|
|
8610
|
+
PM_KEYWORD("retry", 'r', 't', 'y', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_RETRY, PM_TOKEN_EOF),
|
|
8611
|
+
PM_KEYWORD("super", 's', 'p', 'r', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_SUPER, PM_TOKEN_EOF),
|
|
8612
|
+
PM_KEYWORD("undef", 'u', 'd', 'f', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_UNDEF, PM_TOKEN_EOF),
|
|
8613
|
+
PM_KEYWORD("until", 'u', 't', 'l', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNTIL, PM_TOKEN_KEYWORD_UNTIL_MODIFIER),
|
|
8614
|
+
PM_KEYWORD("while", 'w', 'i', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHILE, PM_TOKEN_KEYWORD_WHILE_MODIFIER),
|
|
8615
|
+
PM_KEYWORD("yield", 'y', 'e', 'd', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_YIELD, PM_TOKEN_EOF),
|
|
8616
|
+
PM_KEYWORD("ensure", 'e', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_EOF),
|
|
8617
|
+
PM_KEYWORD("module", 'm', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_MODULE, PM_TOKEN_EOF),
|
|
8618
|
+
PM_KEYWORD("rescue", 'r', 'c', 'e', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_RESCUE_MODIFIER),
|
|
8619
|
+
PM_KEYWORD("return", 'r', 'u', 'n', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RETURN, PM_TOKEN_EOF),
|
|
8620
|
+
PM_KEYWORD("unless", 'u', 'e', 's', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNLESS, PM_TOKEN_KEYWORD_UNLESS_MODIFIER),
|
|
8621
|
+
PM_KEYWORD("defined?", 'd', 'n', '?', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF),
|
|
8622
|
+
PM_KEYWORD("__LINE__", '_', 'N', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___LINE__, PM_TOKEN_EOF),
|
|
8623
|
+
PM_KEYWORD("__FILE__", '_', 'L', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___FILE__, PM_TOKEN_EOF),
|
|
8624
|
+
PM_KEYWORD("__ENCODING__", '_', 'D', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___ENCODING__, PM_TOKEN_EOF)
|
|
8625
|
+
};
|
|
8534
8626
|
|
|
8535
|
-
|
|
8536
|
-
lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
|
|
8537
|
-
return modifier_type;
|
|
8538
|
-
}
|
|
8539
|
-
}
|
|
8627
|
+
#undef PM_KEYWORD
|
|
8540
8628
|
|
|
8541
|
-
|
|
8542
|
-
|
|
8629
|
+
/**
|
|
8630
|
+
* Return the keyword that the identifier corresponds to or NULL if it is not in
|
|
8631
|
+
* the table.
|
|
8632
|
+
*/
|
|
8633
|
+
static PRISM_INLINE const pm_keyword_t *
|
|
8634
|
+
pm_keyword_find(const uint8_t *start, const uint8_t *end) {
|
|
8635
|
+
ptrdiff_t width = end - start;
|
|
8636
|
+
if (width < 2 || width > PM_KEYWORD_LENGTH_MAXIMUM) return NULL;
|
|
8543
8637
|
|
|
8544
|
-
|
|
8638
|
+
size_t uwidth = (size_t) width;
|
|
8639
|
+
uint32_t packed = PM_KEYWORD_PACK(start[0], start[uwidth >> 1], start[uwidth - 1], uwidth);
|
|
8640
|
+
|
|
8641
|
+
const pm_keyword_t *keyword = &pm_keywords[PM_KEYWORD_SLOT(packed)];
|
|
8642
|
+
return (keyword->packed == packed && memcmp(start + 1, keyword->name + 1, uwidth - 2) == 0) ? keyword : NULL;
|
|
8643
|
+
}
|
|
8644
|
+
|
|
8645
|
+
/**
|
|
8646
|
+
* Returns true if the bytes between start and end spell a keyword, using the
|
|
8647
|
+
* same lookup that the lexer does.
|
|
8648
|
+
*/
|
|
8649
|
+
bool
|
|
8650
|
+
pm_keyword_p(const uint8_t *start, const uint8_t *end) {
|
|
8651
|
+
return pm_keyword_find(start, end) != NULL;
|
|
8545
8652
|
}
|
|
8546
8653
|
|
|
8547
8654
|
static pm_token_type_t
|
|
8548
8655
|
lex_identifier(pm_parser_t *parser, bool previous_command_start) {
|
|
8549
|
-
|
|
8550
|
-
size_t width;
|
|
8656
|
+
/* Lex as far as we can into the current identifier. */
|
|
8551
8657
|
const uint8_t *end = parser->end;
|
|
8552
8658
|
const uint8_t *current_start = parser->current.start;
|
|
8553
8659
|
const uint8_t *current_end = parser->current.end;
|
|
8554
8660
|
bool encoding_changed = parser->encoding_changed;
|
|
8555
8661
|
|
|
8556
8662
|
if (encoding_changed) {
|
|
8663
|
+
size_t width;
|
|
8557
8664
|
while ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) {
|
|
8558
8665
|
current_end += width;
|
|
8559
8666
|
}
|
|
8560
8667
|
} else {
|
|
8561
|
-
|
|
8668
|
+
/* Fast path: scan ASCII identifier bytes using wide operations. */
|
|
8562
8669
|
current_end += scan_identifier_ascii(current_end, end);
|
|
8563
8670
|
|
|
8564
|
-
|
|
8671
|
+
/* Byte-at-a-time fallback for the tail and any UTF-8 sequences. */
|
|
8672
|
+
size_t width;
|
|
8565
8673
|
while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) {
|
|
8566
8674
|
current_end += width;
|
|
8567
8675
|
}
|
|
8568
8676
|
}
|
|
8677
|
+
|
|
8569
8678
|
parser->current.end = current_end;
|
|
8570
8679
|
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8680
|
+
/* Whether the identifier ends in ! or ?, which makes it a method name
|
|
8681
|
+
* unless it spells a keyword (defined?). */
|
|
8682
|
+
bool method_name = false;
|
|
8574
8683
|
|
|
8575
8684
|
if (current_end < end) {
|
|
8576
8685
|
if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8686
|
+
/* Extend the identifier by the ! or ?, so that the keyword lookup
|
|
8687
|
+
* below sees the whole name. */
|
|
8688
|
+
current_end++;
|
|
8580
8689
|
|
|
8581
8690
|
if (
|
|
8582
8691
|
((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
|
|
8583
8692
|
(peek(parser) == ':') && (peek_offset(parser, 1) != ':')
|
|
8584
8693
|
) {
|
|
8585
|
-
|
|
8586
|
-
|
|
8694
|
+
/* If we are in a position where we can accept a : at the end of
|
|
8695
|
+
* an identifier, then we will optionally accept it. */
|
|
8587
8696
|
lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
|
|
8588
8697
|
(void) match(parser, ':');
|
|
8589
8698
|
|
|
@@ -8593,27 +8702,17 @@ lex_identifier(pm_parser_t *parser, bool previous_command_start) {
|
|
|
8593
8702
|
return PM_TOKEN_LABEL;
|
|
8594
8703
|
}
|
|
8595
8704
|
|
|
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.
|
|
8705
|
+
method_name = true;
|
|
8706
|
+
} 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, '=')) {
|
|
8707
|
+
/* If we are in a position where we can accept a = at the end of an
|
|
8708
|
+
* identifier, then we will optionally accept it. */
|
|
8608
8709
|
return PM_TOKEN_IDENTIFIER;
|
|
8609
|
-
}
|
|
8610
|
-
|
|
8611
|
-
if (
|
|
8710
|
+
} else if (
|
|
8612
8711
|
((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
|
|
8613
8712
|
peek(parser) == ':' && peek_offset(parser, 1) != ':'
|
|
8614
8713
|
) {
|
|
8615
|
-
|
|
8616
|
-
|
|
8714
|
+
/* If we are in a position where we can accept a : at the end of an
|
|
8715
|
+
* identifier, then we will optionally accept it. */
|
|
8617
8716
|
lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
|
|
8618
8717
|
(void) match(parser, ':');
|
|
8619
8718
|
|
|
@@ -8625,106 +8724,66 @@ lex_identifier(pm_parser_t *parser, bool previous_command_start) {
|
|
|
8625
8724
|
}
|
|
8626
8725
|
|
|
8627
8726
|
if (parser->lex_state != PM_LEX_STATE_DOT) {
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
* `state = p->lex.state` capture in parse.y's keyword handling. */
|
|
8727
|
+
/* The lex state from before the keyword changes it, which decides
|
|
8728
|
+
* whether the keyword is in its modifier form and whether `do` is a
|
|
8729
|
+
* plain name below. */
|
|
8632
8730
|
pm_lex_state_t previous_lex_state = parser->lex_state;
|
|
8731
|
+
const pm_keyword_t *keyword = pm_keyword_find(current_start, current_end);
|
|
8633
8732
|
|
|
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
|
-
}
|
|
8733
|
+
if (keyword != NULL) {
|
|
8734
|
+
if (previous_lex_state & PM_LEX_STATE_FNAME) {
|
|
8735
|
+
lex_state_set(parser, PM_LEX_STATE_ENDFN);
|
|
8736
|
+
} else {
|
|
8737
|
+
lex_state_set(parser, keyword->state);
|
|
8738
|
+
if (keyword->state == PM_LEX_STATE_BEG) {
|
|
8739
|
+
parser->command_start = true;
|
|
8740
|
+
}
|
|
8741
|
+
|
|
8742
|
+
if ((keyword->modifier_type != PM_TOKEN_EOF) && !(previous_lex_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
|
|
8743
|
+
lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
|
|
8744
|
+
return keyword->modifier_type;
|
|
8745
|
+
}
|
|
8746
|
+
}
|
|
8747
|
+
|
|
8748
|
+
if (keyword->type == PM_TOKEN_KEYWORD_DO) {
|
|
8749
|
+
/* In FNAME position (a symbol like `:do` or a method name like
|
|
8750
|
+
* `def do`), `do` is a plain name rather than a block, loop, or
|
|
8751
|
+
* lambda opener, so none of the discrimination below applies
|
|
8752
|
+
* and the lambda nesting is left untouched. */
|
|
8753
|
+
if (previous_lex_state & PM_LEX_STATE_FNAME) {
|
|
8670
8754
|
return PM_TOKEN_KEYWORD_DO;
|
|
8671
8755
|
}
|
|
8756
|
+
if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
|
|
8757
|
+
/* At the bare nesting level of a lambda literal (no
|
|
8758
|
+
* delimiter opened since `->`), a `do` opens the lambda
|
|
8759
|
+
* body. This is a distinct token so that a command in a
|
|
8760
|
+
* parameter default cannot consume it as its own block
|
|
8761
|
+
* (`-> a = foo do end` is `->(a = foo) do end`).
|
|
8762
|
+
*
|
|
8763
|
+
* Clear the nesting so that no token within the `do`/`end`
|
|
8764
|
+
* body is considered to be at the beginning of a lambda;
|
|
8765
|
+
* the parser restores the enclosing value once the lambda
|
|
8766
|
+
* has been fully parsed. */
|
|
8767
|
+
parser->lambda_enclosure_nesting = -1;
|
|
8768
|
+
return PM_TOKEN_KEYWORD_DO_LAMBDA;
|
|
8769
|
+
}
|
|
8770
|
+
if (pm_do_loop_stack_p(parser)) {
|
|
8771
|
+
return PM_TOKEN_KEYWORD_DO_LOOP;
|
|
8772
|
+
}
|
|
8773
|
+
if (!pm_accepts_block_stack_p(parser)) {
|
|
8774
|
+
return PM_TOKEN_KEYWORD_DO_BLOCK;
|
|
8775
|
+
}
|
|
8776
|
+
return PM_TOKEN_KEYWORD_DO;
|
|
8777
|
+
}
|
|
8672
8778
|
|
|
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;
|
|
8779
|
+
return keyword->type;
|
|
8725
8780
|
}
|
|
8726
8781
|
}
|
|
8727
8782
|
|
|
8783
|
+
if (method_name) {
|
|
8784
|
+
return PM_TOKEN_METHOD_NAME;
|
|
8785
|
+
}
|
|
8786
|
+
|
|
8728
8787
|
if (encoding_changed) {
|
|
8729
8788
|
return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
|
|
8730
8789
|
}
|
|
@@ -9575,7 +9634,7 @@ lex_question_mark(pm_parser_t *parser) {
|
|
|
9575
9634
|
|
|
9576
9635
|
// Copy buffer data into the arena and free the heap buffer.
|
|
9577
9636
|
void *arena_data = pm_arena_memdup(parser->arena, buffer.value, buffer.length, PRISM_ALIGNOF(uint8_t));
|
|
9578
|
-
|
|
9637
|
+
pm_string_init(&parser->current_string, (const uint8_t *) arena_data, buffer.length);
|
|
9579
9638
|
pm_buffer_cleanup(&buffer);
|
|
9580
9639
|
|
|
9581
9640
|
return PM_TOKEN_CHARACTER_LITERAL;
|
|
@@ -9899,7 +9958,7 @@ pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
|
|
|
9899
9958
|
// Copy buffer data into the arena and free the heap buffer.
|
|
9900
9959
|
size_t len = pm_buffer_length(&token_buffer->buffer);
|
|
9901
9960
|
void *arena_data = pm_arena_memdup(parser->arena, pm_buffer_value(&token_buffer->buffer), len, PRISM_ALIGNOF(uint8_t));
|
|
9902
|
-
|
|
9961
|
+
pm_string_init(&parser->current_string, (const uint8_t *) arena_data, len);
|
|
9903
9962
|
pm_buffer_cleanup(&token_buffer->buffer);
|
|
9904
9963
|
}
|
|
9905
9964
|
|
|
@@ -12952,6 +13011,19 @@ expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t di
|
|
|
12952
13011
|
*/
|
|
12953
13012
|
#define PM_PARSE_ACCEPTS_STATEMENT ((uint8_t) 0x10)
|
|
12954
13013
|
|
|
13014
|
+
/**
|
|
13015
|
+
* Indicates that an endless method definition may have a command call as its
|
|
13016
|
+
* body, as in `def f = foo 1`. Such a definition is a command write, accepted
|
|
13017
|
+
* as a statement (including the value of a statement-level rescue modifier)
|
|
13018
|
+
* and as the value of a single-target write. Since Ruby 4.0 it is also
|
|
13019
|
+
* accepted as the sole argument of a call, where other command writes such as
|
|
13020
|
+
* `x = foo 1` are not. Expression positions such as conditional predicates and
|
|
13021
|
+
* the operands of `not`, `!`, `and`, and `or` accept a command call but not a
|
|
13022
|
+
* command write, so this is set alongside PM_PARSE_ACCEPTS_COMMAND_CALL only
|
|
13023
|
+
* where both are accepted.
|
|
13024
|
+
*/
|
|
13025
|
+
#define PM_PARSE_ACCEPTS_COMMAND_DEF ((uint8_t) 0x20)
|
|
13026
|
+
|
|
12955
13027
|
static pm_node_t *
|
|
12956
13028
|
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
13029
|
|
|
@@ -13067,6 +13139,23 @@ token_begins_pattern_p(pm_token_type_t type) {
|
|
|
13067
13139
|
);
|
|
13068
13140
|
}
|
|
13069
13141
|
|
|
13142
|
+
/**
|
|
13143
|
+
* Returns true if the given token can begin an argument in an unparenthesized
|
|
13144
|
+
* argument list. This is the set of tokens that can begin an expression plus
|
|
13145
|
+
* the prefix operators that only appear in argument position — `&` (block
|
|
13146
|
+
* arguments), `*` (splats), and `**` (keyword splats) — which are excluded
|
|
13147
|
+
* from token_begins_expression_p.
|
|
13148
|
+
*/
|
|
13149
|
+
static PRISM_INLINE bool
|
|
13150
|
+
token_begins_argument_p(pm_token_type_t type) {
|
|
13151
|
+
return (
|
|
13152
|
+
token_begins_expression_p(type) ||
|
|
13153
|
+
type == PM_TOKEN_UAMPERSAND ||
|
|
13154
|
+
type == PM_TOKEN_USTAR ||
|
|
13155
|
+
type == PM_TOKEN_USTAR_STAR
|
|
13156
|
+
);
|
|
13157
|
+
}
|
|
13158
|
+
|
|
13070
13159
|
/**
|
|
13071
13160
|
* Parse an expression with the given binding power that may be optionally
|
|
13072
13161
|
* prefixed by the * operator.
|
|
@@ -13082,7 +13171,7 @@ parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power,
|
|
|
13082
13171
|
return parse_value_expression(parser, binding_power, flags, diag_id, depth);
|
|
13083
13172
|
}
|
|
13084
13173
|
|
|
13085
|
-
static
|
|
13174
|
+
static pm_visit_result_t
|
|
13086
13175
|
pm_node_unreference_each(const pm_node_t *node, void *data) {
|
|
13087
13176
|
switch (PM_NODE_TYPE(node)) {
|
|
13088
13177
|
/* When we are about to destroy a set of nodes that could potentially
|
|
@@ -13108,15 +13197,15 @@ pm_node_unreference_each(const pm_node_t *node, void *data) {
|
|
|
13108
13197
|
}
|
|
13109
13198
|
parser->current_block_exits->size--;
|
|
13110
13199
|
|
|
13111
|
-
/* Note
|
|
13200
|
+
/* Note descending here because these nodes could have
|
|
13112
13201
|
* arguments that are themselves block exits. */
|
|
13113
|
-
return
|
|
13202
|
+
return PM_VISIT_DESCEND;
|
|
13114
13203
|
}
|
|
13115
13204
|
|
|
13116
13205
|
index++;
|
|
13117
13206
|
}
|
|
13118
13207
|
|
|
13119
|
-
return
|
|
13208
|
+
return PM_VISIT_DESCEND;
|
|
13120
13209
|
}
|
|
13121
13210
|
/* When an implicit local variable is written to or targeted, it becomes
|
|
13122
13211
|
* a regular, named local variable. This branch removes it from the list
|
|
@@ -13140,10 +13229,10 @@ pm_node_unreference_each(const pm_node_t *node, void *data) {
|
|
|
13140
13229
|
}
|
|
13141
13230
|
}
|
|
13142
13231
|
|
|
13143
|
-
return
|
|
13232
|
+
return PM_VISIT_SKIP;
|
|
13144
13233
|
}
|
|
13145
13234
|
default:
|
|
13146
|
-
return
|
|
13235
|
+
return PM_VISIT_DESCEND;
|
|
13147
13236
|
}
|
|
13148
13237
|
}
|
|
13149
13238
|
|
|
@@ -13708,7 +13797,7 @@ parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
|
|
|
13708
13797
|
context_push(parser, context);
|
|
13709
13798
|
|
|
13710
13799
|
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));
|
|
13800
|
+
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
13801
|
pm_statements_node_body_append(parser, statements, node, true);
|
|
13713
13802
|
|
|
13714
13803
|
// If we're recovering from a syntax error, then we need to stop parsing
|
|
@@ -13794,7 +13883,7 @@ parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
|
|
|
13794
13883
|
static void
|
|
13795
13884
|
pm_hash_key_duplicated_warn(pm_parser_t *parser, const pm_node_t *duplicated, const pm_node_t *node) {
|
|
13796
13885
|
pm_buffer_t buffer = { 0 };
|
|
13797
|
-
pm_static_literal_inspect(&buffer,
|
|
13886
|
+
pm_static_literal_inspect(&buffer, parser, duplicated);
|
|
13798
13887
|
|
|
13799
13888
|
pm_diagnostic_list_append_format(
|
|
13800
13889
|
&parser->metadata_arena,
|
|
@@ -13816,7 +13905,7 @@ pm_hash_key_duplicated_warn(pm_parser_t *parser, const pm_node_t *duplicated, co
|
|
|
13816
13905
|
*/
|
|
13817
13906
|
static void
|
|
13818
13907
|
pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
|
|
13819
|
-
const pm_node_t *duplicated = pm_static_literals_add(
|
|
13908
|
+
const pm_node_t *duplicated = pm_static_literals_add(parser, literals, node, true);
|
|
13820
13909
|
|
|
13821
13910
|
if (duplicated != NULL) {
|
|
13822
13911
|
pm_hash_key_duplicated_warn(parser, duplicated, node);
|
|
@@ -13840,7 +13929,7 @@ pm_hash_key_static_literals_merge(pm_parser_t *parser, pm_static_literals_t *lit
|
|
|
13840
13929
|
switch (PM_NODE_TYPE(element)) {
|
|
13841
13930
|
case PM_ASSOC_NODE: {
|
|
13842
13931
|
pm_node_t *key = ((pm_assoc_node_t *) element)->key;
|
|
13843
|
-
const pm_node_t *duplicated = pm_static_literals_add(
|
|
13932
|
+
const pm_node_t *duplicated = pm_static_literals_add(parser, literals, key, true);
|
|
13844
13933
|
|
|
13845
13934
|
if (duplicated != NULL && PM_NODE_START(duplicated) < boundary) {
|
|
13846
13935
|
pm_hash_key_duplicated_warn(parser, duplicated, key);
|
|
@@ -13871,7 +13960,7 @@ static void
|
|
|
13871
13960
|
pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
|
|
13872
13961
|
pm_node_t *previous;
|
|
13873
13962
|
|
|
13874
|
-
if ((previous = pm_static_literals_add(
|
|
13963
|
+
if ((previous = pm_static_literals_add(parser, literals, node, false)) != NULL) {
|
|
13875
13964
|
pm_diagnostic_list_append_format(
|
|
13876
13965
|
&parser->metadata_arena,
|
|
13877
13966
|
&parser->warning_list,
|
|
@@ -14135,6 +14224,27 @@ pm_command_call_value_p(const pm_parser_t *parser, const pm_node_t *node) {
|
|
|
14135
14224
|
}
|
|
14136
14225
|
}
|
|
14137
14226
|
|
|
14227
|
+
/**
|
|
14228
|
+
* Returns true if the given node is a command that can end before a comma
|
|
14229
|
+
* without consuming it: a constant-path command with a brace block
|
|
14230
|
+
* (`Foo::Bar { }`), or an endless method whose command body ends in a rescue
|
|
14231
|
+
* modifier value (`def f = foo 1 rescue z`). Other commands take a following
|
|
14232
|
+
* comma into their own arguments. A command can be neither one of several
|
|
14233
|
+
* arguments nor one of several values of a write, so a comma after one of
|
|
14234
|
+
* these is an error.
|
|
14235
|
+
*/
|
|
14236
|
+
static bool
|
|
14237
|
+
pm_command_before_comma_p(const pm_parser_t *parser, const pm_node_t *node) {
|
|
14238
|
+
switch (PM_NODE_TYPE(node)) {
|
|
14239
|
+
case PM_CALL_NODE:
|
|
14240
|
+
return pm_constant_path_command_call_p(parser, (const pm_call_node_t *) node);
|
|
14241
|
+
case PM_DEF_NODE:
|
|
14242
|
+
return pm_command_call_value_p(parser, node);
|
|
14243
|
+
default:
|
|
14244
|
+
return false;
|
|
14245
|
+
}
|
|
14246
|
+
}
|
|
14247
|
+
|
|
14138
14248
|
/**
|
|
14139
14249
|
* Returns true if the given node is a block call: a command
|
|
14140
14250
|
* with a do-block, or any call chained (via `.`, `::`, `&.`) from such a node.
|
|
@@ -14321,7 +14431,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
|
|
|
14321
14431
|
PRISM_FALLTHROUGH
|
|
14322
14432
|
default: {
|
|
14323
14433
|
if (argument == NULL) {
|
|
14324
|
-
|
|
14434
|
+
/* The first argument may be a command call, and since Ruby
|
|
14435
|
+
* 4.0 also an endless method with a command body
|
|
14436
|
+
* (`private def f = foo 1`), either of which must then be
|
|
14437
|
+
* the sole argument. */
|
|
14438
|
+
uint8_t command_flags = 0;
|
|
14439
|
+
if (!parsed_first_argument) {
|
|
14440
|
+
command_flags = PM_PARSE_ACCEPTS_COMMAND_CALL;
|
|
14441
|
+
if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
|
|
14442
|
+
}
|
|
14443
|
+
|
|
14444
|
+
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
14445
|
}
|
|
14326
14446
|
|
|
14327
14447
|
bool contains_keywords = false;
|
|
@@ -14418,6 +14538,12 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
|
|
|
14418
14538
|
break;
|
|
14419
14539
|
}
|
|
14420
14540
|
}
|
|
14541
|
+
|
|
14542
|
+
/* A command can only be the sole argument, as in
|
|
14543
|
+
* `foo Foo::Bar { }, y` or `foo def f = bar 1 rescue z, y`. */
|
|
14544
|
+
if (pm_command_before_comma_p(parser, argument)) {
|
|
14545
|
+
pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
|
|
14546
|
+
}
|
|
14421
14547
|
} else {
|
|
14422
14548
|
// If there is no comma at the end of the argument list then we're
|
|
14423
14549
|
// done parsing arguments and can break out of this loop.
|
|
@@ -15628,7 +15754,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_a
|
|
|
15628
15754
|
|
|
15629
15755
|
arguments->closing_loc = TOK2LOC(parser, &parser->previous);
|
|
15630
15756
|
}
|
|
15631
|
-
} else if ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (
|
|
15757
|
+
} else if ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
|
|
15632
15758
|
found |= true;
|
|
15633
15759
|
parsed_command_args = true;
|
|
15634
15760
|
|
|
@@ -15848,7 +15974,7 @@ ensure_valid_jump(pm_parser_t *parser, pm_node_t *node) {
|
|
|
15848
15974
|
// block exit to the list of exits for the expression, and
|
|
15849
15975
|
// the node parsing will handle validating it instead.
|
|
15850
15976
|
assert(parser->current_block_exits != NULL);
|
|
15851
|
-
|
|
15977
|
+
pm_node_list_append_inline(&parser->scratch_arena, parser->current_block_exits, node);
|
|
15852
15978
|
return;
|
|
15853
15979
|
case PM_CONTEXT_BEGIN_ELSE:
|
|
15854
15980
|
case PM_CONTEXT_BEGIN_ENSURE:
|
|
@@ -15940,7 +16066,7 @@ pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
|
|
|
15940
16066
|
// However, they could still become valid in a higher level context if
|
|
15941
16067
|
// there is another list above this one. In this case we'll push all of
|
|
15942
16068
|
// the block exits up to the previous list.
|
|
15943
|
-
pm_node_list_concat(parser->
|
|
16069
|
+
pm_node_list_concat(&parser->scratch_arena, previous_block_exits, parser->current_block_exits);
|
|
15944
16070
|
parser->current_block_exits = previous_block_exits;
|
|
15945
16071
|
} else {
|
|
15946
16072
|
// If we did not match a trailing while/until and this was the last
|
|
@@ -16586,12 +16712,12 @@ parse_variable(pm_parser_t *parser) {
|
|
|
16586
16712
|
}
|
|
16587
16713
|
|
|
16588
16714
|
pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false));
|
|
16589
|
-
|
|
16715
|
+
pm_node_list_append_inline(&parser->scratch_arena, ¤t_scope->implicit_parameters, node);
|
|
16590
16716
|
|
|
16591
16717
|
return node;
|
|
16592
16718
|
} else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
|
|
16593
16719
|
pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous));
|
|
16594
|
-
|
|
16720
|
+
pm_node_list_append_inline(&parser->scratch_arena, ¤t_scope->implicit_parameters, node);
|
|
16595
16721
|
|
|
16596
16722
|
return node;
|
|
16597
16723
|
}
|
|
@@ -16648,18 +16774,13 @@ parse_method_definition_name(pm_parser_t *parser) {
|
|
|
16648
16774
|
|
|
16649
16775
|
static void
|
|
16650
16776
|
parse_heredoc_dedent_string(pm_arena_t *arena, pm_string_t *string, size_t common_whitespace) {
|
|
16651
|
-
// Make a writable copy
|
|
16652
|
-
// We keep a mutable pointer to the arena memory so we can
|
|
16653
|
-
// below without casting away const from the string's
|
|
16654
|
-
|
|
16655
|
-
|
|
16656
|
-
|
|
16657
|
-
|
|
16658
|
-
writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
|
|
16659
|
-
pm_string_constant_init(string, (const char *) writable, length);
|
|
16660
|
-
} else {
|
|
16661
|
-
writable = (uint8_t *) string->source;
|
|
16662
|
-
}
|
|
16777
|
+
// Make a writable copy of the string in the arena, since strings only view
|
|
16778
|
+
// their bytes. We keep a mutable pointer to the arena memory so we can
|
|
16779
|
+
// memmove into it below without casting away const from the string's
|
|
16780
|
+
// source field.
|
|
16781
|
+
size_t length = pm_string_length(string);
|
|
16782
|
+
uint8_t *writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
|
|
16783
|
+
pm_string_init(string, writable, length);
|
|
16663
16784
|
|
|
16664
16785
|
// Now get the bounds of the existing string. We'll use this as a
|
|
16665
16786
|
// destination to move bytes into. We'll also use it for bounds checking
|
|
@@ -16824,11 +16945,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
|
|
|
16824
16945
|
if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
|
|
16825
16946
|
pm_node_list_t parts = { 0 };
|
|
16826
16947
|
pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
|
|
16827
|
-
|
|
16948
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16828
16949
|
|
|
16829
16950
|
do {
|
|
16830
16951
|
part = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
|
|
16831
|
-
|
|
16952
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16832
16953
|
parser_lex(parser);
|
|
16833
16954
|
} while (match1(parser, PM_TOKEN_STRING_CONTENT));
|
|
16834
16955
|
|
|
@@ -16883,11 +17004,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
|
|
|
16883
17004
|
pm_node_list_t parts = { 0 };
|
|
16884
17005
|
pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
|
|
16885
17006
|
pm_node_flag_set(part, parse_unescaped_encoding(parser, explicit_encoding));
|
|
16886
|
-
|
|
17007
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16887
17008
|
|
|
16888
17009
|
while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
|
|
16889
17010
|
if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
|
|
16890
|
-
|
|
17011
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16891
17012
|
}
|
|
16892
17013
|
}
|
|
16893
17014
|
|
|
@@ -16911,7 +17032,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
|
|
|
16911
17032
|
|
|
16912
17033
|
while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
|
|
16913
17034
|
if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
|
|
16914
|
-
|
|
17035
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16915
17036
|
}
|
|
16916
17037
|
}
|
|
16917
17038
|
|
|
@@ -16983,7 +17104,7 @@ parse_pattern_capture(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_co
|
|
|
16983
17104
|
// Skip this capture if it starts with an underscore.
|
|
16984
17105
|
if (peek_at(parser, parser->start + location->start) == '_') return;
|
|
16985
17106
|
|
|
16986
|
-
if (!pm_constant_id_set_insert(parser->
|
|
17107
|
+
if (!pm_constant_id_set_insert(&parser->scratch_arena, captures, capture)) {
|
|
16987
17108
|
pm_parser_err(parser, location->start, location->length, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
|
|
16988
17109
|
}
|
|
16989
17110
|
}
|
|
@@ -17249,7 +17370,7 @@ parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_set_t *cap
|
|
|
17249
17370
|
*/
|
|
17250
17371
|
static void
|
|
17251
17372
|
parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) {
|
|
17252
|
-
if (pm_static_literals_add(
|
|
17373
|
+
if (pm_static_literals_add(parser, keys, node, true) != NULL) {
|
|
17253
17374
|
pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE);
|
|
17254
17375
|
}
|
|
17255
17376
|
}
|
|
@@ -17297,7 +17418,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
|
|
|
17297
17418
|
}
|
|
17298
17419
|
|
|
17299
17420
|
pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
|
|
17300
|
-
|
|
17421
|
+
pm_node_list_append_inline(parser->arena, &assocs, assoc);
|
|
17301
17422
|
break;
|
|
17302
17423
|
}
|
|
17303
17424
|
}
|
|
@@ -17312,7 +17433,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
|
|
|
17312
17433
|
pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_START(first_node), PM_NODE_LENGTH(first_node)));
|
|
17313
17434
|
pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
|
|
17314
17435
|
|
|
17315
|
-
|
|
17436
|
+
pm_node_list_append_inline(parser->arena, &assocs, assoc);
|
|
17316
17437
|
break;
|
|
17317
17438
|
}
|
|
17318
17439
|
}
|
|
@@ -17340,7 +17461,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
|
|
|
17340
17461
|
rest = assoc;
|
|
17341
17462
|
} else {
|
|
17342
17463
|
pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
|
|
17343
|
-
|
|
17464
|
+
pm_node_list_append_inline(parser->arena, &assocs, assoc);
|
|
17344
17465
|
}
|
|
17345
17466
|
} else {
|
|
17346
17467
|
pm_node_t *key;
|
|
@@ -17386,7 +17507,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
|
|
|
17386
17507
|
pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
|
|
17387
17508
|
}
|
|
17388
17509
|
|
|
17389
|
-
|
|
17510
|
+
pm_node_list_append_inline(parser->arena, &assocs, assoc);
|
|
17390
17511
|
}
|
|
17391
17512
|
}
|
|
17392
17513
|
|
|
@@ -17666,7 +17787,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_
|
|
|
17666
17787
|
* are not captures of this pattern. A nested alternation is not visited
|
|
17667
17788
|
* either: its operands were already checked when it was parsed.
|
|
17668
17789
|
*/
|
|
17669
|
-
static
|
|
17790
|
+
static pm_visit_result_t
|
|
17670
17791
|
parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
|
|
17671
17792
|
pm_parser_t *parser = (pm_parser_t *) data;
|
|
17672
17793
|
|
|
@@ -17677,7 +17798,7 @@ parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
|
|
|
17677
17798
|
if (peek_at(parser, parser->start + PM_NODE_START(node)) != '_') {
|
|
17678
17799
|
pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
|
|
17679
17800
|
}
|
|
17680
|
-
return
|
|
17801
|
+
return PM_VISIT_SKIP;
|
|
17681
17802
|
case PM_ARRAY_PATTERN_NODE:
|
|
17682
17803
|
case PM_ASSOC_NODE:
|
|
17683
17804
|
case PM_ASSOC_SPLAT_NODE:
|
|
@@ -17687,9 +17808,9 @@ parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
|
|
|
17687
17808
|
case PM_IMPLICIT_NODE:
|
|
17688
17809
|
case PM_PARENTHESES_NODE:
|
|
17689
17810
|
case PM_SPLAT_NODE:
|
|
17690
|
-
return
|
|
17811
|
+
return PM_VISIT_DESCEND;
|
|
17691
17812
|
default:
|
|
17692
|
-
return
|
|
17813
|
+
return PM_VISIT_SKIP;
|
|
17693
17814
|
}
|
|
17694
17815
|
}
|
|
17695
17816
|
|
|
@@ -17885,7 +18006,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags
|
|
|
17885
18006
|
// or a find pattern. We need to parse all of the patterns, put them
|
|
17886
18007
|
// into a big list, and then determine which type of node we have.
|
|
17887
18008
|
pm_node_list_t nodes = { 0 };
|
|
17888
|
-
|
|
18009
|
+
pm_node_list_append_inline(parser->arena, &nodes, node);
|
|
17889
18010
|
|
|
17890
18011
|
// Gather up all of the patterns into the list.
|
|
17891
18012
|
while (accept1(parser, PM_TOKEN_COMMA)) {
|
|
@@ -17904,7 +18025,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags
|
|
|
17904
18025
|
}
|
|
17905
18026
|
|
|
17906
18027
|
node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
|
|
17907
|
-
|
|
18028
|
+
pm_node_list_append_inline(parser->arena, &nodes, node);
|
|
17908
18029
|
trailing_rest = true;
|
|
17909
18030
|
break;
|
|
17910
18031
|
}
|
|
@@ -17924,7 +18045,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags
|
|
|
17924
18045
|
node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
|
|
17925
18046
|
}
|
|
17926
18047
|
|
|
17927
|
-
|
|
18048
|
+
pm_node_list_append_inline(parser->arena, &nodes, node);
|
|
17928
18049
|
}
|
|
17929
18050
|
|
|
17930
18051
|
// If the first pattern and the last pattern are rest patterns, then we
|
|
@@ -18556,7 +18677,7 @@ parse_class(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
|
|
|
18556
18677
|
* Parse a method definition expression (the `def` keyword).
|
|
18557
18678
|
*/
|
|
18558
18679
|
static pm_node_t *
|
|
18559
|
-
parse_def(pm_parser_t *parser,
|
|
18680
|
+
parse_def(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
|
|
18560
18681
|
pm_node_list_t current_block_exits = { 0 };
|
|
18561
18682
|
pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
|
|
18562
18683
|
|
|
@@ -18806,14 +18927,9 @@ parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags,
|
|
|
18806
18927
|
pm_do_loop_stack_push(parser, false);
|
|
18807
18928
|
statements = UP(pm_statements_node_create(parser));
|
|
18808
18929
|
|
|
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
|
-
}
|
|
18930
|
+
/* The body accepts a command call (`def f = foo 1`) only where the
|
|
18931
|
+
* caller accepts an endless method with a command body. */
|
|
18932
|
+
uint8_t allow_flags = (flags & PM_PARSE_ACCEPTS_COMMAND_DEF) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
|
|
18817
18933
|
|
|
18818
18934
|
/* Inside a def body, we push true onto the accepts_block_stack so that
|
|
18819
18935
|
* `do` is lexed as PM_TOKEN_KEYWORD_DO (which can only start a block
|
|
@@ -18858,7 +18974,7 @@ parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags,
|
|
|
18858
18974
|
pm_accepts_block_stack_pop(parser);
|
|
18859
18975
|
|
|
18860
18976
|
/* A nested endless def whose body is a command call (e.g.,
|
|
18861
|
-
* `def f = def g = foo bar`) is a command
|
|
18977
|
+
* `def f = def g = foo bar`) is a command write and cannot appear
|
|
18862
18978
|
* as a def body. */
|
|
18863
18979
|
if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(parser, statement)) {
|
|
18864
18980
|
PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
|
|
@@ -19041,7 +19157,7 @@ parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
19041
19157
|
/* Otherwise, we're going to parse the first statement in the list of
|
|
19042
19158
|
* statements within the parentheses. */
|
|
19043
19159
|
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));
|
|
19160
|
+
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
19161
|
context_pop(parser);
|
|
19046
19162
|
|
|
19047
19163
|
/* Determine if this statement is followed by a terminator. In the case of a
|
|
@@ -19154,7 +19270,7 @@ parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
19154
19270
|
|
|
19155
19271
|
/* Parse each statement within the parentheses. */
|
|
19156
19272
|
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));
|
|
19273
|
+
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
19274
|
pm_statements_node_body_append(parser, statements, node, true);
|
|
19159
19275
|
|
|
19160
19276
|
/* If we're recovering from a syntax error, then we need to stop parsing
|
|
@@ -19435,11 +19551,11 @@ parse_heredoc(pm_parser_t *parser, uint16_t depth) {
|
|
|
19435
19551
|
// so we'll need to create an interpolated string node to hold
|
|
19436
19552
|
// them all.
|
|
19437
19553
|
pm_node_list_t parts = { 0 };
|
|
19438
|
-
|
|
19554
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
19439
19555
|
|
|
19440
19556
|
while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
|
|
19441
19557
|
if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
|
|
19442
|
-
|
|
19558
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
19443
19559
|
}
|
|
19444
19560
|
}
|
|
19445
19561
|
|
|
@@ -19670,17 +19786,17 @@ parse_lambda(pm_parser_t *parser, uint16_t depth) {
|
|
|
19670
19786
|
/* Restore the enclosing lambda's nesting now that the body has been
|
|
19671
19787
|
* parsed, so that the token following the closing `}` is lexed in the
|
|
19672
19788
|
* 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`. */
|
|
19789
|
+
* level, which every token inside the braces sits above. */
|
|
19675
19790
|
parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
|
|
19676
19791
|
expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
|
|
19677
19792
|
} else {
|
|
19678
|
-
/* A `-> { }` body is delimited by `{`/`}`, whose
|
|
19679
|
-
*
|
|
19680
|
-
*
|
|
19681
|
-
*
|
|
19682
|
-
*
|
|
19683
|
-
|
|
19793
|
+
/* A `-> { }` body is delimited by `{`/`}`, whose enclosure frame the
|
|
19794
|
+
* lexer manages. A `-> do end` body is delimited by keywords, so push
|
|
19795
|
+
* the frame here and pop it before `end`. The frame isolates the body
|
|
19796
|
+
* from an enclosing `while`/`until`/`for` condition, so a `do` inside
|
|
19797
|
+
* it opens a block rather than the loop body. The push must precede
|
|
19798
|
+
* consuming the `do`, which lexes the first token of the body. */
|
|
19799
|
+
pm_enclosure_frame_push(parser);
|
|
19684
19800
|
expect1(parser, PM_TOKEN_KEYWORD_DO_LAMBDA, PM_ERR_LAMBDA_OPEN);
|
|
19685
19801
|
opening = parser->previous;
|
|
19686
19802
|
|
|
@@ -19701,7 +19817,7 @@ parse_lambda(pm_parser_t *parser, uint16_t depth) {
|
|
|
19701
19817
|
parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
|
|
19702
19818
|
}
|
|
19703
19819
|
|
|
19704
|
-
|
|
19820
|
+
pm_enclosure_frame_pop(parser);
|
|
19705
19821
|
|
|
19706
19822
|
/* As with the brace branch above, restore the nesting before consuming
|
|
19707
19823
|
* the closing `end`, which lexes the token that follows it. */
|
|
@@ -20040,7 +20156,7 @@ parse_identifier(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
20040
20156
|
// can still be a method call if it is followed by arguments or
|
|
20041
20157
|
// a block, so we need to check for that here.
|
|
20042
20158
|
if (
|
|
20043
|
-
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (
|
|
20159
|
+
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
|
|
20044
20160
|
(pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
|
|
20045
20161
|
match1(parser, PM_TOKEN_BRACE_LEFT)
|
|
20046
20162
|
) {
|
|
@@ -20497,7 +20613,14 @@ parse_jump(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
|
|
|
20497
20613
|
|
|
20498
20614
|
if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
|
|
20499
20615
|
pm_token_t next = parser->current;
|
|
20616
|
+
|
|
20617
|
+
/* The arguments of a jump start a fresh command context, so a
|
|
20618
|
+
* `do` inside them binds to the nearest call within the
|
|
20619
|
+
* arguments rather than to an enclosing command call, as in
|
|
20620
|
+
* `check 1, case x when 1 then return bar do end end`. */
|
|
20621
|
+
pm_accepts_block_stack_push(parser, true);
|
|
20500
20622
|
parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
|
|
20623
|
+
pm_accepts_block_stack_pop(parser);
|
|
20501
20624
|
|
|
20502
20625
|
// Reject `foo && return bar`.
|
|
20503
20626
|
if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) {
|
|
@@ -20590,7 +20713,16 @@ parse_not(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags,
|
|
|
20590
20713
|
}
|
|
20591
20714
|
}
|
|
20592
20715
|
} else {
|
|
20593
|
-
|
|
20716
|
+
/* The operand of `not` may be a command call, except at the start of
|
|
20717
|
+
* an endless method body, where it may be one only if the body may
|
|
20718
|
+
* be: `def f = not foo 1` has a command body, so it is rejected where
|
|
20719
|
+
* that is not accepted (`if def f = not foo 1; end`). A nested `not`
|
|
20720
|
+
* is still at the start of the body, so the operand carries the
|
|
20721
|
+
* endless method context on (`def f = not not foo 1`). */
|
|
20722
|
+
uint8_t operand_flags = (flags & PM_PARSE_IN_ENDLESS_DEF)
|
|
20723
|
+
? (uint8_t) (flags & (PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_IN_ENDLESS_DEF))
|
|
20724
|
+
: PM_PARSE_ACCEPTS_COMMAND_CALL;
|
|
20725
|
+
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
20726
|
}
|
|
20595
20727
|
|
|
20596
20728
|
return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
|
|
@@ -20736,7 +20868,7 @@ parse_constant(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t fl
|
|
|
20736
20868
|
* fact a method call, not a constant read. */
|
|
20737
20869
|
if (
|
|
20738
20870
|
match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
|
|
20739
|
-
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (
|
|
20871
|
+
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
|
|
20740
20872
|
(pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
|
|
20741
20873
|
match1(parser, PM_TOKEN_BRACE_LEFT)
|
|
20742
20874
|
) {
|
|
@@ -20996,7 +21128,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, u
|
|
|
20996
21128
|
case PM_TOKEN_KEYWORD_CLASS:
|
|
20997
21129
|
return parse_class(parser, flags, depth);
|
|
20998
21130
|
case PM_TOKEN_KEYWORD_DEF:
|
|
20999
|
-
return parse_def(parser,
|
|
21131
|
+
return parse_def(parser, flags, depth);
|
|
21000
21132
|
case PM_TOKEN_KEYWORD_DEFINED:
|
|
21001
21133
|
return UP(parse_defined(parser, flags, depth));
|
|
21002
21134
|
case PM_TOKEN_KEYWORD_END_UPCASE:
|
|
@@ -21138,6 +21270,18 @@ parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t de
|
|
|
21138
21270
|
}
|
|
21139
21271
|
}
|
|
21140
21272
|
|
|
21273
|
+
/**
|
|
21274
|
+
* A command is a complete value of a write: it cannot begin a multi-value
|
|
21275
|
+
* right-hand side (`x = Foo::Bar { }, y`), nor stand as the first element of
|
|
21276
|
+
* one when nested in another write (`x = y = def f = foo 1 rescue z, w`).
|
|
21277
|
+
*/
|
|
21278
|
+
static void
|
|
21279
|
+
parse_write_value_comma_check(pm_parser_t *parser, const pm_node_t *value) {
|
|
21280
|
+
if (match1(parser, PM_TOKEN_COMMA) && pm_command_before_comma_p(parser, value)) {
|
|
21281
|
+
pm_parser_err_current(parser, PM_ERR_INVALID_COMMA);
|
|
21282
|
+
}
|
|
21283
|
+
}
|
|
21284
|
+
|
|
21141
21285
|
/**
|
|
21142
21286
|
* Parse a value that is going to be written to some kind of variable or method
|
|
21143
21287
|
* call. We need to handle this separately because the rescue modifier is
|
|
@@ -21149,7 +21293,21 @@ parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t de
|
|
|
21149
21293
|
*/
|
|
21150
21294
|
static pm_node_t *
|
|
21151
21295
|
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
|
-
|
|
21296
|
+
/* Where an operator write accepts a command call it also accepts an
|
|
21297
|
+
* endless method with a command body (`x += def f = foo 1`). */
|
|
21298
|
+
uint8_t command_flags = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
|
|
21299
|
+
? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
|
|
21300
|
+
: (previous_binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
|
|
21301
|
+
if (command_flags) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
|
|
21302
|
+
|
|
21303
|
+
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));
|
|
21304
|
+
|
|
21305
|
+
/* An operator write never takes a multi-value right-hand side, so a comma
|
|
21306
|
+
* after a statement-level one is already reported by the statement that
|
|
21307
|
+
* contains it. */
|
|
21308
|
+
if (previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
|
|
21309
|
+
parse_write_value_comma_check(parser, value);
|
|
21310
|
+
}
|
|
21153
21311
|
|
|
21154
21312
|
// Assignments whose value is a command call (e.g., a = b c) can only
|
|
21155
21313
|
// be followed by modifiers (if/unless/while/until/rescue) and not by
|
|
@@ -21253,12 +21411,21 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding
|
|
|
21253
21411
|
? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
|
|
21254
21412
|
: ((previous_binding_power < PM_BINDING_POWER_MODIFIER || statement_level) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
|
|
21255
21413
|
|
|
21414
|
+
/* Where a single-target write accepts a command call it also accepts an
|
|
21415
|
+
* endless method with a command body (`x = def f = foo 1`), but a
|
|
21416
|
+
* multi-write accepts only the command call. */
|
|
21417
|
+
if (command_call_flag && binding_power != PM_BINDING_POWER_MULTI_ASSIGNMENT + 1) {
|
|
21418
|
+
command_call_flag |= PM_PARSE_ACCEPTS_COMMAND_DEF;
|
|
21419
|
+
}
|
|
21420
|
+
|
|
21256
21421
|
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
21422
|
if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
|
|
21258
21423
|
|
|
21259
21424
|
parse_assignment_value_local(parser, value);
|
|
21260
21425
|
bool single_value = true;
|
|
21261
21426
|
|
|
21427
|
+
parse_write_value_comma_check(parser, value);
|
|
21428
|
+
|
|
21262
21429
|
// Block calls (command call + do block, e.g., `foo bar do end`) cannot
|
|
21263
21430
|
// be followed by a comma to form a multi-value RHS because each element
|
|
21264
21431
|
// of a multi-value assignment must be an `arg`, not a `block_call`.
|
|
@@ -21360,7 +21527,7 @@ parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement,
|
|
|
21360
21527
|
// splat right-hand side (`b = c, d` / `b = *c`); a comma _before_ an
|
|
21361
21528
|
// `=`, or a parenthesized target list (`(b, c), d = 1`), instead
|
|
21362
21529
|
// 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));
|
|
21530
|
+
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
21531
|
multiple = match1(parser, PM_TOKEN_COMMA) || PM_NODE_TYPE_P(value, PM_MULTI_TARGET_NODE);
|
|
21365
21532
|
}
|
|
21366
21533
|
|
|
@@ -21630,7 +21797,7 @@ parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *c
|
|
|
21630
21797
|
* Add this name to the set of constants if it is valid, not duplicated,
|
|
21631
21798
|
* and not a keyword.
|
|
21632
21799
|
*/
|
|
21633
|
-
if (name != 0 && pm_constant_id_set_insert(parser->
|
|
21800
|
+
if (name != 0 && pm_constant_id_set_insert(&parser->scratch_arena, names, name)) {
|
|
21634
21801
|
|
|
21635
21802
|
int depth;
|
|
21636
21803
|
if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
|
|
@@ -21657,7 +21824,7 @@ parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *c
|
|
|
21657
21824
|
pm_token_t token = { .type = 0, .start = start, .end = end };
|
|
21658
21825
|
pm_location_t token_loc = TOK2LOC(parser, &token);
|
|
21659
21826
|
pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &token_loc, name, depth == -1 ? 0 : (uint32_t) depth));
|
|
21660
|
-
|
|
21827
|
+
pm_node_list_append_inline(parser->arena, &name_data->match->targets, target);
|
|
21661
21828
|
}
|
|
21662
21829
|
|
|
21663
21830
|
pm_buffer_cleanup(&unescaped);
|
|
@@ -21735,7 +21902,7 @@ parse_constant_path_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power
|
|
|
21735
21902
|
|
|
21736
21903
|
if (
|
|
21737
21904
|
(parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
|
|
21738
|
-
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (
|
|
21905
|
+
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type))
|
|
21739
21906
|
) {
|
|
21740
21907
|
/* If we have a constant immediately following a '::' operator,
|
|
21741
21908
|
* then this can either be a constant path or a method call,
|
|
@@ -21826,9 +21993,7 @@ parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_pow
|
|
|
21826
21993
|
}
|
|
21827
21994
|
|
|
21828
21995
|
if (!interpolated && total_length > 0) {
|
|
21829
|
-
|
|
21830
|
-
if (!memory) abort();
|
|
21831
|
-
|
|
21996
|
+
uint8_t *memory = (uint8_t *) pm_arena_alloc(&parser->scratch_arena, total_length, PRISM_ALIGNOF(uint8_t));
|
|
21832
21997
|
uint8_t *cursor = memory;
|
|
21833
21998
|
PM_NODE_LIST_FOREACH(parts, index, part) {
|
|
21834
21999
|
pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
|
|
@@ -21838,11 +22003,9 @@ parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_pow
|
|
|
21838
22003
|
cursor += length;
|
|
21839
22004
|
}
|
|
21840
22005
|
|
|
21841
|
-
pm_string_t
|
|
21842
|
-
|
|
21843
|
-
|
|
21844
|
-
result = parse_interpolated_regular_expression_named_captures(parser, &owned, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
|
|
21845
|
-
pm_string_cleanup(&owned);
|
|
22006
|
+
pm_string_t content;
|
|
22007
|
+
pm_string_init(&content, memory, total_length);
|
|
22008
|
+
result = parse_interpolated_regular_expression_named_captures(parser, &content, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
|
|
21846
22009
|
}
|
|
21847
22010
|
} else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
|
|
21848
22011
|
/* If we have a regular expression node, then we can parse the named
|
|
@@ -21852,7 +22015,7 @@ parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_pow
|
|
|
21852
22015
|
.call = call,
|
|
21853
22016
|
.match = NULL,
|
|
21854
22017
|
.names = { 0 },
|
|
21855
|
-
.shared = regexp->unescaped
|
|
22018
|
+
.shared = pm_string_source(®exp->unescaped) == parser->start + regexp->content_loc.start,
|
|
21856
22019
|
};
|
|
21857
22020
|
|
|
21858
22021
|
pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data));
|
|
@@ -22709,7 +22872,7 @@ parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) {
|
|
|
22709
22872
|
return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
|
|
22710
22873
|
case PM_DEF_NODE:
|
|
22711
22874
|
// An endless method whose body is a command-style call (e.g.,
|
|
22712
|
-
// `def f = foo bar`) is a command
|
|
22875
|
+
// `def f = foo bar`) is a command write and can only be
|
|
22713
22876
|
// followed by modifiers.
|
|
22714
22877
|
return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(parser, node);
|
|
22715
22878
|
case PM_RESCUE_MODIFIER_NODE:
|
|
@@ -22864,7 +23027,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
22864
23027
|
cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
|
|
22865
23028
|
)
|
|
22866
23029
|
) {
|
|
22867
|
-
flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
|
|
23030
|
+
flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
|
|
22868
23031
|
}
|
|
22869
23032
|
break;
|
|
22870
23033
|
}
|
|
@@ -22872,7 +23035,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
22872
23035
|
case PM_CONSTANT_PATH_NODE:
|
|
22873
23036
|
break;
|
|
22874
23037
|
default:
|
|
22875
|
-
flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
|
|
23038
|
+
flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
|
|
22876
23039
|
break;
|
|
22877
23040
|
}
|
|
22878
23041
|
}
|
|
@@ -23091,6 +23254,16 @@ pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const c
|
|
|
23091
23254
|
parser->frozen_string_literal = next_options.frozen_string_literal;
|
|
23092
23255
|
}
|
|
23093
23256
|
|
|
23257
|
+
/**
|
|
23258
|
+
* The smallest capacity the constant pool starts with, whatever the source
|
|
23259
|
+
* size suggests. An embedder that interns a fixed set of names into the pool
|
|
23260
|
+
* at every parse (a symbol table) can raise it to that set's size, so the
|
|
23261
|
+
* pool does not grow to hold them and leave each outgrown array in the arena.
|
|
23262
|
+
*/
|
|
23263
|
+
#ifndef PM_CONSTANT_POOL_MIN_CAPACITY
|
|
23264
|
+
#define PM_CONSTANT_POOL_MIN_CAPACITY 4
|
|
23265
|
+
#endif
|
|
23266
|
+
|
|
23094
23267
|
/**
|
|
23095
23268
|
* Initialize a parser with the given start and end pointers.
|
|
23096
23269
|
*/
|
|
@@ -23110,9 +23283,8 @@ pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, si
|
|
|
23110
23283
|
.do_loop_stack = 0,
|
|
23111
23284
|
.accepts_block_stack = 0,
|
|
23112
23285
|
.lex_modes = {
|
|
23113
|
-
.
|
|
23114
|
-
.
|
|
23115
|
-
.current = &parser->lex_modes.stack[0],
|
|
23286
|
+
.current = &parser->lex_modes.base,
|
|
23287
|
+
.base = { .mode = PM_LEX_DEFAULT },
|
|
23116
23288
|
},
|
|
23117
23289
|
.start = source,
|
|
23118
23290
|
.end = source + size,
|
|
@@ -23169,7 +23341,8 @@ pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, si
|
|
|
23169
23341
|
* frequency. Resizes are cheap with arena allocation, so we lean toward
|
|
23170
23342
|
* under-estimating. */
|
|
23171
23343
|
uint32_t constant_size = ((uint32_t) size) / 120;
|
|
23172
|
-
|
|
23344
|
+
if (constant_size < PM_CONSTANT_POOL_MIN_CAPACITY) constant_size = PM_CONSTANT_POOL_MIN_CAPACITY;
|
|
23345
|
+
pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size);
|
|
23173
23346
|
|
|
23174
23347
|
/* Initialize the line offset list. Similar to the constant pool, we are
|
|
23175
23348
|
* going to estimate the number of newlines that we will need based on the
|
|
@@ -23378,20 +23551,8 @@ pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_op
|
|
|
23378
23551
|
*/
|
|
23379
23552
|
void
|
|
23380
23553
|
pm_parser_cleanup(pm_parser_t *parser) {
|
|
23381
|
-
pm_string_cleanup(&parser->filepath);
|
|
23382
23554
|
pm_arena_cleanup(&parser->metadata_arena);
|
|
23383
|
-
|
|
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
|
-
while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
|
|
23393
|
-
lex_mode_pop(parser);
|
|
23394
|
-
}
|
|
23555
|
+
pm_arena_cleanup(&parser->scratch_arena);
|
|
23395
23556
|
}
|
|
23396
23557
|
|
|
23397
23558
|
/**
|
|
@@ -23573,6 +23734,7 @@ pm_parse(pm_parser_t *parser) {
|
|
|
23573
23734
|
pm_node_t *node = parse_program(parser);
|
|
23574
23735
|
pm_parse_continuable(parser);
|
|
23575
23736
|
if (parser->attach_comments) pm_comments_attach(parser, node);
|
|
23737
|
+
pm_arena_cleanup(&parser->scratch_arena);
|
|
23576
23738
|
return node;
|
|
23577
23739
|
}
|
|
23578
23740
|
|
|
@@ -23728,6 +23890,7 @@ pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t s
|
|
|
23728
23890
|
pm_serialize_header(buffer);
|
|
23729
23891
|
pm_serialize_encoding(parser.encoding, buffer);
|
|
23730
23892
|
pm_buffer_append_varsint(buffer, parser.start_line);
|
|
23893
|
+
pm_buffer_append_uint64(buffer, pm_parser_checksum(&parser));
|
|
23731
23894
|
pm_serialize_line_offset_list(&parser.line_offsets, buffer);
|
|
23732
23895
|
pm_serialize_comment_list(&parser.comment_list, buffer);
|
|
23733
23896
|
|