prism 2.0.0.rc1 → 2.0.0
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 +13 -2
- data/CODE_OF_CONDUCT.md +1 -1
- data/LICENSE.md +1 -1
- 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/node.h +8 -4
- data/include/prism/internal/parser.h +20 -0
- 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 +9 -1
- data/include/prism.h +0 -8
- data/include/prism.hpp +24 -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 +2567 -3286
- 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/buffer.c +9 -0
- data/src/comments.c +2 -2
- data/src/diagnostic.c +8 -0
- data/src/node.c +216 -368
- data/src/options.c +7 -13
- data/src/parser.c +70 -48
- data/src/prism.c +581 -419
- data/src/regexp.c +0 -1
- data/src/serialize.c +2301 -1950
- data/src/source.c +8 -2
- 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
|
*/
|
|
@@ -700,17 +683,38 @@ pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size
|
|
|
700
683
|
/* Scope-related functions */
|
|
701
684
|
/******************************************************************************/
|
|
702
685
|
|
|
686
|
+
/**
|
|
687
|
+
* The largest locals table that a popped scope hands to the next pushed scope.
|
|
688
|
+
*/
|
|
689
|
+
#define PM_LOCALS_REUSE_CAPACITY 16
|
|
690
|
+
|
|
703
691
|
/**
|
|
704
692
|
* Allocate and initialize a new scope. Push it onto the scope stack.
|
|
705
693
|
*/
|
|
706
|
-
static
|
|
694
|
+
static void
|
|
707
695
|
pm_parser_scope_push(pm_parser_t *parser, bool closed) {
|
|
708
|
-
pm_scope_t *scope =
|
|
709
|
-
|
|
696
|
+
pm_scope_t *scope = parser->free_scopes;
|
|
697
|
+
pm_locals_t locals = { 0 };
|
|
698
|
+
|
|
699
|
+
if (scope != NULL) {
|
|
700
|
+
parser->free_scopes = scope->previous;
|
|
701
|
+
|
|
702
|
+
/* Reuse the locals table of the popped scope when it is small, so that
|
|
703
|
+
* most scopes allocate no locals storage at all. Large tables are
|
|
704
|
+
* dropped instead, because every scope that reuses a table pays to
|
|
705
|
+
* clear it and to scan its full capacity when the locals are ordered. */
|
|
706
|
+
if (scope->locals.capacity > 0 && scope->locals.capacity <= PM_LOCALS_REUSE_CAPACITY) {
|
|
707
|
+
locals.capacity = scope->locals.capacity;
|
|
708
|
+
locals.locals = scope->locals.locals;
|
|
709
|
+
memset(locals.locals, 0, locals.capacity * sizeof(pm_local_t));
|
|
710
|
+
}
|
|
711
|
+
} else {
|
|
712
|
+
scope = (pm_scope_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_scope_t), PRISM_ALIGNOF(pm_scope_t));
|
|
713
|
+
}
|
|
710
714
|
|
|
711
715
|
*scope = (pm_scope_t) {
|
|
712
716
|
.previous = parser->current_scope,
|
|
713
|
-
.locals =
|
|
717
|
+
.locals = locals,
|
|
714
718
|
.parameters = PM_SCOPE_PARAMETERS_NONE,
|
|
715
719
|
.implicit_parameters = { 0 },
|
|
716
720
|
.shareable_constant = parser->current_scope == NULL ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant,
|
|
@@ -718,7 +722,6 @@ pm_parser_scope_push(pm_parser_t *parser, bool closed) {
|
|
|
718
722
|
};
|
|
719
723
|
|
|
720
724
|
parser->current_scope = scope;
|
|
721
|
-
return true;
|
|
722
725
|
}
|
|
723
726
|
|
|
724
727
|
/**
|
|
@@ -876,24 +879,16 @@ pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constan
|
|
|
876
879
|
*/
|
|
877
880
|
#define PM_LOCALS_HASH_THRESHOLD 5
|
|
878
881
|
|
|
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
882
|
/**
|
|
887
883
|
* Resize the locals list to be twice its current size. If the next capacity is
|
|
888
884
|
* above the threshold for switching to a hash, then we'll switch to a hash.
|
|
889
885
|
*/
|
|
890
886
|
static void
|
|
891
|
-
pm_locals_resize(pm_locals_t *locals) {
|
|
887
|
+
pm_locals_resize(pm_arena_t *arena, pm_locals_t *locals) {
|
|
892
888
|
uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2);
|
|
893
889
|
assert(next_capacity > locals->capacity);
|
|
894
890
|
|
|
895
|
-
pm_local_t *next_locals =
|
|
896
|
-
if (next_locals == NULL) abort();
|
|
891
|
+
pm_local_t *next_locals = (pm_local_t *) pm_arena_zalloc(arena, next_capacity * sizeof(pm_local_t), PRISM_ALIGNOF(pm_local_t));
|
|
897
892
|
|
|
898
893
|
if (next_capacity < PM_LOCALS_HASH_THRESHOLD) {
|
|
899
894
|
if (locals->size > 0) {
|
|
@@ -918,7 +913,6 @@ pm_locals_resize(pm_locals_t *locals) {
|
|
|
918
913
|
}
|
|
919
914
|
}
|
|
920
915
|
|
|
921
|
-
pm_locals_free(locals);
|
|
922
916
|
locals->locals = next_locals;
|
|
923
917
|
locals->capacity = next_capacity;
|
|
924
918
|
}
|
|
@@ -939,9 +933,9 @@ pm_locals_resize(pm_locals_t *locals) {
|
|
|
939
933
|
* @return True if the local was added, and false if the local already exists.
|
|
940
934
|
*/
|
|
941
935
|
static bool
|
|
942
|
-
pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint32_t length, uint32_t reads) {
|
|
936
|
+
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
937
|
if (locals->size >= (locals->capacity / 4 * 3)) {
|
|
944
|
-
pm_locals_resize(locals);
|
|
938
|
+
pm_locals_resize(arena, locals);
|
|
945
939
|
}
|
|
946
940
|
|
|
947
941
|
locals->bloom |= (1u << (name & 31));
|
|
@@ -2441,7 +2435,7 @@ pm_arguments_node_arguments_append(pm_arena_t *arena, pm_arguments_node_t *node,
|
|
|
2441
2435
|
PM_NODE_LENGTH_SET_NODE(node, argument);
|
|
2442
2436
|
}
|
|
2443
2437
|
|
|
2444
|
-
|
|
2438
|
+
pm_node_list_append_inline(arena, &node->arguments, argument);
|
|
2445
2439
|
|
|
2446
2440
|
if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) {
|
|
2447
2441
|
if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
|
|
@@ -2489,7 +2483,7 @@ pm_array_node_elements_append(pm_arena_t *arena, pm_array_node_t *node, pm_node_
|
|
|
2489
2483
|
PM_NODE_START_SET_NODE(node, element);
|
|
2490
2484
|
}
|
|
2491
2485
|
|
|
2492
|
-
|
|
2486
|
+
pm_node_list_append_inline(arena, &node->elements, element);
|
|
2493
2487
|
PM_NODE_LENGTH_SET_NODE(node, element);
|
|
2494
2488
|
|
|
2495
2489
|
// If the element is not a static literal, then the array is not a static
|
|
@@ -2542,9 +2536,9 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node
|
|
|
2542
2536
|
node->rest = child;
|
|
2543
2537
|
found_rest = true;
|
|
2544
2538
|
} else if (found_rest) {
|
|
2545
|
-
|
|
2539
|
+
pm_node_list_append_inline(parser->arena, &node->posts, child);
|
|
2546
2540
|
} else {
|
|
2547
|
-
|
|
2541
|
+
pm_node_list_append_inline(parser->arena, &node->requireds, child);
|
|
2548
2542
|
}
|
|
2549
2543
|
}
|
|
2550
2544
|
|
|
@@ -2612,7 +2606,7 @@ pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *openin
|
|
|
2612
2606
|
|
|
2613
2607
|
static PRISM_INLINE void
|
|
2614
2608
|
pm_array_pattern_node_requireds_append(pm_arena_t *arena, pm_array_pattern_node_t *node, pm_node_t *inner) {
|
|
2615
|
-
|
|
2609
|
+
pm_node_list_append_inline(arena, &node->requireds, inner);
|
|
2616
2610
|
}
|
|
2617
2611
|
|
|
2618
2612
|
/**
|
|
@@ -2876,7 +2870,7 @@ pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name)
|
|
|
2876
2870
|
*/
|
|
2877
2871
|
static void
|
|
2878
2872
|
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
|
-
|
|
2873
|
+
pm_node_list_append_inline(arena, &node->locals, UP(local));
|
|
2880
2874
|
|
|
2881
2875
|
if (PM_NODE_LENGTH(node) == 0) {
|
|
2882
2876
|
PM_NODE_START_SET_NODE(node, local);
|
|
@@ -3529,7 +3523,7 @@ static void
|
|
|
3529
3523
|
pm_case_node_condition_append(pm_arena_t *arena, pm_case_node_t *node, pm_node_t *condition) {
|
|
3530
3524
|
assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
|
|
3531
3525
|
|
|
3532
|
-
|
|
3526
|
+
pm_node_list_append_inline(arena, &node->conditions, condition);
|
|
3533
3527
|
PM_NODE_LENGTH_SET_NODE(node, condition);
|
|
3534
3528
|
}
|
|
3535
3529
|
|
|
@@ -3576,7 +3570,7 @@ static void
|
|
|
3576
3570
|
pm_case_match_node_condition_append(pm_arena_t *arena, pm_case_match_node_t *node, pm_node_t *condition) {
|
|
3577
3571
|
assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
|
|
3578
3572
|
|
|
3579
|
-
|
|
3573
|
+
pm_node_list_append_inline(arena, &node->conditions, condition);
|
|
3580
3574
|
PM_NODE_LENGTH_SET_NODE(node, condition);
|
|
3581
3575
|
}
|
|
3582
3576
|
|
|
@@ -4116,7 +4110,7 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) {
|
|
|
4116
4110
|
// much more efficient, as we could instead resize the node list to only point
|
|
4117
4111
|
// to 1...-1.
|
|
4118
4112
|
for (size_t index = 1; index < nodes->size - 1; index++) {
|
|
4119
|
-
|
|
4113
|
+
pm_node_list_append_inline(parser->arena, &node->requireds, nodes->nodes[index]);
|
|
4120
4114
|
}
|
|
4121
4115
|
|
|
4122
4116
|
return node;
|
|
@@ -4613,7 +4607,7 @@ pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) {
|
|
|
4613
4607
|
*/
|
|
4614
4608
|
static PRISM_INLINE void
|
|
4615
4609
|
pm_hash_node_elements_append(pm_arena_t *arena, pm_hash_node_t *hash, pm_node_t *element) {
|
|
4616
|
-
|
|
4610
|
+
pm_node_list_append_inline(arena, &hash->elements, element);
|
|
4617
4611
|
|
|
4618
4612
|
bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE);
|
|
4619
4613
|
if (static_literal) {
|
|
@@ -5038,7 +5032,7 @@ pm_interpolated_node_append(pm_arena_t *arena, pm_node_t *node, pm_node_list_t *
|
|
|
5038
5032
|
break;
|
|
5039
5033
|
}
|
|
5040
5034
|
|
|
5041
|
-
|
|
5035
|
+
pm_node_list_append_inline(arena, parts, part);
|
|
5042
5036
|
}
|
|
5043
5037
|
|
|
5044
5038
|
/**
|
|
@@ -5180,7 +5174,7 @@ pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_n
|
|
|
5180
5174
|
// These will only happen in error cases. But we want to handle it
|
|
5181
5175
|
// here so that we don't fail the assertion.
|
|
5182
5176
|
CLEAR_FLAGS(node);
|
|
5183
|
-
|
|
5177
|
+
pm_node_list_append_inline(arena, &node->parts, UP(pm_error_recovery_node_create_unexpected(parser, part)));
|
|
5184
5178
|
return;
|
|
5185
5179
|
case PM_ERROR_RECOVERY_NODE:
|
|
5186
5180
|
CLEAR_FLAGS(node);
|
|
@@ -5190,7 +5184,7 @@ pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_n
|
|
|
5190
5184
|
break;
|
|
5191
5185
|
}
|
|
5192
5186
|
|
|
5193
|
-
|
|
5187
|
+
pm_node_list_append_inline(arena, &node->parts, part);
|
|
5194
5188
|
|
|
5195
5189
|
#undef CLEAR_FLAGS
|
|
5196
5190
|
#undef MUTABLE_FLAGS
|
|
@@ -5370,7 +5364,7 @@ pm_keyword_hash_node_elements_append(pm_arena_t *arena, pm_keyword_hash_node_t *
|
|
|
5370
5364
|
pm_node_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS);
|
|
5371
5365
|
}
|
|
5372
5366
|
|
|
5373
|
-
|
|
5367
|
+
pm_node_list_append_inline(arena, &hash->elements, element);
|
|
5374
5368
|
if (PM_NODE_LENGTH(hash) == 0) {
|
|
5375
5369
|
PM_NODE_START_SET_NODE(hash, element);
|
|
5376
5370
|
}
|
|
@@ -5718,19 +5712,19 @@ pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t
|
|
|
5718
5712
|
node->rest = target;
|
|
5719
5713
|
} else {
|
|
5720
5714
|
pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
|
|
5721
|
-
|
|
5715
|
+
pm_node_list_append_inline(parser->arena, &node->rights, target);
|
|
5722
5716
|
}
|
|
5723
5717
|
} else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
|
|
5724
5718
|
if (node->rest == NULL) {
|
|
5725
5719
|
node->rest = target;
|
|
5726
5720
|
} else {
|
|
5727
5721
|
PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
|
|
5728
|
-
|
|
5722
|
+
pm_node_list_append_inline(parser->arena, &node->rights, target);
|
|
5729
5723
|
}
|
|
5730
5724
|
} else if (node->rest == NULL) {
|
|
5731
|
-
|
|
5725
|
+
pm_node_list_append_inline(parser->arena, &node->lefts, target);
|
|
5732
5726
|
} else {
|
|
5733
|
-
|
|
5727
|
+
pm_node_list_append_inline(parser->arena, &node->rights, target);
|
|
5734
5728
|
}
|
|
5735
5729
|
|
|
5736
5730
|
if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_START(node) > PM_NODE_START(target))) {
|
|
@@ -6005,7 +5999,7 @@ pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param)
|
|
|
6005
5999
|
static void
|
|
6006
6000
|
pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
|
|
6007
6001
|
pm_parameters_node_location_set(params, param);
|
|
6008
|
-
|
|
6002
|
+
pm_node_list_append_inline(arena, ¶ms->requireds, param);
|
|
6009
6003
|
}
|
|
6010
6004
|
|
|
6011
6005
|
/**
|
|
@@ -6014,7 +6008,7 @@ pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *par
|
|
|
6014
6008
|
static void
|
|
6015
6009
|
pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_optional_parameter_node_t *param) {
|
|
6016
6010
|
pm_parameters_node_location_set(params, UP(param));
|
|
6017
|
-
|
|
6011
|
+
pm_node_list_append_inline(arena, ¶ms->optionals, UP(param));
|
|
6018
6012
|
}
|
|
6019
6013
|
|
|
6020
6014
|
/**
|
|
@@ -6023,7 +6017,7 @@ pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *par
|
|
|
6023
6017
|
static void
|
|
6024
6018
|
pm_parameters_node_posts_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
|
|
6025
6019
|
pm_parameters_node_location_set(params, param);
|
|
6026
|
-
|
|
6020
|
+
pm_node_list_append_inline(arena, ¶ms->posts, param);
|
|
6027
6021
|
}
|
|
6028
6022
|
|
|
6029
6023
|
/**
|
|
@@ -6041,7 +6035,7 @@ pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
|
|
|
6041
6035
|
static void
|
|
6042
6036
|
pm_parameters_node_keywords_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
|
|
6043
6037
|
pm_parameters_node_location_set(params, param);
|
|
6044
|
-
|
|
6038
|
+
pm_node_list_append_inline(arena, ¶ms->keywords, param);
|
|
6045
6039
|
}
|
|
6046
6040
|
|
|
6047
6041
|
/**
|
|
@@ -6329,7 +6323,7 @@ pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subseque
|
|
|
6329
6323
|
*/
|
|
6330
6324
|
static void
|
|
6331
6325
|
pm_rescue_node_exceptions_append(pm_arena_t *arena, pm_rescue_node_t *node, pm_node_t *exception) {
|
|
6332
|
-
|
|
6326
|
+
pm_node_list_append_inline(arena, &node->exceptions, exception);
|
|
6333
6327
|
PM_NODE_LENGTH_SET_NODE(node, exception);
|
|
6334
6328
|
}
|
|
6335
6329
|
|
|
@@ -6562,7 +6556,7 @@ pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node,
|
|
|
6562
6556
|
}
|
|
6563
6557
|
}
|
|
6564
6558
|
|
|
6565
|
-
|
|
6559
|
+
pm_node_list_append_inline(parser->arena, &node->body, statement);
|
|
6566
6560
|
if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
|
|
6567
6561
|
}
|
|
6568
6562
|
|
|
@@ -6809,7 +6803,7 @@ pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
|
|
|
6809
6803
|
((pm_string_t) { 0 })
|
|
6810
6804
|
);
|
|
6811
6805
|
|
|
6812
|
-
|
|
6806
|
+
pm_string_init(&node->unescaped, (const uint8_t *) content, strlen(content));
|
|
6813
6807
|
return node;
|
|
6814
6808
|
}
|
|
6815
6809
|
|
|
@@ -6952,7 +6946,7 @@ pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
|
|
|
6952
6946
|
static void
|
|
6953
6947
|
pm_undef_node_append(pm_arena_t *arena, pm_undef_node_t *node, pm_node_t *name) {
|
|
6954
6948
|
PM_NODE_LENGTH_SET_NODE(node, name);
|
|
6955
|
-
|
|
6949
|
+
pm_node_list_append_inline(arena, &node->names, name);
|
|
6956
6950
|
}
|
|
6957
6951
|
|
|
6958
6952
|
/**
|
|
@@ -7094,7 +7088,7 @@ pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
|
|
|
7094
7088
|
static void
|
|
7095
7089
|
pm_when_node_conditions_append(pm_arena_t *arena, pm_when_node_t *node, pm_node_t *condition) {
|
|
7096
7090
|
PM_NODE_LENGTH_SET_NODE(node, condition);
|
|
7097
|
-
|
|
7091
|
+
pm_node_list_append_inline(arena, &node->conditions, condition);
|
|
7098
7092
|
}
|
|
7099
7093
|
|
|
7100
7094
|
/**
|
|
@@ -7268,7 +7262,7 @@ pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
|
|
|
7268
7262
|
*/
|
|
7269
7263
|
static PRISM_INLINE void
|
|
7270
7264
|
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);
|
|
7265
|
+
pm_locals_write(&parser->metadata_arena, &parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
|
|
7272
7266
|
}
|
|
7273
7267
|
|
|
7274
7268
|
/**
|
|
@@ -7351,8 +7345,8 @@ static void
|
|
|
7351
7345
|
pm_parser_scope_pop(pm_parser_t *parser) {
|
|
7352
7346
|
pm_scope_t *scope = parser->current_scope;
|
|
7353
7347
|
parser->current_scope = scope->previous;
|
|
7354
|
-
|
|
7355
|
-
|
|
7348
|
+
scope->previous = parser->free_scopes;
|
|
7349
|
+
parser->free_scopes = scope;
|
|
7356
7350
|
}
|
|
7357
7351
|
|
|
7358
7352
|
/******************************************************************************/
|
|
@@ -7420,8 +7414,7 @@ pm_do_loop_stack_p(pm_parser_t *parser) {
|
|
|
7420
7414
|
* lexed: the do-loop stack (so a `do` inside is not read as a `while`/`until`
|
|
7421
7415
|
* loop body) and the accepts-block stack (so a `do` inside is accepted as a
|
|
7422
7416
|
* block rather than binding to an enclosing command). The matching close pops
|
|
7423
|
-
* both.
|
|
7424
|
-
* following in mind before touching either stack:
|
|
7417
|
+
* both. Keep the following in mind before touching either stack:
|
|
7425
7418
|
*
|
|
7426
7419
|
* - The LEXER owns the frame for delimiter-bound stuff. Every token that opens
|
|
7427
7420
|
* a matched pair calls this on the open and `pm_enclosure_frame_pop` on the
|
|
@@ -7429,14 +7422,20 @@ pm_do_loop_stack_p(pm_parser_t *parser) {
|
|
|
7429
7422
|
* `parser_lex` and the `#{` case in `lex_interpolation`; the pop sites are
|
|
7430
7423
|
* the matching `)`, `]`, `}` (including `}` as `EMBEXPR_END`).
|
|
7431
7424
|
* - The PARSER owns the frame for keyword-bounded blocks, where there is no
|
|
7432
|
-
* delimiter token to hang it on
|
|
7433
|
-
*
|
|
7425
|
+
* delimiter token to hang it on. The `-> do end` lambda body pushes the full
|
|
7426
|
+
* frame through this helper: the lexer produces the lambda's `do` before
|
|
7427
|
+
* consulting the do-loop stack, so the body can open inside a loop condition
|
|
7428
|
+
* and must reset it. The `do`/`end` forms in `parse_block` push only
|
|
7429
|
+
* `accepts_block`: the lexer produces a block's `do` only when the do-loop
|
|
7430
|
+
* stack is already false, so another false frame would change nothing. Both
|
|
7431
|
+
* pop before consuming `end`.
|
|
7434
7432
|
* - The `parse_arguments_list` command-args branch juggles ONLY `accepts_block`
|
|
7435
7433
|
* (never through this helper), because the lexer has pushed a delimiter frame
|
|
7436
7434
|
* one token early and the command-args frame must be threaded beneath it. Its
|
|
7437
7435
|
* match lookahead sets must stay in sync with the lexer push sites above.
|
|
7438
|
-
*
|
|
7439
|
-
*
|
|
7436
|
+
* Command arguments change which call a `do` block binds to, not whether a
|
|
7437
|
+
* `do` closes a loop condition (`while foo bar do ... end` still loops), so
|
|
7438
|
+
* the do-loop stack is left alone.
|
|
7440
7439
|
*/
|
|
7441
7440
|
static PRISM_INLINE void
|
|
7442
7441
|
pm_enclosure_frame_push(pm_parser_t *parser) {
|
|
@@ -7676,6 +7675,27 @@ parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor
|
|
|
7676
7675
|
return NULL;
|
|
7677
7676
|
}
|
|
7678
7677
|
|
|
7678
|
+
/**
|
|
7679
|
+
* Returns true if the given magic comment key matches the given name, which is
|
|
7680
|
+
* written in lowercase with underscores and is the same length as the key.
|
|
7681
|
+
* Keys match case-insensitively, and a dash in the key matches an underscore in
|
|
7682
|
+
* the name. Every byte of a name is ASCII, so a byte of a multibyte character
|
|
7683
|
+
* in the key never matches one.
|
|
7684
|
+
*/
|
|
7685
|
+
static bool
|
|
7686
|
+
parser_magic_comment_key_p(const uint8_t *key, size_t key_length, const char *name) {
|
|
7687
|
+
assert(name[key_length] == '\0');
|
|
7688
|
+
|
|
7689
|
+
for (size_t index = 0; index < key_length; index++) {
|
|
7690
|
+
uint8_t byte = key[index];
|
|
7691
|
+
if (byte == '-') byte = '_';
|
|
7692
|
+
if ('A' <= byte && byte <= 'Z') byte |= 0x20;
|
|
7693
|
+
if (byte != (uint8_t) name[index]) return false;
|
|
7694
|
+
}
|
|
7695
|
+
|
|
7696
|
+
return true;
|
|
7697
|
+
}
|
|
7698
|
+
|
|
7679
7699
|
/**
|
|
7680
7700
|
* Parse the current token on the parser to see if it's a magic comment and
|
|
7681
7701
|
* potentially perform some action based on that. A regular expression that this
|
|
@@ -7767,73 +7787,34 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
|
|
|
7767
7787
|
if (cursor != end) return false;
|
|
7768
7788
|
}
|
|
7769
7789
|
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
pm_string_t key;
|
|
7790
|
+
/* Finally, we can start checking the key against the list of known
|
|
7791
|
+
* magic comment keys, and potentially change state based on that. */
|
|
7773
7792
|
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
7793
|
uint32_t value_length = (uint32_t) (value_end - value_start);
|
|
7796
7794
|
|
|
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;
|
|
7795
|
+
switch (key_length) {
|
|
7796
|
+
case 6:
|
|
7797
|
+
/* We only want to attempt to compare against encoding comments
|
|
7798
|
+
* if it is the first line in the file (or the second in the
|
|
7799
|
+
* case of a shebang). */
|
|
7800
|
+
if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
|
|
7801
|
+
if (parser_magic_comment_key_p(key_start, key_length, "coding")) {
|
|
7802
|
+
result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
|
|
7803
|
+
}
|
|
7828
7804
|
}
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
if (
|
|
7835
|
-
|
|
7836
|
-
|
|
7805
|
+
break;
|
|
7806
|
+
case 8:
|
|
7807
|
+
/* We only want to attempt to compare against encoding comments
|
|
7808
|
+
* if it is the first line in the file (or the second in the
|
|
7809
|
+
* case of a shebang). */
|
|
7810
|
+
if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
|
|
7811
|
+
if (parser_magic_comment_key_p(key_start, key_length, "encoding")) {
|
|
7812
|
+
result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
|
|
7813
|
+
}
|
|
7814
|
+
}
|
|
7815
|
+
break;
|
|
7816
|
+
case 11:
|
|
7817
|
+
if (parser_magic_comment_key_p(key_start, key_length, "warn_indent")) {
|
|
7837
7818
|
switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
|
|
7838
7819
|
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
|
|
7839
7820
|
PM_PARSER_WARN_TOKEN_FORMAT(
|
|
@@ -7841,53 +7822,79 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
|
|
|
7841
7822
|
&parser->current,
|
|
7842
7823
|
PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
|
|
7843
7824
|
(int) key_length,
|
|
7844
|
-
|
|
7825
|
+
"warn_indent",
|
|
7845
7826
|
(int) value_length,
|
|
7846
7827
|
(const char *) value_start
|
|
7847
7828
|
);
|
|
7848
7829
|
break;
|
|
7849
7830
|
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
|
|
7850
|
-
parser->
|
|
7831
|
+
parser->warn_mismatched_indentation = false;
|
|
7851
7832
|
break;
|
|
7852
7833
|
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
|
|
7853
|
-
parser->
|
|
7834
|
+
parser->warn_mismatched_indentation = true;
|
|
7854
7835
|
break;
|
|
7855
7836
|
}
|
|
7856
7837
|
}
|
|
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
|
-
|
|
7838
|
+
break;
|
|
7839
|
+
case 21:
|
|
7840
|
+
if (parser_magic_comment_key_p(key_start, key_length, "frozen_string_literal")) {
|
|
7841
|
+
/* We only want to handle frozen string literal comments if
|
|
7842
|
+
* it is before any semantic tokens have been seen. */
|
|
7843
|
+
if (semantic_token_seen) {
|
|
7844
|
+
pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
|
|
7845
|
+
} else {
|
|
7846
|
+
switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
|
|
7847
|
+
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
|
|
7848
|
+
PM_PARSER_WARN_TOKEN_FORMAT(
|
|
7849
|
+
parser,
|
|
7850
|
+
&parser->current,
|
|
7851
|
+
PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
|
|
7852
|
+
(int) key_length,
|
|
7853
|
+
"frozen_string_literal",
|
|
7854
|
+
(int) value_length,
|
|
7855
|
+
(const char *) value_start
|
|
7856
|
+
);
|
|
7857
|
+
break;
|
|
7858
|
+
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
|
|
7859
|
+
parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
|
|
7860
|
+
break;
|
|
7861
|
+
case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
|
|
7862
|
+
parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
|
|
7863
|
+
break;
|
|
7864
|
+
}
|
|
7865
|
+
}
|
|
7883
7866
|
}
|
|
7884
|
-
|
|
7867
|
+
break;
|
|
7868
|
+
case 24:
|
|
7869
|
+
if (parser_magic_comment_key_p(key_start, key_length, "shareable_constant_value")) {
|
|
7870
|
+
const uint8_t *cursor = parser->current.start;
|
|
7871
|
+
while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
|
|
7872
|
+
|
|
7873
|
+
if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
|
|
7874
|
+
pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
|
|
7875
|
+
} else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
|
|
7876
|
+
pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
|
|
7877
|
+
} else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
|
|
7878
|
+
pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
|
|
7879
|
+
} else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
|
|
7880
|
+
pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
|
|
7881
|
+
} else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
|
|
7882
|
+
pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
|
|
7883
|
+
} else {
|
|
7884
|
+
PM_PARSER_WARN_TOKEN_FORMAT(
|
|
7885
|
+
parser,
|
|
7886
|
+
&parser->current,
|
|
7887
|
+
PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
|
|
7888
|
+
(int) key_length,
|
|
7889
|
+
"shareable_constant_value",
|
|
7890
|
+
(int) value_length,
|
|
7891
|
+
(const char *) value_start
|
|
7892
|
+
);
|
|
7893
|
+
}
|
|
7894
|
+
}
|
|
7895
|
+
break;
|
|
7885
7896
|
}
|
|
7886
7897
|
|
|
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
7898
|
// Allocate a new magic comment node to append to the parser's list.
|
|
7892
7899
|
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
7900
|
magic_comment->node.next = NULL;
|
|
@@ -7986,8 +7993,12 @@ PM_STATIC_ASSERT(__LINE__, PM_CONTEXT_MAXIMUM <= 64, "Expected every context to
|
|
|
7986
7993
|
|
|
7987
7994
|
static bool
|
|
7988
7995
|
context_push(pm_parser_t *parser, pm_context_t context) {
|
|
7989
|
-
pm_context_node_t *context_node =
|
|
7990
|
-
if (context_node
|
|
7996
|
+
pm_context_node_t *context_node = parser->free_contexts;
|
|
7997
|
+
if (context_node != NULL) {
|
|
7998
|
+
parser->free_contexts = context_node->prev;
|
|
7999
|
+
} else {
|
|
8000
|
+
context_node = (pm_context_node_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_context_node_t), PRISM_ALIGNOF(pm_context_node_t));
|
|
8001
|
+
}
|
|
7991
8002
|
|
|
7992
8003
|
pm_context_node_t *previous = parser->current_context;
|
|
7993
8004
|
*context_node = (pm_context_node_t) {
|
|
@@ -8002,9 +8013,10 @@ context_push(pm_parser_t *parser, pm_context_t context) {
|
|
|
8002
8013
|
|
|
8003
8014
|
static void
|
|
8004
8015
|
context_pop(pm_parser_t *parser) {
|
|
8005
|
-
pm_context_node_t *
|
|
8006
|
-
|
|
8007
|
-
|
|
8016
|
+
pm_context_node_t *context_node = parser->current_context;
|
|
8017
|
+
parser->current_context = context_node->prev;
|
|
8018
|
+
context_node->prev = parser->free_contexts;
|
|
8019
|
+
parser->free_contexts = context_node;
|
|
8008
8020
|
}
|
|
8009
8021
|
|
|
8010
8022
|
static bool
|
|
@@ -8508,82 +8520,186 @@ lex_global_variable(pm_parser_t *parser) {
|
|
|
8508
8520
|
}
|
|
8509
8521
|
|
|
8510
8522
|
/**
|
|
8511
|
-
*
|
|
8512
|
-
*
|
|
8523
|
+
* A Ruby keyword, along with the lex state that it transitions to and the
|
|
8524
|
+
* token types that it produces.
|
|
8525
|
+
*/
|
|
8526
|
+
typedef struct {
|
|
8527
|
+
/* The keyword itself. */
|
|
8528
|
+
const char *name;
|
|
8529
|
+
|
|
8530
|
+
/* The keyword's first, middle, and last bytes and its length, packed by
|
|
8531
|
+
* PM_KEYWORD_PACK. */
|
|
8532
|
+
uint32_t packed;
|
|
8533
|
+
|
|
8534
|
+
/* The state to transition to when the keyword is lexed. */
|
|
8535
|
+
pm_lex_state_t state;
|
|
8536
|
+
|
|
8537
|
+
/* The token type of the keyword. */
|
|
8538
|
+
pm_token_type_t type;
|
|
8539
|
+
|
|
8540
|
+
/* The token type of the keyword in modifier position, or PM_TOKEN_EOF if
|
|
8541
|
+
* the keyword has no modifier form. */
|
|
8542
|
+
pm_token_type_t modifier_type;
|
|
8543
|
+
} pm_keyword_t;
|
|
8544
|
+
|
|
8545
|
+
/**
|
|
8546
|
+
* The length of the longest keyword, __ENCODING__.
|
|
8547
|
+
*/
|
|
8548
|
+
#define PM_KEYWORD_LENGTH_MAXIMUM 12
|
|
8549
|
+
|
|
8550
|
+
/**
|
|
8551
|
+
* Pack the first, middle (at index length / 2), and last bytes of an identifier
|
|
8552
|
+
* together with its length. No two keywords pack to the same value.
|
|
8553
|
+
*/
|
|
8554
|
+
#define PM_KEYWORD_PACK(first, middle, last, length) \
|
|
8555
|
+
((uint32_t) (first) | ((uint32_t) (middle) << 8) | ((uint32_t) (last) << 16) | ((uint32_t) (length) << 24))
|
|
8556
|
+
|
|
8557
|
+
/**
|
|
8558
|
+
* The number of bits in a slot index of pm_keywords, which has
|
|
8559
|
+
* 1 << PM_KEYWORD_SLOT_BITS slots.
|
|
8560
|
+
*/
|
|
8561
|
+
#define PM_KEYWORD_SLOT_BITS 6
|
|
8562
|
+
|
|
8563
|
+
/**
|
|
8564
|
+
* Return the slot in pm_keywords for a packed identifier. This is
|
|
8565
|
+
* multiplicative hashing: the product is taken modulo 2^32, and the slot is its
|
|
8566
|
+
* top PM_KEYWORD_SLOT_BITS bits. The top bits are used because the low bits of
|
|
8567
|
+
* a product depend only on the low bits of its operands (here, the low bits of
|
|
8568
|
+
* the first byte), while the top bits depend on all of them.
|
|
8513
8569
|
*
|
|
8514
|
-
*
|
|
8515
|
-
*
|
|
8516
|
-
*
|
|
8517
|
-
*
|
|
8518
|
-
*
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8570
|
+
* The multiplier comes from `bin/prism keywords`, which searches for one that
|
|
8571
|
+
* gives every keyword its own slot, making this a perfect hash over the
|
|
8572
|
+
* keywords. Rerun it whenever the set of keywords changes. The table below is
|
|
8573
|
+
* indexed with this same macro, so if two keywords ever shared a slot, the
|
|
8574
|
+
* compiler would reject the duplicate initializer.
|
|
8575
|
+
*/
|
|
8576
|
+
#define PM_KEYWORD_SLOT(packed) ((uint32_t) ((uint32_t) (packed) * 0x75f796ddU) >> (32 - PM_KEYWORD_SLOT_BITS))
|
|
8577
|
+
|
|
8578
|
+
/**
|
|
8579
|
+
* Define the entry in pm_keywords for a keyword, given its first, middle, and
|
|
8580
|
+
* last characters.
|
|
8581
|
+
*/
|
|
8582
|
+
#define PM_KEYWORD(name, first, middle, last, state, type, modifier_type) \
|
|
8583
|
+
[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 }
|
|
8584
|
+
|
|
8585
|
+
/**
|
|
8586
|
+
* The keywords, indexed by PM_KEYWORD_SLOT. Unused slots are zeroed, and a
|
|
8587
|
+
* packed value of zero never matches an identifier because its length is at
|
|
8588
|
+
* least one.
|
|
8589
|
+
*/
|
|
8590
|
+
static const pm_keyword_t pm_keywords[1 << PM_KEYWORD_SLOT_BITS] = {
|
|
8591
|
+
PM_KEYWORD("do", 'd', 'o', 'o', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF),
|
|
8592
|
+
PM_KEYWORD("if", 'i', 'f', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER),
|
|
8593
|
+
PM_KEYWORD("in", 'i', 'n', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IN, PM_TOKEN_EOF),
|
|
8594
|
+
PM_KEYWORD("or", 'o', 'r', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_OR, PM_TOKEN_EOF),
|
|
8595
|
+
PM_KEYWORD("and", 'a', 'n', 'd', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_AND, PM_TOKEN_EOF),
|
|
8596
|
+
PM_KEYWORD("def", 'd', 'e', 'f', PM_LEX_STATE_FNAME, PM_TOKEN_KEYWORD_DEF, PM_TOKEN_EOF),
|
|
8597
|
+
PM_KEYWORD("end", 'e', 'n', 'd', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END, PM_TOKEN_EOF),
|
|
8598
|
+
PM_KEYWORD("END", 'E', 'N', 'D', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END_UPCASE, PM_TOKEN_EOF),
|
|
8599
|
+
PM_KEYWORD("for", 'f', 'o', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_FOR, PM_TOKEN_EOF),
|
|
8600
|
+
PM_KEYWORD("nil", 'n', 'i', 'l', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_NIL, PM_TOKEN_EOF),
|
|
8601
|
+
PM_KEYWORD("not", 'n', 'o', 't', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_NOT, PM_TOKEN_EOF),
|
|
8602
|
+
PM_KEYWORD("case", 'c', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_CASE, PM_TOKEN_EOF),
|
|
8603
|
+
PM_KEYWORD("else", 'e', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_EOF),
|
|
8604
|
+
PM_KEYWORD("next", 'n', 'x', 't', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_NEXT, PM_TOKEN_EOF),
|
|
8605
|
+
PM_KEYWORD("redo", 'r', 'd', 'o', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_REDO, PM_TOKEN_EOF),
|
|
8606
|
+
PM_KEYWORD("self", 's', 'l', 'f', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_SELF, PM_TOKEN_EOF),
|
|
8607
|
+
PM_KEYWORD("then", 't', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_EOF),
|
|
8608
|
+
PM_KEYWORD("true", 't', 'u', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_TRUE, PM_TOKEN_EOF),
|
|
8609
|
+
PM_KEYWORD("when", 'w', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_EOF),
|
|
8610
|
+
PM_KEYWORD("alias", 'a', 'i', 's', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_ALIAS, PM_TOKEN_EOF),
|
|
8611
|
+
PM_KEYWORD("begin", 'b', 'g', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_BEGIN, PM_TOKEN_EOF),
|
|
8612
|
+
PM_KEYWORD("BEGIN", 'B', 'G', 'N', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_BEGIN_UPCASE, PM_TOKEN_EOF),
|
|
8613
|
+
PM_KEYWORD("break", 'b', 'e', 'k', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_BREAK, PM_TOKEN_EOF),
|
|
8614
|
+
PM_KEYWORD("class", 'c', 'a', 's', PM_LEX_STATE_CLASS, PM_TOKEN_KEYWORD_CLASS, PM_TOKEN_EOF),
|
|
8615
|
+
PM_KEYWORD("elsif", 'e', 's', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_EOF),
|
|
8616
|
+
PM_KEYWORD("false", 'f', 'l', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_FALSE, PM_TOKEN_EOF),
|
|
8617
|
+
PM_KEYWORD("retry", 'r', 't', 'y', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_RETRY, PM_TOKEN_EOF),
|
|
8618
|
+
PM_KEYWORD("super", 's', 'p', 'r', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_SUPER, PM_TOKEN_EOF),
|
|
8619
|
+
PM_KEYWORD("undef", 'u', 'd', 'f', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_UNDEF, PM_TOKEN_EOF),
|
|
8620
|
+
PM_KEYWORD("until", 'u', 't', 'l', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNTIL, PM_TOKEN_KEYWORD_UNTIL_MODIFIER),
|
|
8621
|
+
PM_KEYWORD("while", 'w', 'i', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHILE, PM_TOKEN_KEYWORD_WHILE_MODIFIER),
|
|
8622
|
+
PM_KEYWORD("yield", 'y', 'e', 'd', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_YIELD, PM_TOKEN_EOF),
|
|
8623
|
+
PM_KEYWORD("ensure", 'e', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_EOF),
|
|
8624
|
+
PM_KEYWORD("module", 'm', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_MODULE, PM_TOKEN_EOF),
|
|
8625
|
+
PM_KEYWORD("rescue", 'r', 'c', 'e', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_RESCUE_MODIFIER),
|
|
8626
|
+
PM_KEYWORD("return", 'r', 'u', 'n', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RETURN, PM_TOKEN_EOF),
|
|
8627
|
+
PM_KEYWORD("unless", 'u', 'e', 's', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNLESS, PM_TOKEN_KEYWORD_UNLESS_MODIFIER),
|
|
8628
|
+
PM_KEYWORD("defined?", 'd', 'n', '?', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF),
|
|
8629
|
+
PM_KEYWORD("__LINE__", '_', 'N', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___LINE__, PM_TOKEN_EOF),
|
|
8630
|
+
PM_KEYWORD("__FILE__", '_', 'L', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___FILE__, PM_TOKEN_EOF),
|
|
8631
|
+
PM_KEYWORD("__ENCODING__", '_', 'D', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___ENCODING__, PM_TOKEN_EOF)
|
|
8632
|
+
};
|
|
8534
8633
|
|
|
8535
|
-
|
|
8536
|
-
lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
|
|
8537
|
-
return modifier_type;
|
|
8538
|
-
}
|
|
8539
|
-
}
|
|
8634
|
+
#undef PM_KEYWORD
|
|
8540
8635
|
|
|
8541
|
-
|
|
8542
|
-
|
|
8636
|
+
/**
|
|
8637
|
+
* Return the keyword that the identifier corresponds to or NULL if it is not in
|
|
8638
|
+
* the table.
|
|
8639
|
+
*/
|
|
8640
|
+
static PRISM_INLINE const pm_keyword_t *
|
|
8641
|
+
pm_keyword_find(const uint8_t *start, const uint8_t *end) {
|
|
8642
|
+
ptrdiff_t width = end - start;
|
|
8643
|
+
if (width < 2 || width > PM_KEYWORD_LENGTH_MAXIMUM) return NULL;
|
|
8543
8644
|
|
|
8544
|
-
|
|
8645
|
+
size_t uwidth = (size_t) width;
|
|
8646
|
+
uint32_t packed = PM_KEYWORD_PACK(start[0], start[uwidth >> 1], start[uwidth - 1], uwidth);
|
|
8647
|
+
|
|
8648
|
+
const pm_keyword_t *keyword = &pm_keywords[PM_KEYWORD_SLOT(packed)];
|
|
8649
|
+
return (keyword->packed == packed && memcmp(start + 1, keyword->name + 1, uwidth - 2) == 0) ? keyword : NULL;
|
|
8650
|
+
}
|
|
8651
|
+
|
|
8652
|
+
/**
|
|
8653
|
+
* Returns true if the bytes between start and end spell a keyword, using the
|
|
8654
|
+
* same lookup that the lexer does.
|
|
8655
|
+
*/
|
|
8656
|
+
bool
|
|
8657
|
+
pm_keyword_p(const uint8_t *start, const uint8_t *end) {
|
|
8658
|
+
return pm_keyword_find(start, end) != NULL;
|
|
8545
8659
|
}
|
|
8546
8660
|
|
|
8547
8661
|
static pm_token_type_t
|
|
8548
8662
|
lex_identifier(pm_parser_t *parser, bool previous_command_start) {
|
|
8549
|
-
|
|
8550
|
-
size_t width;
|
|
8663
|
+
/* Lex as far as we can into the current identifier. */
|
|
8551
8664
|
const uint8_t *end = parser->end;
|
|
8552
8665
|
const uint8_t *current_start = parser->current.start;
|
|
8553
8666
|
const uint8_t *current_end = parser->current.end;
|
|
8554
8667
|
bool encoding_changed = parser->encoding_changed;
|
|
8555
8668
|
|
|
8556
8669
|
if (encoding_changed) {
|
|
8670
|
+
size_t width;
|
|
8557
8671
|
while ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) {
|
|
8558
8672
|
current_end += width;
|
|
8559
8673
|
}
|
|
8560
8674
|
} else {
|
|
8561
|
-
|
|
8675
|
+
/* Fast path: scan ASCII identifier bytes using wide operations. */
|
|
8562
8676
|
current_end += scan_identifier_ascii(current_end, end);
|
|
8563
8677
|
|
|
8564
|
-
|
|
8678
|
+
/* Byte-at-a-time fallback for the tail and any UTF-8 sequences. */
|
|
8679
|
+
size_t width;
|
|
8565
8680
|
while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) {
|
|
8566
8681
|
current_end += width;
|
|
8567
8682
|
}
|
|
8568
8683
|
}
|
|
8684
|
+
|
|
8569
8685
|
parser->current.end = current_end;
|
|
8570
8686
|
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8687
|
+
/* Whether the identifier ends in ! or ?, which makes it a method name
|
|
8688
|
+
* unless it spells a keyword (defined?). */
|
|
8689
|
+
bool method_name = false;
|
|
8574
8690
|
|
|
8575
8691
|
if (current_end < end) {
|
|
8576
8692
|
if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8693
|
+
/* Extend the identifier by the ! or ?, so that the keyword lookup
|
|
8694
|
+
* below sees the whole name. */
|
|
8695
|
+
current_end++;
|
|
8580
8696
|
|
|
8581
8697
|
if (
|
|
8582
8698
|
((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
|
|
8583
8699
|
(peek(parser) == ':') && (peek_offset(parser, 1) != ':')
|
|
8584
8700
|
) {
|
|
8585
|
-
|
|
8586
|
-
|
|
8701
|
+
/* If we are in a position where we can accept a : at the end of
|
|
8702
|
+
* an identifier, then we will optionally accept it. */
|
|
8587
8703
|
lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
|
|
8588
8704
|
(void) match(parser, ':');
|
|
8589
8705
|
|
|
@@ -8593,27 +8709,17 @@ lex_identifier(pm_parser_t *parser, bool previous_command_start) {
|
|
|
8593
8709
|
return PM_TOKEN_LABEL;
|
|
8594
8710
|
}
|
|
8595
8711
|
|
|
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.
|
|
8712
|
+
method_name = true;
|
|
8713
|
+
} 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, '=')) {
|
|
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. */
|
|
8608
8716
|
return PM_TOKEN_IDENTIFIER;
|
|
8609
|
-
}
|
|
8610
|
-
|
|
8611
|
-
if (
|
|
8717
|
+
} else if (
|
|
8612
8718
|
((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
|
|
8613
8719
|
peek(parser) == ':' && peek_offset(parser, 1) != ':'
|
|
8614
8720
|
) {
|
|
8615
|
-
|
|
8616
|
-
|
|
8721
|
+
/* If we are in a position where we can accept a : at the end of an
|
|
8722
|
+
* identifier, then we will optionally accept it. */
|
|
8617
8723
|
lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
|
|
8618
8724
|
(void) match(parser, ':');
|
|
8619
8725
|
|
|
@@ -8625,106 +8731,66 @@ lex_identifier(pm_parser_t *parser, bool previous_command_start) {
|
|
|
8625
8731
|
}
|
|
8626
8732
|
|
|
8627
8733
|
if (parser->lex_state != PM_LEX_STATE_DOT) {
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
* `state = p->lex.state` capture in parse.y's keyword handling. */
|
|
8734
|
+
/* The lex state from before the keyword changes it, which decides
|
|
8735
|
+
* whether the keyword is in its modifier form and whether `do` is a
|
|
8736
|
+
* plain name below. */
|
|
8632
8737
|
pm_lex_state_t previous_lex_state = parser->lex_state;
|
|
8738
|
+
const pm_keyword_t *keyword = pm_keyword_find(current_start, current_end);
|
|
8633
8739
|
|
|
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
|
-
}
|
|
8740
|
+
if (keyword != NULL) {
|
|
8741
|
+
if (previous_lex_state & PM_LEX_STATE_FNAME) {
|
|
8742
|
+
lex_state_set(parser, PM_LEX_STATE_ENDFN);
|
|
8743
|
+
} else {
|
|
8744
|
+
lex_state_set(parser, keyword->state);
|
|
8745
|
+
if (keyword->state == PM_LEX_STATE_BEG) {
|
|
8746
|
+
parser->command_start = true;
|
|
8747
|
+
}
|
|
8748
|
+
|
|
8749
|
+
if ((keyword->modifier_type != PM_TOKEN_EOF) && !(previous_lex_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
|
|
8750
|
+
lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
|
|
8751
|
+
return keyword->modifier_type;
|
|
8752
|
+
}
|
|
8753
|
+
}
|
|
8754
|
+
|
|
8755
|
+
if (keyword->type == PM_TOKEN_KEYWORD_DO) {
|
|
8756
|
+
/* In FNAME position (a symbol like `:do` or a method name like
|
|
8757
|
+
* `def do`), `do` is a plain name rather than a block, loop, or
|
|
8758
|
+
* lambda opener, so none of the discrimination below applies
|
|
8759
|
+
* and the lambda nesting is left untouched. */
|
|
8760
|
+
if (previous_lex_state & PM_LEX_STATE_FNAME) {
|
|
8670
8761
|
return PM_TOKEN_KEYWORD_DO;
|
|
8671
8762
|
}
|
|
8763
|
+
if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
|
|
8764
|
+
/* At the bare nesting level of a lambda literal (no
|
|
8765
|
+
* delimiter opened since `->`), a `do` opens the lambda
|
|
8766
|
+
* body. This is a distinct token so that a command in a
|
|
8767
|
+
* parameter default cannot consume it as its own block
|
|
8768
|
+
* (`-> a = foo do end` is `->(a = foo) do end`).
|
|
8769
|
+
*
|
|
8770
|
+
* Clear the nesting so that no token within the `do`/`end`
|
|
8771
|
+
* body is considered to be at the beginning of a lambda;
|
|
8772
|
+
* the parser restores the enclosing value once the lambda
|
|
8773
|
+
* has been fully parsed. */
|
|
8774
|
+
parser->lambda_enclosure_nesting = -1;
|
|
8775
|
+
return PM_TOKEN_KEYWORD_DO_LAMBDA;
|
|
8776
|
+
}
|
|
8777
|
+
if (pm_do_loop_stack_p(parser)) {
|
|
8778
|
+
return PM_TOKEN_KEYWORD_DO_LOOP;
|
|
8779
|
+
}
|
|
8780
|
+
if (!pm_accepts_block_stack_p(parser)) {
|
|
8781
|
+
return PM_TOKEN_KEYWORD_DO_BLOCK;
|
|
8782
|
+
}
|
|
8783
|
+
return PM_TOKEN_KEYWORD_DO;
|
|
8784
|
+
}
|
|
8672
8785
|
|
|
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;
|
|
8786
|
+
return keyword->type;
|
|
8725
8787
|
}
|
|
8726
8788
|
}
|
|
8727
8789
|
|
|
8790
|
+
if (method_name) {
|
|
8791
|
+
return PM_TOKEN_METHOD_NAME;
|
|
8792
|
+
}
|
|
8793
|
+
|
|
8728
8794
|
if (encoding_changed) {
|
|
8729
8795
|
return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
|
|
8730
8796
|
}
|
|
@@ -9575,7 +9641,7 @@ lex_question_mark(pm_parser_t *parser) {
|
|
|
9575
9641
|
|
|
9576
9642
|
// Copy buffer data into the arena and free the heap buffer.
|
|
9577
9643
|
void *arena_data = pm_arena_memdup(parser->arena, buffer.value, buffer.length, PRISM_ALIGNOF(uint8_t));
|
|
9578
|
-
|
|
9644
|
+
pm_string_init(&parser->current_string, (const uint8_t *) arena_data, buffer.length);
|
|
9579
9645
|
pm_buffer_cleanup(&buffer);
|
|
9580
9646
|
|
|
9581
9647
|
return PM_TOKEN_CHARACTER_LITERAL;
|
|
@@ -9899,7 +9965,7 @@ pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
|
|
|
9899
9965
|
// Copy buffer data into the arena and free the heap buffer.
|
|
9900
9966
|
size_t len = pm_buffer_length(&token_buffer->buffer);
|
|
9901
9967
|
void *arena_data = pm_arena_memdup(parser->arena, pm_buffer_value(&token_buffer->buffer), len, PRISM_ALIGNOF(uint8_t));
|
|
9902
|
-
|
|
9968
|
+
pm_string_init(&parser->current_string, (const uint8_t *) arena_data, len);
|
|
9903
9969
|
pm_buffer_cleanup(&token_buffer->buffer);
|
|
9904
9970
|
}
|
|
9905
9971
|
|
|
@@ -12952,6 +13018,19 @@ expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t di
|
|
|
12952
13018
|
*/
|
|
12953
13019
|
#define PM_PARSE_ACCEPTS_STATEMENT ((uint8_t) 0x10)
|
|
12954
13020
|
|
|
13021
|
+
/**
|
|
13022
|
+
* Indicates that an endless method definition may have a command call as its
|
|
13023
|
+
* body, as in `def f = foo 1`. Such a definition is a command write, accepted
|
|
13024
|
+
* as a statement (including the value of a statement-level rescue modifier)
|
|
13025
|
+
* and as the value of a single-target write. Since Ruby 4.0 it is also
|
|
13026
|
+
* accepted as the sole argument of a call, where other command writes such as
|
|
13027
|
+
* `x = foo 1` are not. Expression positions such as conditional predicates and
|
|
13028
|
+
* the operands of `not`, `!`, `and`, and `or` accept a command call but not a
|
|
13029
|
+
* command write, so this is set alongside PM_PARSE_ACCEPTS_COMMAND_CALL only
|
|
13030
|
+
* where both are accepted.
|
|
13031
|
+
*/
|
|
13032
|
+
#define PM_PARSE_ACCEPTS_COMMAND_DEF ((uint8_t) 0x20)
|
|
13033
|
+
|
|
12955
13034
|
static pm_node_t *
|
|
12956
13035
|
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
13036
|
|
|
@@ -13067,6 +13146,23 @@ token_begins_pattern_p(pm_token_type_t type) {
|
|
|
13067
13146
|
);
|
|
13068
13147
|
}
|
|
13069
13148
|
|
|
13149
|
+
/**
|
|
13150
|
+
* Returns true if the given token can begin an argument in an unparenthesized
|
|
13151
|
+
* argument list. This is the set of tokens that can begin an expression plus
|
|
13152
|
+
* the prefix operators that only appear in argument position — `&` (block
|
|
13153
|
+
* arguments), `*` (splats), and `**` (keyword splats) — which are excluded
|
|
13154
|
+
* from token_begins_expression_p.
|
|
13155
|
+
*/
|
|
13156
|
+
static PRISM_INLINE bool
|
|
13157
|
+
token_begins_argument_p(pm_token_type_t type) {
|
|
13158
|
+
return (
|
|
13159
|
+
token_begins_expression_p(type) ||
|
|
13160
|
+
type == PM_TOKEN_UAMPERSAND ||
|
|
13161
|
+
type == PM_TOKEN_USTAR ||
|
|
13162
|
+
type == PM_TOKEN_USTAR_STAR
|
|
13163
|
+
);
|
|
13164
|
+
}
|
|
13165
|
+
|
|
13070
13166
|
/**
|
|
13071
13167
|
* Parse an expression with the given binding power that may be optionally
|
|
13072
13168
|
* prefixed by the * operator.
|
|
@@ -13082,7 +13178,7 @@ parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power,
|
|
|
13082
13178
|
return parse_value_expression(parser, binding_power, flags, diag_id, depth);
|
|
13083
13179
|
}
|
|
13084
13180
|
|
|
13085
|
-
static
|
|
13181
|
+
static pm_visit_result_t
|
|
13086
13182
|
pm_node_unreference_each(const pm_node_t *node, void *data) {
|
|
13087
13183
|
switch (PM_NODE_TYPE(node)) {
|
|
13088
13184
|
/* When we are about to destroy a set of nodes that could potentially
|
|
@@ -13108,15 +13204,15 @@ pm_node_unreference_each(const pm_node_t *node, void *data) {
|
|
|
13108
13204
|
}
|
|
13109
13205
|
parser->current_block_exits->size--;
|
|
13110
13206
|
|
|
13111
|
-
/* Note
|
|
13207
|
+
/* Note descending here because these nodes could have
|
|
13112
13208
|
* arguments that are themselves block exits. */
|
|
13113
|
-
return
|
|
13209
|
+
return PM_VISIT_DESCEND;
|
|
13114
13210
|
}
|
|
13115
13211
|
|
|
13116
13212
|
index++;
|
|
13117
13213
|
}
|
|
13118
13214
|
|
|
13119
|
-
return
|
|
13215
|
+
return PM_VISIT_DESCEND;
|
|
13120
13216
|
}
|
|
13121
13217
|
/* When an implicit local variable is written to or targeted, it becomes
|
|
13122
13218
|
* a regular, named local variable. This branch removes it from the list
|
|
@@ -13140,10 +13236,10 @@ pm_node_unreference_each(const pm_node_t *node, void *data) {
|
|
|
13140
13236
|
}
|
|
13141
13237
|
}
|
|
13142
13238
|
|
|
13143
|
-
return
|
|
13239
|
+
return PM_VISIT_SKIP;
|
|
13144
13240
|
}
|
|
13145
13241
|
default:
|
|
13146
|
-
return
|
|
13242
|
+
return PM_VISIT_DESCEND;
|
|
13147
13243
|
}
|
|
13148
13244
|
}
|
|
13149
13245
|
|
|
@@ -13708,7 +13804,7 @@ parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
|
|
|
13708
13804
|
context_push(parser, context);
|
|
13709
13805
|
|
|
13710
13806
|
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));
|
|
13807
|
+
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
13808
|
pm_statements_node_body_append(parser, statements, node, true);
|
|
13713
13809
|
|
|
13714
13810
|
// If we're recovering from a syntax error, then we need to stop parsing
|
|
@@ -14135,6 +14231,27 @@ pm_command_call_value_p(const pm_parser_t *parser, const pm_node_t *node) {
|
|
|
14135
14231
|
}
|
|
14136
14232
|
}
|
|
14137
14233
|
|
|
14234
|
+
/**
|
|
14235
|
+
* Returns true if the given node is a command that can end before a comma
|
|
14236
|
+
* without consuming it: a constant-path command with a brace block
|
|
14237
|
+
* (`Foo::Bar { }`), or an endless method whose command body ends in a rescue
|
|
14238
|
+
* modifier value (`def f = foo 1 rescue z`). Other commands take a following
|
|
14239
|
+
* comma into their own arguments. A command can be neither one of several
|
|
14240
|
+
* arguments nor one of several values of a write, so a comma after one of
|
|
14241
|
+
* these is an error.
|
|
14242
|
+
*/
|
|
14243
|
+
static bool
|
|
14244
|
+
pm_command_before_comma_p(const pm_parser_t *parser, const pm_node_t *node) {
|
|
14245
|
+
switch (PM_NODE_TYPE(node)) {
|
|
14246
|
+
case PM_CALL_NODE:
|
|
14247
|
+
return pm_constant_path_command_call_p(parser, (const pm_call_node_t *) node);
|
|
14248
|
+
case PM_DEF_NODE:
|
|
14249
|
+
return pm_command_call_value_p(parser, node);
|
|
14250
|
+
default:
|
|
14251
|
+
return false;
|
|
14252
|
+
}
|
|
14253
|
+
}
|
|
14254
|
+
|
|
14138
14255
|
/**
|
|
14139
14256
|
* Returns true if the given node is a block call: a command
|
|
14140
14257
|
* with a do-block, or any call chained (via `.`, `::`, `&.`) from such a node.
|
|
@@ -14321,7 +14438,17 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
|
|
|
14321
14438
|
PRISM_FALLTHROUGH
|
|
14322
14439
|
default: {
|
|
14323
14440
|
if (argument == NULL) {
|
|
14324
|
-
|
|
14441
|
+
/* The first argument may be a command call, and since Ruby
|
|
14442
|
+
* 4.0 also an endless method with a command body
|
|
14443
|
+
* (`private def f = foo 1`), either of which must then be
|
|
14444
|
+
* the sole argument. */
|
|
14445
|
+
uint8_t command_flags = 0;
|
|
14446
|
+
if (!parsed_first_argument) {
|
|
14447
|
+
command_flags = PM_PARSE_ACCEPTS_COMMAND_CALL;
|
|
14448
|
+
if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
|
|
14449
|
+
}
|
|
14450
|
+
|
|
14451
|
+
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
14452
|
}
|
|
14326
14453
|
|
|
14327
14454
|
bool contains_keywords = false;
|
|
@@ -14418,6 +14545,12 @@ parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_for
|
|
|
14418
14545
|
break;
|
|
14419
14546
|
}
|
|
14420
14547
|
}
|
|
14548
|
+
|
|
14549
|
+
/* A command can only be the sole argument, as in
|
|
14550
|
+
* `foo Foo::Bar { }, y` or `foo def f = bar 1 rescue z, y`. */
|
|
14551
|
+
if (pm_command_before_comma_p(parser, argument)) {
|
|
14552
|
+
pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
|
|
14553
|
+
}
|
|
14421
14554
|
} else {
|
|
14422
14555
|
// If there is no comma at the end of the argument list then we're
|
|
14423
14556
|
// done parsing arguments and can break out of this loop.
|
|
@@ -15628,7 +15761,7 @@ parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_a
|
|
|
15628
15761
|
|
|
15629
15762
|
arguments->closing_loc = TOK2LOC(parser, &parser->previous);
|
|
15630
15763
|
}
|
|
15631
|
-
} else if ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (
|
|
15764
|
+
} else if ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
|
|
15632
15765
|
found |= true;
|
|
15633
15766
|
parsed_command_args = true;
|
|
15634
15767
|
|
|
@@ -15848,7 +15981,7 @@ ensure_valid_jump(pm_parser_t *parser, pm_node_t *node) {
|
|
|
15848
15981
|
// block exit to the list of exits for the expression, and
|
|
15849
15982
|
// the node parsing will handle validating it instead.
|
|
15850
15983
|
assert(parser->current_block_exits != NULL);
|
|
15851
|
-
|
|
15984
|
+
pm_node_list_append_inline(parser->arena, parser->current_block_exits, node);
|
|
15852
15985
|
return;
|
|
15853
15986
|
case PM_CONTEXT_BEGIN_ELSE:
|
|
15854
15987
|
case PM_CONTEXT_BEGIN_ENSURE:
|
|
@@ -16586,12 +16719,12 @@ parse_variable(pm_parser_t *parser) {
|
|
|
16586
16719
|
}
|
|
16587
16720
|
|
|
16588
16721
|
pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false));
|
|
16589
|
-
|
|
16722
|
+
pm_node_list_append_inline(parser->arena, ¤t_scope->implicit_parameters, node);
|
|
16590
16723
|
|
|
16591
16724
|
return node;
|
|
16592
16725
|
} else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
|
|
16593
16726
|
pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous));
|
|
16594
|
-
|
|
16727
|
+
pm_node_list_append_inline(parser->arena, ¤t_scope->implicit_parameters, node);
|
|
16595
16728
|
|
|
16596
16729
|
return node;
|
|
16597
16730
|
}
|
|
@@ -16648,18 +16781,13 @@ parse_method_definition_name(pm_parser_t *parser) {
|
|
|
16648
16781
|
|
|
16649
16782
|
static void
|
|
16650
16783
|
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
|
-
}
|
|
16784
|
+
// Make a writable copy of the string in the arena, since strings only view
|
|
16785
|
+
// their bytes. We keep a mutable pointer to the arena memory so we can
|
|
16786
|
+
// memmove into it below without casting away const from the string's
|
|
16787
|
+
// source field.
|
|
16788
|
+
size_t length = pm_string_length(string);
|
|
16789
|
+
uint8_t *writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
|
|
16790
|
+
pm_string_init(string, writable, length);
|
|
16663
16791
|
|
|
16664
16792
|
// Now get the bounds of the existing string. We'll use this as a
|
|
16665
16793
|
// destination to move bytes into. We'll also use it for bounds checking
|
|
@@ -16824,11 +16952,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
|
|
|
16824
16952
|
if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
|
|
16825
16953
|
pm_node_list_t parts = { 0 };
|
|
16826
16954
|
pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
|
|
16827
|
-
|
|
16955
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16828
16956
|
|
|
16829
16957
|
do {
|
|
16830
16958
|
part = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
|
|
16831
|
-
|
|
16959
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16832
16960
|
parser_lex(parser);
|
|
16833
16961
|
} while (match1(parser, PM_TOKEN_STRING_CONTENT));
|
|
16834
16962
|
|
|
@@ -16883,11 +17011,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
|
|
|
16883
17011
|
pm_node_list_t parts = { 0 };
|
|
16884
17012
|
pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
|
|
16885
17013
|
pm_node_flag_set(part, parse_unescaped_encoding(parser, explicit_encoding));
|
|
16886
|
-
|
|
17014
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16887
17015
|
|
|
16888
17016
|
while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
|
|
16889
17017
|
if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
|
|
16890
|
-
|
|
17018
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16891
17019
|
}
|
|
16892
17020
|
}
|
|
16893
17021
|
|
|
@@ -16911,7 +17039,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
|
|
|
16911
17039
|
|
|
16912
17040
|
while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
|
|
16913
17041
|
if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
|
|
16914
|
-
|
|
17042
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
16915
17043
|
}
|
|
16916
17044
|
}
|
|
16917
17045
|
|
|
@@ -17297,7 +17425,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
|
|
|
17297
17425
|
}
|
|
17298
17426
|
|
|
17299
17427
|
pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
|
|
17300
|
-
|
|
17428
|
+
pm_node_list_append_inline(parser->arena, &assocs, assoc);
|
|
17301
17429
|
break;
|
|
17302
17430
|
}
|
|
17303
17431
|
}
|
|
@@ -17312,7 +17440,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
|
|
|
17312
17440
|
pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_START(first_node), PM_NODE_LENGTH(first_node)));
|
|
17313
17441
|
pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
|
|
17314
17442
|
|
|
17315
|
-
|
|
17443
|
+
pm_node_list_append_inline(parser->arena, &assocs, assoc);
|
|
17316
17444
|
break;
|
|
17317
17445
|
}
|
|
17318
17446
|
}
|
|
@@ -17340,7 +17468,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
|
|
|
17340
17468
|
rest = assoc;
|
|
17341
17469
|
} else {
|
|
17342
17470
|
pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
|
|
17343
|
-
|
|
17471
|
+
pm_node_list_append_inline(parser->arena, &assocs, assoc);
|
|
17344
17472
|
}
|
|
17345
17473
|
} else {
|
|
17346
17474
|
pm_node_t *key;
|
|
@@ -17386,7 +17514,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
|
|
|
17386
17514
|
pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
|
|
17387
17515
|
}
|
|
17388
17516
|
|
|
17389
|
-
|
|
17517
|
+
pm_node_list_append_inline(parser->arena, &assocs, assoc);
|
|
17390
17518
|
}
|
|
17391
17519
|
}
|
|
17392
17520
|
|
|
@@ -17666,7 +17794,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_
|
|
|
17666
17794
|
* are not captures of this pattern. A nested alternation is not visited
|
|
17667
17795
|
* either: its operands were already checked when it was parsed.
|
|
17668
17796
|
*/
|
|
17669
|
-
static
|
|
17797
|
+
static pm_visit_result_t
|
|
17670
17798
|
parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
|
|
17671
17799
|
pm_parser_t *parser = (pm_parser_t *) data;
|
|
17672
17800
|
|
|
@@ -17677,7 +17805,7 @@ parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
|
|
|
17677
17805
|
if (peek_at(parser, parser->start + PM_NODE_START(node)) != '_') {
|
|
17678
17806
|
pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
|
|
17679
17807
|
}
|
|
17680
|
-
return
|
|
17808
|
+
return PM_VISIT_SKIP;
|
|
17681
17809
|
case PM_ARRAY_PATTERN_NODE:
|
|
17682
17810
|
case PM_ASSOC_NODE:
|
|
17683
17811
|
case PM_ASSOC_SPLAT_NODE:
|
|
@@ -17687,9 +17815,9 @@ parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
|
|
|
17687
17815
|
case PM_IMPLICIT_NODE:
|
|
17688
17816
|
case PM_PARENTHESES_NODE:
|
|
17689
17817
|
case PM_SPLAT_NODE:
|
|
17690
|
-
return
|
|
17818
|
+
return PM_VISIT_DESCEND;
|
|
17691
17819
|
default:
|
|
17692
|
-
return
|
|
17820
|
+
return PM_VISIT_SKIP;
|
|
17693
17821
|
}
|
|
17694
17822
|
}
|
|
17695
17823
|
|
|
@@ -17885,7 +18013,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags
|
|
|
17885
18013
|
// or a find pattern. We need to parse all of the patterns, put them
|
|
17886
18014
|
// into a big list, and then determine which type of node we have.
|
|
17887
18015
|
pm_node_list_t nodes = { 0 };
|
|
17888
|
-
|
|
18016
|
+
pm_node_list_append_inline(parser->arena, &nodes, node);
|
|
17889
18017
|
|
|
17890
18018
|
// Gather up all of the patterns into the list.
|
|
17891
18019
|
while (accept1(parser, PM_TOKEN_COMMA)) {
|
|
@@ -17904,7 +18032,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags
|
|
|
17904
18032
|
}
|
|
17905
18033
|
|
|
17906
18034
|
node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
|
|
17907
|
-
|
|
18035
|
+
pm_node_list_append_inline(parser->arena, &nodes, node);
|
|
17908
18036
|
trailing_rest = true;
|
|
17909
18037
|
break;
|
|
17910
18038
|
}
|
|
@@ -17924,7 +18052,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags
|
|
|
17924
18052
|
node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
|
|
17925
18053
|
}
|
|
17926
18054
|
|
|
17927
|
-
|
|
18055
|
+
pm_node_list_append_inline(parser->arena, &nodes, node);
|
|
17928
18056
|
}
|
|
17929
18057
|
|
|
17930
18058
|
// If the first pattern and the last pattern are rest patterns, then we
|
|
@@ -18556,7 +18684,7 @@ parse_class(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
|
|
|
18556
18684
|
* Parse a method definition expression (the `def` keyword).
|
|
18557
18685
|
*/
|
|
18558
18686
|
static pm_node_t *
|
|
18559
|
-
parse_def(pm_parser_t *parser,
|
|
18687
|
+
parse_def(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
|
|
18560
18688
|
pm_node_list_t current_block_exits = { 0 };
|
|
18561
18689
|
pm_node_list_t *previous_block_exits = push_block_exits(parser, ¤t_block_exits);
|
|
18562
18690
|
|
|
@@ -18806,14 +18934,9 @@ parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags,
|
|
|
18806
18934
|
pm_do_loop_stack_push(parser, false);
|
|
18807
18935
|
statements = UP(pm_statements_node_create(parser));
|
|
18808
18936
|
|
|
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
|
-
}
|
|
18937
|
+
/* The body accepts a command call (`def f = foo 1`) only where the
|
|
18938
|
+
* caller accepts an endless method with a command body. */
|
|
18939
|
+
uint8_t allow_flags = (flags & PM_PARSE_ACCEPTS_COMMAND_DEF) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
|
|
18817
18940
|
|
|
18818
18941
|
/* Inside a def body, we push true onto the accepts_block_stack so that
|
|
18819
18942
|
* `do` is lexed as PM_TOKEN_KEYWORD_DO (which can only start a block
|
|
@@ -18858,7 +18981,7 @@ parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags,
|
|
|
18858
18981
|
pm_accepts_block_stack_pop(parser);
|
|
18859
18982
|
|
|
18860
18983
|
/* A nested endless def whose body is a command call (e.g.,
|
|
18861
|
-
* `def f = def g = foo bar`) is a command
|
|
18984
|
+
* `def f = def g = foo bar`) is a command write and cannot appear
|
|
18862
18985
|
* as a def body. */
|
|
18863
18986
|
if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(parser, statement)) {
|
|
18864
18987
|
PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
|
|
@@ -19041,7 +19164,7 @@ parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
19041
19164
|
/* Otherwise, we're going to parse the first statement in the list of
|
|
19042
19165
|
* statements within the parentheses. */
|
|
19043
19166
|
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));
|
|
19167
|
+
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
19168
|
context_pop(parser);
|
|
19046
19169
|
|
|
19047
19170
|
/* Determine if this statement is followed by a terminator. In the case of a
|
|
@@ -19154,7 +19277,7 @@ parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
19154
19277
|
|
|
19155
19278
|
/* Parse each statement within the parentheses. */
|
|
19156
19279
|
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));
|
|
19280
|
+
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
19281
|
pm_statements_node_body_append(parser, statements, node, true);
|
|
19159
19282
|
|
|
19160
19283
|
/* If we're recovering from a syntax error, then we need to stop parsing
|
|
@@ -19435,11 +19558,11 @@ parse_heredoc(pm_parser_t *parser, uint16_t depth) {
|
|
|
19435
19558
|
// so we'll need to create an interpolated string node to hold
|
|
19436
19559
|
// them all.
|
|
19437
19560
|
pm_node_list_t parts = { 0 };
|
|
19438
|
-
|
|
19561
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
19439
19562
|
|
|
19440
19563
|
while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
|
|
19441
19564
|
if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
|
|
19442
|
-
|
|
19565
|
+
pm_node_list_append_inline(parser->arena, &parts, part);
|
|
19443
19566
|
}
|
|
19444
19567
|
}
|
|
19445
19568
|
|
|
@@ -19670,17 +19793,17 @@ parse_lambda(pm_parser_t *parser, uint16_t depth) {
|
|
|
19670
19793
|
/* Restore the enclosing lambda's nesting now that the body has been
|
|
19671
19794
|
* parsed, so that the token following the closing `}` is lexed in the
|
|
19672
19795
|
* 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`. */
|
|
19796
|
+
* level, which every token inside the braces sits above. */
|
|
19675
19797
|
parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
|
|
19676
19798
|
expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
|
|
19677
19799
|
} else {
|
|
19678
|
-
/* A `-> { }` body is delimited by `{`/`}`, whose
|
|
19679
|
-
*
|
|
19680
|
-
*
|
|
19681
|
-
*
|
|
19682
|
-
*
|
|
19683
|
-
|
|
19800
|
+
/* A `-> { }` body is delimited by `{`/`}`, whose enclosure frame the
|
|
19801
|
+
* lexer manages. A `-> do end` body is delimited by keywords, so push
|
|
19802
|
+
* the frame here and pop it before `end`. The frame isolates the body
|
|
19803
|
+
* from an enclosing `while`/`until`/`for` condition, so a `do` inside
|
|
19804
|
+
* it opens a block rather than the loop body. The push must precede
|
|
19805
|
+
* consuming the `do`, which lexes the first token of the body. */
|
|
19806
|
+
pm_enclosure_frame_push(parser);
|
|
19684
19807
|
expect1(parser, PM_TOKEN_KEYWORD_DO_LAMBDA, PM_ERR_LAMBDA_OPEN);
|
|
19685
19808
|
opening = parser->previous;
|
|
19686
19809
|
|
|
@@ -19701,7 +19824,7 @@ parse_lambda(pm_parser_t *parser, uint16_t depth) {
|
|
|
19701
19824
|
parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
|
|
19702
19825
|
}
|
|
19703
19826
|
|
|
19704
|
-
|
|
19827
|
+
pm_enclosure_frame_pop(parser);
|
|
19705
19828
|
|
|
19706
19829
|
/* As with the brace branch above, restore the nesting before consuming
|
|
19707
19830
|
* the closing `end`, which lexes the token that follows it. */
|
|
@@ -20040,7 +20163,7 @@ parse_identifier(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
20040
20163
|
// can still be a method call if it is followed by arguments or
|
|
20041
20164
|
// a block, so we need to check for that here.
|
|
20042
20165
|
if (
|
|
20043
|
-
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (
|
|
20166
|
+
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
|
|
20044
20167
|
(pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
|
|
20045
20168
|
match1(parser, PM_TOKEN_BRACE_LEFT)
|
|
20046
20169
|
) {
|
|
@@ -20497,7 +20620,14 @@ parse_jump(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
|
|
|
20497
20620
|
|
|
20498
20621
|
if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
|
|
20499
20622
|
pm_token_t next = parser->current;
|
|
20623
|
+
|
|
20624
|
+
/* The arguments of a jump start a fresh command context, so a
|
|
20625
|
+
* `do` inside them binds to the nearest call within the
|
|
20626
|
+
* arguments rather than to an enclosing command call, as in
|
|
20627
|
+
* `check 1, case x when 1 then return bar do end end`. */
|
|
20628
|
+
pm_accepts_block_stack_push(parser, true);
|
|
20500
20629
|
parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
|
|
20630
|
+
pm_accepts_block_stack_pop(parser);
|
|
20501
20631
|
|
|
20502
20632
|
// Reject `foo && return bar`.
|
|
20503
20633
|
if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) {
|
|
@@ -20590,7 +20720,16 @@ parse_not(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags,
|
|
|
20590
20720
|
}
|
|
20591
20721
|
}
|
|
20592
20722
|
} else {
|
|
20593
|
-
|
|
20723
|
+
/* The operand of `not` may be a command call, except at the start of
|
|
20724
|
+
* an endless method body, where it may be one only if the body may
|
|
20725
|
+
* be: `def f = not foo 1` has a command body, so it is rejected where
|
|
20726
|
+
* that is not accepted (`if def f = not foo 1; end`). A nested `not`
|
|
20727
|
+
* is still at the start of the body, so the operand carries the
|
|
20728
|
+
* endless method context on (`def f = not not foo 1`). */
|
|
20729
|
+
uint8_t operand_flags = (flags & PM_PARSE_IN_ENDLESS_DEF)
|
|
20730
|
+
? (uint8_t) (flags & (PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_IN_ENDLESS_DEF))
|
|
20731
|
+
: PM_PARSE_ACCEPTS_COMMAND_CALL;
|
|
20732
|
+
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
20733
|
}
|
|
20595
20734
|
|
|
20596
20735
|
return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
|
|
@@ -20736,7 +20875,7 @@ parse_constant(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t fl
|
|
|
20736
20875
|
* fact a method call, not a constant read. */
|
|
20737
20876
|
if (
|
|
20738
20877
|
match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
|
|
20739
|
-
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (
|
|
20878
|
+
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
|
|
20740
20879
|
(pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
|
|
20741
20880
|
match1(parser, PM_TOKEN_BRACE_LEFT)
|
|
20742
20881
|
) {
|
|
@@ -20996,7 +21135,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, u
|
|
|
20996
21135
|
case PM_TOKEN_KEYWORD_CLASS:
|
|
20997
21136
|
return parse_class(parser, flags, depth);
|
|
20998
21137
|
case PM_TOKEN_KEYWORD_DEF:
|
|
20999
|
-
return parse_def(parser,
|
|
21138
|
+
return parse_def(parser, flags, depth);
|
|
21000
21139
|
case PM_TOKEN_KEYWORD_DEFINED:
|
|
21001
21140
|
return UP(parse_defined(parser, flags, depth));
|
|
21002
21141
|
case PM_TOKEN_KEYWORD_END_UPCASE:
|
|
@@ -21138,6 +21277,18 @@ parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t de
|
|
|
21138
21277
|
}
|
|
21139
21278
|
}
|
|
21140
21279
|
|
|
21280
|
+
/**
|
|
21281
|
+
* A command is a complete value of a write: it cannot begin a multi-value
|
|
21282
|
+
* right-hand side (`x = Foo::Bar { }, y`), nor stand as the first element of
|
|
21283
|
+
* one when nested in another write (`x = y = def f = foo 1 rescue z, w`).
|
|
21284
|
+
*/
|
|
21285
|
+
static void
|
|
21286
|
+
parse_write_value_comma_check(pm_parser_t *parser, const pm_node_t *value) {
|
|
21287
|
+
if (match1(parser, PM_TOKEN_COMMA) && pm_command_before_comma_p(parser, value)) {
|
|
21288
|
+
pm_parser_err_current(parser, PM_ERR_INVALID_COMMA);
|
|
21289
|
+
}
|
|
21290
|
+
}
|
|
21291
|
+
|
|
21141
21292
|
/**
|
|
21142
21293
|
* Parse a value that is going to be written to some kind of variable or method
|
|
21143
21294
|
* call. We need to handle this separately because the rescue modifier is
|
|
@@ -21149,7 +21300,21 @@ parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t de
|
|
|
21149
21300
|
*/
|
|
21150
21301
|
static pm_node_t *
|
|
21151
21302
|
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
|
-
|
|
21303
|
+
/* Where an operator write accepts a command call it also accepts an
|
|
21304
|
+
* endless method with a command body (`x += def f = foo 1`). */
|
|
21305
|
+
uint8_t command_flags = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
|
|
21306
|
+
? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
|
|
21307
|
+
: (previous_binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
|
|
21308
|
+
if (command_flags) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
|
|
21309
|
+
|
|
21310
|
+
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));
|
|
21311
|
+
|
|
21312
|
+
/* An operator write never takes a multi-value right-hand side, so a comma
|
|
21313
|
+
* after a statement-level one is already reported by the statement that
|
|
21314
|
+
* contains it. */
|
|
21315
|
+
if (previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
|
|
21316
|
+
parse_write_value_comma_check(parser, value);
|
|
21317
|
+
}
|
|
21153
21318
|
|
|
21154
21319
|
// Assignments whose value is a command call (e.g., a = b c) can only
|
|
21155
21320
|
// be followed by modifiers (if/unless/while/until/rescue) and not by
|
|
@@ -21253,12 +21418,21 @@ parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding
|
|
|
21253
21418
|
? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
|
|
21254
21419
|
: ((previous_binding_power < PM_BINDING_POWER_MODIFIER || statement_level) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
|
|
21255
21420
|
|
|
21421
|
+
/* Where a single-target write accepts a command call it also accepts an
|
|
21422
|
+
* endless method with a command body (`x = def f = foo 1`), but a
|
|
21423
|
+
* multi-write accepts only the command call. */
|
|
21424
|
+
if (command_call_flag && binding_power != PM_BINDING_POWER_MULTI_ASSIGNMENT + 1) {
|
|
21425
|
+
command_call_flag |= PM_PARSE_ACCEPTS_COMMAND_DEF;
|
|
21426
|
+
}
|
|
21427
|
+
|
|
21256
21428
|
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
21429
|
if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
|
|
21258
21430
|
|
|
21259
21431
|
parse_assignment_value_local(parser, value);
|
|
21260
21432
|
bool single_value = true;
|
|
21261
21433
|
|
|
21434
|
+
parse_write_value_comma_check(parser, value);
|
|
21435
|
+
|
|
21262
21436
|
// Block calls (command call + do block, e.g., `foo bar do end`) cannot
|
|
21263
21437
|
// be followed by a comma to form a multi-value RHS because each element
|
|
21264
21438
|
// of a multi-value assignment must be an `arg`, not a `block_call`.
|
|
@@ -21360,7 +21534,7 @@ parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement,
|
|
|
21360
21534
|
// splat right-hand side (`b = c, d` / `b = *c`); a comma _before_ an
|
|
21361
21535
|
// `=`, or a parenthesized target list (`(b, c), d = 1`), instead
|
|
21362
21536
|
// 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));
|
|
21537
|
+
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
21538
|
multiple = match1(parser, PM_TOKEN_COMMA) || PM_NODE_TYPE_P(value, PM_MULTI_TARGET_NODE);
|
|
21365
21539
|
}
|
|
21366
21540
|
|
|
@@ -21657,7 +21831,7 @@ parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *c
|
|
|
21657
21831
|
pm_token_t token = { .type = 0, .start = start, .end = end };
|
|
21658
21832
|
pm_location_t token_loc = TOK2LOC(parser, &token);
|
|
21659
21833
|
pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &token_loc, name, depth == -1 ? 0 : (uint32_t) depth));
|
|
21660
|
-
|
|
21834
|
+
pm_node_list_append_inline(parser->arena, &name_data->match->targets, target);
|
|
21661
21835
|
}
|
|
21662
21836
|
|
|
21663
21837
|
pm_buffer_cleanup(&unescaped);
|
|
@@ -21735,7 +21909,7 @@ parse_constant_path_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power
|
|
|
21735
21909
|
|
|
21736
21910
|
if (
|
|
21737
21911
|
(parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
|
|
21738
|
-
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (
|
|
21912
|
+
((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type))
|
|
21739
21913
|
) {
|
|
21740
21914
|
/* If we have a constant immediately following a '::' operator,
|
|
21741
21915
|
* then this can either be a constant path or a method call,
|
|
@@ -21826,9 +22000,7 @@ parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_pow
|
|
|
21826
22000
|
}
|
|
21827
22001
|
|
|
21828
22002
|
if (!interpolated && total_length > 0) {
|
|
21829
|
-
|
|
21830
|
-
if (!memory) abort();
|
|
21831
|
-
|
|
22003
|
+
uint8_t *memory = (uint8_t *) pm_arena_alloc(&parser->metadata_arena, total_length, PRISM_ALIGNOF(uint8_t));
|
|
21832
22004
|
uint8_t *cursor = memory;
|
|
21833
22005
|
PM_NODE_LIST_FOREACH(parts, index, part) {
|
|
21834
22006
|
pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
|
|
@@ -21838,11 +22010,9 @@ parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_pow
|
|
|
21838
22010
|
cursor += length;
|
|
21839
22011
|
}
|
|
21840
22012
|
|
|
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);
|
|
22013
|
+
pm_string_t content;
|
|
22014
|
+
pm_string_init(&content, memory, total_length);
|
|
22015
|
+
result = parse_interpolated_regular_expression_named_captures(parser, &content, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
|
|
21846
22016
|
}
|
|
21847
22017
|
} else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
|
|
21848
22018
|
/* If we have a regular expression node, then we can parse the named
|
|
@@ -21852,7 +22022,7 @@ parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_pow
|
|
|
21852
22022
|
.call = call,
|
|
21853
22023
|
.match = NULL,
|
|
21854
22024
|
.names = { 0 },
|
|
21855
|
-
.shared = regexp->unescaped
|
|
22025
|
+
.shared = pm_string_source(®exp->unescaped) == parser->start + regexp->content_loc.start,
|
|
21856
22026
|
};
|
|
21857
22027
|
|
|
21858
22028
|
pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data));
|
|
@@ -22709,7 +22879,7 @@ parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) {
|
|
|
22709
22879
|
return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
|
|
22710
22880
|
case PM_DEF_NODE:
|
|
22711
22881
|
// An endless method whose body is a command-style call (e.g.,
|
|
22712
|
-
// `def f = foo bar`) is a command
|
|
22882
|
+
// `def f = foo bar`) is a command write and can only be
|
|
22713
22883
|
// followed by modifiers.
|
|
22714
22884
|
return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(parser, node);
|
|
22715
22885
|
case PM_RESCUE_MODIFIER_NODE:
|
|
@@ -22864,7 +23034,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
22864
23034
|
cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
|
|
22865
23035
|
)
|
|
22866
23036
|
) {
|
|
22867
|
-
flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
|
|
23037
|
+
flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
|
|
22868
23038
|
}
|
|
22869
23039
|
break;
|
|
22870
23040
|
}
|
|
@@ -22872,7 +23042,7 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t
|
|
|
22872
23042
|
case PM_CONSTANT_PATH_NODE:
|
|
22873
23043
|
break;
|
|
22874
23044
|
default:
|
|
22875
|
-
flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
|
|
23045
|
+
flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
|
|
22876
23046
|
break;
|
|
22877
23047
|
}
|
|
22878
23048
|
}
|
|
@@ -23378,17 +23548,8 @@ pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_op
|
|
|
23378
23548
|
*/
|
|
23379
23549
|
void
|
|
23380
23550
|
pm_parser_cleanup(pm_parser_t *parser) {
|
|
23381
|
-
pm_string_cleanup(&parser->filepath);
|
|
23382
23551
|
pm_arena_cleanup(&parser->metadata_arena);
|
|
23383
23552
|
|
|
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
23553
|
while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
|
|
23393
23554
|
lex_mode_pop(parser);
|
|
23394
23555
|
}
|
|
@@ -23728,6 +23889,7 @@ pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t s
|
|
|
23728
23889
|
pm_serialize_header(buffer);
|
|
23729
23890
|
pm_serialize_encoding(parser.encoding, buffer);
|
|
23730
23891
|
pm_buffer_append_varsint(buffer, parser.start_line);
|
|
23892
|
+
pm_buffer_append_uint64(buffer, pm_parser_checksum(&parser));
|
|
23731
23893
|
pm_serialize_line_offset_list(&parser.line_offsets, buffer);
|
|
23732
23894
|
pm_serialize_comment_list(&parser.comment_list, buffer);
|
|
23733
23895
|
|