Ruby 4.0.9p0 (2026-10-23 revision 3e93b23afcec3433f6aecc34b5d832799d754238)
prism.c
1#include "prism.h"
2
6const char *
7pm_version(void) {
8 return PRISM_VERSION;
9}
10
15#define PM_TAB_WHITESPACE_SIZE 8
16
17// Macros for min/max.
18#define MIN(a,b) (((a)<(b))?(a):(b))
19#define MAX(a,b) (((a)>(b))?(a):(b))
20
21/******************************************************************************/
22/* Helpful AST-related macros */
23/******************************************************************************/
24
25#define FL PM_NODE_FLAGS
26#define UP PM_NODE_UPCAST
27
28#define PM_TOKEN_START(token_) ((token_)->start)
29#define PM_TOKEN_END(token_) ((token_)->end)
30
31#define PM_NODE_START(node_) (UP(node_)->location.start)
32#define PM_NODE_END(node_) (UP(node_)->location.end)
33
34#define PM_LOCATION_NULL_VALUE(parser_) ((pm_location_t) { .start = (parser_)->start, .end = (parser_)->start })
35#define PM_LOCATION_TOKEN_VALUE(token_) ((pm_location_t) { .start = PM_TOKEN_START(token_), .end = PM_TOKEN_END(token_) })
36#define PM_LOCATION_NODE_VALUE(node_) ((pm_location_t) { .start = PM_NODE_START(node_), .end = PM_NODE_END(node_) })
37#define PM_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == PM_TOKEN_NOT_PROVIDED ? ((pm_location_t) { 0 }) : PM_LOCATION_TOKEN_VALUE(token))
38
39/******************************************************************************/
40/* Lex mode manipulations */
41/******************************************************************************/
42
47static inline uint8_t
48lex_mode_incrementor(const uint8_t start) {
49 switch (start) {
50 case '(':
51 case '[':
52 case '{':
53 case '<':
54 return start;
55 default:
56 return '\0';
57 }
58}
59
64static inline uint8_t
65lex_mode_terminator(const uint8_t start) {
66 switch (start) {
67 case '(':
68 return ')';
69 case '[':
70 return ']';
71 case '{':
72 return '}';
73 case '<':
74 return '>';
75 default:
76 return start;
77 }
78}
79
85static bool
86lex_mode_push(pm_parser_t *parser, pm_lex_mode_t lex_mode) {
87 lex_mode.prev = parser->lex_modes.current;
88 parser->lex_modes.index++;
89
90 if (parser->lex_modes.index > PM_LEX_STACK_SIZE - 1) {
91 parser->lex_modes.current = (pm_lex_mode_t *) xmalloc(sizeof(pm_lex_mode_t));
92 if (parser->lex_modes.current == NULL) return false;
93
94 *parser->lex_modes.current = lex_mode;
95 } else {
96 parser->lex_modes.stack[parser->lex_modes.index] = lex_mode;
97 parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index];
98 }
99
100 return true;
101}
102
106static inline bool
107lex_mode_push_list(pm_parser_t *parser, bool interpolation, uint8_t delimiter) {
108 uint8_t incrementor = lex_mode_incrementor(delimiter);
109 uint8_t terminator = lex_mode_terminator(delimiter);
110
111 pm_lex_mode_t lex_mode = {
112 .mode = PM_LEX_LIST,
113 .as.list = {
114 .nesting = 0,
115 .interpolation = interpolation,
116 .incrementor = incrementor,
117 .terminator = terminator
118 }
119 };
120
121 // These are the places where we need to split up the content of the list.
122 // We'll use strpbrk to find the first of these characters.
123 uint8_t *breakpoints = lex_mode.as.list.breakpoints;
124 memcpy(breakpoints, "\\ \t\f\r\v\n\0\0\0", sizeof(lex_mode.as.list.breakpoints));
125 size_t index = 7;
126
127 // Now we'll add the terminator to the list of breakpoints. If the
128 // terminator is not already a NULL byte, add it to the list.
129 if (terminator != '\0') {
130 breakpoints[index++] = terminator;
131 }
132
133 // If interpolation is allowed, then we're going to check for the #
134 // character. Otherwise we'll only look for escapes and the terminator.
135 if (interpolation) {
136 breakpoints[index++] = '#';
137 }
138
139 // If there is an incrementor, then we'll check for that as well.
140 if (incrementor != '\0') {
141 breakpoints[index++] = incrementor;
142 }
143
144 parser->explicit_encoding = NULL;
145 return lex_mode_push(parser, lex_mode);
146}
147
153static inline bool
154lex_mode_push_list_eof(pm_parser_t *parser) {
155 return lex_mode_push_list(parser, false, '\0');
156}
157
161static inline bool
162lex_mode_push_regexp(pm_parser_t *parser, uint8_t incrementor, uint8_t terminator) {
163 pm_lex_mode_t lex_mode = {
164 .mode = PM_LEX_REGEXP,
165 .as.regexp = {
166 .nesting = 0,
167 .incrementor = incrementor,
168 .terminator = terminator
169 }
170 };
171
172 // These are the places where we need to split up the content of the
173 // regular expression. We'll use strpbrk to find the first of these
174 // characters.
175 uint8_t *breakpoints = lex_mode.as.regexp.breakpoints;
176 memcpy(breakpoints, "\r\n\\#\0\0", sizeof(lex_mode.as.regexp.breakpoints));
177 size_t index = 4;
178
179 // First we'll add the terminator.
180 if (terminator != '\0') {
181 breakpoints[index++] = terminator;
182 }
183
184 // Next, if there is an incrementor, then we'll check for that as well.
185 if (incrementor != '\0') {
186 breakpoints[index++] = incrementor;
187 }
188
189 parser->explicit_encoding = NULL;
190 return lex_mode_push(parser, lex_mode);
191}
192
196static inline bool
197lex_mode_push_string(pm_parser_t *parser, bool interpolation, bool label_allowed, uint8_t incrementor, uint8_t terminator) {
198 pm_lex_mode_t lex_mode = {
199 .mode = PM_LEX_STRING,
200 .as.string = {
201 .nesting = 0,
202 .interpolation = interpolation,
203 .label_allowed = label_allowed,
204 .incrementor = incrementor,
205 .terminator = terminator
206 }
207 };
208
209 // These are the places where we need to split up the content of the
210 // string. We'll use strpbrk to find the first of these characters.
211 uint8_t *breakpoints = lex_mode.as.string.breakpoints;
212 memcpy(breakpoints, "\r\n\\\0\0\0", sizeof(lex_mode.as.string.breakpoints));
213 size_t index = 3;
214
215 // Now add in the terminator. If the terminator is not already a NULL byte,
216 // then we'll add it.
217 if (terminator != '\0') {
218 breakpoints[index++] = terminator;
219 }
220
221 // If interpolation is allowed, then we're going to check for the #
222 // character. Otherwise we'll only look for escapes and the terminator.
223 if (interpolation) {
224 breakpoints[index++] = '#';
225 }
226
227 // If we have an incrementor, then we'll add that in as a breakpoint as
228 // well.
229 if (incrementor != '\0') {
230 breakpoints[index++] = incrementor;
231 }
232
233 parser->explicit_encoding = NULL;
234 return lex_mode_push(parser, lex_mode);
235}
236
242static inline bool
243lex_mode_push_string_eof(pm_parser_t *parser) {
244 return lex_mode_push_string(parser, false, false, '\0', '\0');
245}
246
252static void
253lex_mode_pop(pm_parser_t *parser) {
254 if (parser->lex_modes.index == 0) {
255 parser->lex_modes.current->mode = PM_LEX_DEFAULT;
256 } else if (parser->lex_modes.index < PM_LEX_STACK_SIZE) {
257 parser->lex_modes.index--;
258 parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index];
259 } else {
260 parser->lex_modes.index--;
261 pm_lex_mode_t *prev = parser->lex_modes.current->prev;
262 xfree(parser->lex_modes.current);
263 parser->lex_modes.current = prev;
264 }
265}
266
270static inline bool
271lex_state_p(const pm_parser_t *parser, pm_lex_state_t state) {
272 return parser->lex_state & state;
273}
274
275typedef enum {
276 PM_IGNORED_NEWLINE_NONE = 0,
277 PM_IGNORED_NEWLINE_ALL,
278 PM_IGNORED_NEWLINE_PATTERN
279} pm_ignored_newline_type_t;
280
281static inline pm_ignored_newline_type_t
282lex_state_ignored_p(pm_parser_t *parser) {
283 bool ignored = lex_state_p(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_CLASS | PM_LEX_STATE_FNAME | PM_LEX_STATE_DOT) && !lex_state_p(parser, PM_LEX_STATE_LABELED);
284
285 if (ignored) {
286 return PM_IGNORED_NEWLINE_ALL;
287 } else if ((parser->lex_state & ~((unsigned int) PM_LEX_STATE_LABEL)) == (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)) {
288 return PM_IGNORED_NEWLINE_PATTERN;
289 } else {
290 return PM_IGNORED_NEWLINE_NONE;
291 }
292}
293
294static inline bool
295lex_state_beg_p(pm_parser_t *parser) {
296 return lex_state_p(parser, PM_LEX_STATE_BEG_ANY) || ((parser->lex_state & (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)) == (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED));
297}
298
299static inline bool
300lex_state_arg_p(pm_parser_t *parser) {
301 return lex_state_p(parser, PM_LEX_STATE_ARG_ANY);
302}
303
304static inline bool
305lex_state_spcarg_p(pm_parser_t *parser, bool space_seen) {
306 if (parser->current.end >= parser->end) {
307 return false;
308 }
309 return lex_state_arg_p(parser) && space_seen && !pm_char_is_whitespace(*parser->current.end);
310}
311
312static inline bool
313lex_state_end_p(pm_parser_t *parser) {
314 return lex_state_p(parser, PM_LEX_STATE_END_ANY);
315}
316
320static inline bool
321lex_state_operator_p(pm_parser_t *parser) {
322 return lex_state_p(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_DOT);
323}
324
329static inline void
330lex_state_set(pm_parser_t *parser, pm_lex_state_t state) {
331 parser->lex_state = state;
332}
333
334#ifndef PM_DEBUG_LOGGING
339#define PM_DEBUG_LOGGING 0
340#endif
341
342#if PM_DEBUG_LOGGING
343PRISM_ATTRIBUTE_UNUSED static void
344debug_state(pm_parser_t *parser) {
345 fprintf(stderr, "STATE: ");
346 bool first = true;
347
348 if (parser->lex_state == PM_LEX_STATE_NONE) {
349 fprintf(stderr, "NONE\n");
350 return;
351 }
352
353#define CHECK_STATE(state) \
354 if (parser->lex_state & state) { \
355 if (!first) fprintf(stderr, "|"); \
356 fprintf(stderr, "%s", #state); \
357 first = false; \
358 }
359
360 CHECK_STATE(PM_LEX_STATE_BEG)
361 CHECK_STATE(PM_LEX_STATE_END)
362 CHECK_STATE(PM_LEX_STATE_ENDARG)
363 CHECK_STATE(PM_LEX_STATE_ENDFN)
364 CHECK_STATE(PM_LEX_STATE_ARG)
365 CHECK_STATE(PM_LEX_STATE_CMDARG)
366 CHECK_STATE(PM_LEX_STATE_MID)
367 CHECK_STATE(PM_LEX_STATE_FNAME)
368 CHECK_STATE(PM_LEX_STATE_DOT)
369 CHECK_STATE(PM_LEX_STATE_CLASS)
370 CHECK_STATE(PM_LEX_STATE_LABEL)
371 CHECK_STATE(PM_LEX_STATE_LABELED)
372 CHECK_STATE(PM_LEX_STATE_FITEM)
373
374#undef CHECK_STATE
375
376 fprintf(stderr, "\n");
377}
378
379static void
380debug_lex_state_set(pm_parser_t *parser, pm_lex_state_t state, char const * caller_name, int line_number) {
381 fprintf(stderr, "Caller: %s:%d\nPrevious: ", caller_name, line_number);
382 debug_state(parser);
383 lex_state_set(parser, state);
384 fprintf(stderr, "Now: ");
385 debug_state(parser);
386 fprintf(stderr, "\n");
387}
388
389#define lex_state_set(parser, state) debug_lex_state_set(parser, state, __func__, __LINE__)
390#endif
391
392/******************************************************************************/
393/* Command-line macro helpers */
394/******************************************************************************/
395
397#define PM_PARSER_COMMAND_LINE_OPTION(parser, option) ((parser)->command_line & (option))
398
400#define PM_PARSER_COMMAND_LINE_OPTION_A(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_A)
401
403#define PM_PARSER_COMMAND_LINE_OPTION_E(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_E)
404
406#define PM_PARSER_COMMAND_LINE_OPTION_L(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_L)
407
409#define PM_PARSER_COMMAND_LINE_OPTION_N(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_N)
410
412#define PM_PARSER_COMMAND_LINE_OPTION_P(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_P)
413
415#define PM_PARSER_COMMAND_LINE_OPTION_X(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_X)
416
417/******************************************************************************/
418/* Diagnostic-related functions */
419/******************************************************************************/
420
424static inline void
425pm_parser_err(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id) {
426 pm_diagnostic_list_append(&parser->error_list, start, end, diag_id);
427}
428
432#define PM_PARSER_ERR_FORMAT(parser, start, end, diag_id, ...) \
433 pm_diagnostic_list_append_format(&parser->error_list, start, end, diag_id, __VA_ARGS__)
434
439static inline void
440pm_parser_err_current(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
441 pm_parser_err(parser, parser->current.start, parser->current.end, diag_id);
442}
443
448#define PM_PARSER_ERR_LOCATION_FORMAT(parser, location, diag_id, ...) \
449 PM_PARSER_ERR_FORMAT(parser, (location)->start, (location)->end, diag_id, __VA_ARGS__)
450
455static inline void
456pm_parser_err_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) {
457 pm_parser_err(parser, node->location.start, node->location.end, diag_id);
458}
459
464#define PM_PARSER_ERR_NODE_FORMAT(parser, node, diag_id, ...) \
465 PM_PARSER_ERR_FORMAT(parser, (node)->location.start, (node)->location.end, diag_id, __VA_ARGS__)
466
471#define PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, diag_id) \
472 PM_PARSER_ERR_NODE_FORMAT(parser, node, diag_id, (int) ((node)->location.end - (node)->location.start), (const char *) (node)->location.start)
473
478static inline void
479pm_parser_err_previous(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
480 pm_parser_err(parser, parser->previous.start, parser->previous.end, diag_id);
481}
482
487static inline void
488pm_parser_err_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) {
489 pm_parser_err(parser, token->start, token->end, diag_id);
490}
491
496#define PM_PARSER_ERR_TOKEN_FORMAT(parser, token, diag_id, ...) \
497 PM_PARSER_ERR_FORMAT(parser, (token).start, (token).end, diag_id, __VA_ARGS__)
498
503#define PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, diag_id) \
504 PM_PARSER_ERR_TOKEN_FORMAT(parser, token, diag_id, (int) ((token).end - (token).start), (const char *) (token).start)
505
509static inline void
510pm_parser_warn(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, pm_diagnostic_id_t diag_id) {
511 pm_diagnostic_list_append(&parser->warning_list, start, end, diag_id);
512}
513
518static inline void
519pm_parser_warn_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) {
520 pm_parser_warn(parser, token->start, token->end, diag_id);
521}
522
527static inline void
528pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) {
529 pm_parser_warn(parser, node->location.start, node->location.end, diag_id);
530}
531
535#define PM_PARSER_WARN_FORMAT(parser, start, end, diag_id, ...) \
536 pm_diagnostic_list_append_format(&parser->warning_list, start, end, diag_id, __VA_ARGS__)
537
542#define PM_PARSER_WARN_TOKEN_FORMAT(parser, token, diag_id, ...) \
543 PM_PARSER_WARN_FORMAT(parser, (token).start, (token).end, diag_id, __VA_ARGS__)
544
549#define PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, token, diag_id) \
550 PM_PARSER_WARN_TOKEN_FORMAT(parser, token, diag_id, (int) ((token).end - (token).start), (const char *) (token).start)
551
556#define PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, ...) \
557 PM_PARSER_WARN_FORMAT(parser, (node)->location.start, (node)->location.end, diag_id, __VA_ARGS__)
558
564static void
565pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
566 PM_PARSER_ERR_FORMAT(
567 parser,
568 ident_start,
569 ident_start + ident_length,
570 PM_ERR_HEREDOC_TERM,
571 (int) ident_length,
572 (const char *) ident_start
573 );
574}
575
576/******************************************************************************/
577/* Scope-related functions */
578/******************************************************************************/
579
583static bool
584pm_parser_scope_push(pm_parser_t *parser, bool closed) {
585 pm_scope_t *scope = (pm_scope_t *) xmalloc(sizeof(pm_scope_t));
586 if (scope == NULL) return false;
587
588 *scope = (pm_scope_t) {
589 .previous = parser->current_scope,
590 .locals = { 0 },
591 .parameters = PM_SCOPE_PARAMETERS_NONE,
592 .implicit_parameters = { 0 },
593 .shareable_constant = parser->current_scope == NULL ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant,
594 .closed = closed
595 };
596
597 parser->current_scope = scope;
598 return true;
599}
600
605static bool
606pm_parser_scope_toplevel_p(pm_parser_t *parser) {
607 pm_scope_t *scope = parser->current_scope;
608
609 do {
610 if (scope->previous == NULL) return true;
611 if (scope->closed) return false;
612 } while ((scope = scope->previous) != NULL);
613
614 assert(false && "unreachable");
615 return true;
616}
617
621static pm_scope_t *
622pm_parser_scope_find(pm_parser_t *parser, uint32_t depth) {
623 pm_scope_t *scope = parser->current_scope;
624
625 while (depth-- > 0) {
626 assert(scope != NULL);
627 scope = scope->previous;
628 }
629
630 return scope;
631}
632
633typedef enum {
634 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS,
635 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT,
636 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL
637} pm_scope_forwarding_param_check_result_t;
638
639static pm_scope_forwarding_param_check_result_t
640pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const uint8_t mask) {
641 pm_scope_t *scope = parser->current_scope;
642 bool conflict = false;
643
644 while (scope != NULL) {
645 if (scope->parameters & mask) {
646 if (scope->closed) {
647 if (conflict) {
648 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT;
649 } else {
650 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS;
651 }
652 }
653
654 conflict = true;
655 }
656
657 if (scope->closed) break;
658 scope = scope->previous;
659 }
660
661 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL;
662}
663
664static void
665pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) {
666 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK)) {
667 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
668 // Pass.
669 break;
670 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
671 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_AMPERSAND);
672 break;
673 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
674 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND);
675 break;
676 }
677}
678
679static void
680pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) {
681 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS)) {
682 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
683 // Pass.
684 break;
685 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
686 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR);
687 break;
688 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
689 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR);
690 break;
691 }
692}
693
694static void
695pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t *token) {
696 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_ALL)) {
697 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
698 // Pass.
699 break;
700 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
701 // This shouldn't happen, because ... is not allowed in the
702 // declaration of blocks. If we get here, we assume we already have
703 // an error for this.
704 break;
705 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
706 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES);
707 break;
708 }
709}
710
711static void
712pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) {
713 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS)) {
714 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
715 // Pass.
716 break;
717 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
718 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR_STAR);
719 break;
720 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
721 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR);
722 break;
723 }
724}
725
730pm_parser_scope_shareable_constant_get(pm_parser_t *parser) {
731 return parser->current_scope->shareable_constant;
732}
733
738static void
739pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constant_value_t shareable_constant) {
740 pm_scope_t *scope = parser->current_scope;
741
742 do {
743 scope->shareable_constant = shareable_constant;
744 } while (!scope->closed && (scope = scope->previous) != NULL);
745}
746
747/******************************************************************************/
748/* Local variable-related functions */
749/******************************************************************************/
750
754#define PM_LOCALS_HASH_THRESHOLD 9
755
756static void
757pm_locals_free(pm_locals_t *locals) {
758 if (locals->capacity > 0) {
759 xfree(locals->locals);
760 }
761}
762
767static uint32_t
768pm_locals_hash(pm_constant_id_t name) {
769 name = ((name >> 16) ^ name) * 0x45d9f3b;
770 name = ((name >> 16) ^ name) * 0x45d9f3b;
771 name = (name >> 16) ^ name;
772 return name;
773}
774
779static void
780pm_locals_resize(pm_locals_t *locals) {
781 uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2);
782 assert(next_capacity > locals->capacity);
783
784 pm_local_t *next_locals = xcalloc(next_capacity, sizeof(pm_local_t));
785 if (next_locals == NULL) abort();
786
787 if (next_capacity < PM_LOCALS_HASH_THRESHOLD) {
788 if (locals->size > 0) {
789 memcpy(next_locals, locals->locals, locals->size * sizeof(pm_local_t));
790 }
791 } else {
792 // If we just switched from a list to a hash, then we need to fill in
793 // the hash values of all of the locals.
794 bool hash_needed = (locals->capacity <= PM_LOCALS_HASH_THRESHOLD);
795 uint32_t mask = next_capacity - 1;
796
797 for (uint32_t index = 0; index < locals->capacity; index++) {
798 pm_local_t *local = &locals->locals[index];
799
800 if (local->name != PM_CONSTANT_ID_UNSET) {
801 if (hash_needed) local->hash = pm_locals_hash(local->name);
802
803 uint32_t hash = local->hash;
804 while (next_locals[hash & mask].name != PM_CONSTANT_ID_UNSET) hash++;
805 next_locals[hash & mask] = *local;
806 }
807 }
808 }
809
810 pm_locals_free(locals);
811 locals->locals = next_locals;
812 locals->capacity = next_capacity;
813}
814
830static bool
831pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, const uint8_t *start, const uint8_t *end, uint32_t reads) {
832 if (locals->size >= (locals->capacity / 4 * 3)) {
833 pm_locals_resize(locals);
834 }
835
836 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
837 for (uint32_t index = 0; index < locals->capacity; index++) {
838 pm_local_t *local = &locals->locals[index];
839
840 if (local->name == PM_CONSTANT_ID_UNSET) {
841 *local = (pm_local_t) {
842 .name = name,
843 .location = { .start = start, .end = end },
844 .index = locals->size++,
845 .reads = reads,
846 .hash = 0
847 };
848 return true;
849 } else if (local->name == name) {
850 return false;
851 }
852 }
853 } else {
854 uint32_t mask = locals->capacity - 1;
855 uint32_t hash = pm_locals_hash(name);
856 uint32_t initial_hash = hash;
857
858 do {
859 pm_local_t *local = &locals->locals[hash & mask];
860
861 if (local->name == PM_CONSTANT_ID_UNSET) {
862 *local = (pm_local_t) {
863 .name = name,
864 .location = { .start = start, .end = end },
865 .index = locals->size++,
866 .reads = reads,
867 .hash = initial_hash
868 };
869 return true;
870 } else if (local->name == name) {
871 return false;
872 } else {
873 hash++;
874 }
875 } while ((hash & mask) != initial_hash);
876 }
877
878 assert(false && "unreachable");
879 return true;
880}
881
886static uint32_t
887pm_locals_find(pm_locals_t *locals, pm_constant_id_t name) {
888 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
889 for (uint32_t index = 0; index < locals->size; index++) {
890 pm_local_t *local = &locals->locals[index];
891 if (local->name == name) return index;
892 }
893 } else {
894 uint32_t mask = locals->capacity - 1;
895 uint32_t hash = pm_locals_hash(name);
896 uint32_t initial_hash = hash & mask;
897
898 do {
899 pm_local_t *local = &locals->locals[hash & mask];
900
901 if (local->name == PM_CONSTANT_ID_UNSET) {
902 return UINT32_MAX;
903 } else if (local->name == name) {
904 return hash & mask;
905 } else {
906 hash++;
907 }
908 } while ((hash & mask) != initial_hash);
909 }
910
911 return UINT32_MAX;
912}
913
918static void
919pm_locals_read(pm_locals_t *locals, pm_constant_id_t name) {
920 uint32_t index = pm_locals_find(locals, name);
921 assert(index != UINT32_MAX);
922
923 pm_local_t *local = &locals->locals[index];
924 assert(local->reads < UINT32_MAX);
925
926 local->reads++;
927}
928
933static void
934pm_locals_unread(pm_locals_t *locals, pm_constant_id_t name) {
935 uint32_t index = pm_locals_find(locals, name);
936 assert(index != UINT32_MAX);
937
938 pm_local_t *local = &locals->locals[index];
939 assert(local->reads > 0);
940
941 local->reads--;
942}
943
947static uint32_t
948pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) {
949 uint32_t index = pm_locals_find(locals, name);
950 assert(index != UINT32_MAX);
951
952 return locals->locals[index].reads;
953}
954
963static void
964pm_locals_order(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) {
965 pm_constant_id_list_init_capacity(list, locals->size);
966
967 // If we're still below the threshold for switching to a hash, then we only
968 // need to loop over the locals until we hit the size because the locals are
969 // stored in a list.
970 uint32_t capacity = locals->capacity < PM_LOCALS_HASH_THRESHOLD ? locals->size : locals->capacity;
971
972 // We will only warn for unused variables if we're not at the top level, or
973 // if we're parsing a file outside of eval or -e.
974 bool warn_unused = !toplevel || (!parser->parsing_eval && !PM_PARSER_COMMAND_LINE_OPTION_E(parser));
975
976 for (uint32_t index = 0; index < capacity; index++) {
977 pm_local_t *local = &locals->locals[index];
978
979 if (local->name != PM_CONSTANT_ID_UNSET) {
980 pm_constant_id_list_insert(list, (size_t) local->index, local->name);
981
982 if (warn_unused && local->reads == 0 && ((parser->start_line >= 0) || (pm_newline_list_line(&parser->newline_list, local->location.start, parser->start_line) >= 0))) {
983 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name);
984
985 if (constant->length >= 1 && *constant->start != '_') {
986 PM_PARSER_WARN_FORMAT(
987 parser,
988 local->location.start,
989 local->location.end,
990 PM_WARN_UNUSED_LOCAL_VARIABLE,
991 (int) constant->length,
992 (const char *) constant->start
993 );
994 }
995 }
996 }
997 }
998}
999
1000/******************************************************************************/
1001/* Node-related functions */
1002/******************************************************************************/
1003
1007static inline pm_constant_id_t
1008pm_parser_constant_id_location(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
1009 return pm_constant_pool_insert_shared(&parser->constant_pool, start, (size_t) (end - start));
1010}
1011
1015static inline pm_constant_id_t
1016pm_parser_constant_id_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
1017 return pm_constant_pool_insert_owned(&parser->constant_pool, start, length);
1018}
1019
1023static inline pm_constant_id_t
1024pm_parser_constant_id_constant(pm_parser_t *parser, const char *start, size_t length) {
1025 return pm_constant_pool_insert_constant(&parser->constant_pool, (const uint8_t *) start, length);
1026}
1027
1031static inline pm_constant_id_t
1032pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
1033 return pm_parser_constant_id_location(parser, token->start, token->end);
1034}
1035
1040static inline pm_constant_id_t
1041pm_parser_optional_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
1042 return token->type == PM_TOKEN_NOT_PROVIDED ? 0 : pm_parser_constant_id_token(parser, token);
1043}
1044
1050static pm_node_t *
1051pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
1052 pm_node_t *void_node = NULL;
1053
1054 while (node != NULL) {
1055 switch (PM_NODE_TYPE(node)) {
1056 case PM_RETURN_NODE:
1057 case PM_BREAK_NODE:
1058 case PM_NEXT_NODE:
1059 case PM_REDO_NODE:
1060 case PM_RETRY_NODE:
1061 case PM_MATCH_REQUIRED_NODE:
1062 return void_node != NULL ? void_node : node;
1063 case PM_MATCH_PREDICATE_NODE:
1064 return NULL;
1065 case PM_BEGIN_NODE: {
1066 pm_begin_node_t *cast = (pm_begin_node_t *) node;
1067
1068 if (cast->ensure_clause != NULL) {
1069 if (cast->rescue_clause != NULL) {
1070 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->rescue_clause));
1071 if (vn != NULL) return vn;
1072 }
1073
1074 if (cast->statements != NULL) {
1075 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1076 if (vn != NULL) return vn;
1077 }
1078
1079 node = UP(cast->ensure_clause);
1080 } else if (cast->rescue_clause != NULL) {
1081 if (cast->statements == NULL) return NULL;
1082
1083 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1084 if (vn == NULL) return NULL;
1085 if (void_node == NULL) void_node = vn;
1086
1087 for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) {
1088 pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements));
1089 if (vn == NULL) {
1090 void_node = NULL;
1091 break;
1092 }
1093 if (void_node == NULL) {
1094 void_node = vn;
1095 }
1096 }
1097
1098 if (cast->else_clause != NULL) {
1099 node = UP(cast->else_clause);
1100 } else {
1101 return void_node;
1102 }
1103 } else {
1104 node = UP(cast->statements);
1105 }
1106
1107 break;
1108 }
1109 case PM_ENSURE_NODE: {
1110 pm_ensure_node_t *cast = (pm_ensure_node_t *) node;
1111 node = UP(cast->statements);
1112 break;
1113 }
1114 case PM_PARENTHESES_NODE: {
1115 pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node;
1116 node = UP(cast->body);
1117 break;
1118 }
1119 case PM_STATEMENTS_NODE: {
1120 pm_statements_node_t *cast = (pm_statements_node_t *) node;
1121 node = cast->body.nodes[cast->body.size - 1];
1122 break;
1123 }
1124 case PM_IF_NODE: {
1125 pm_if_node_t *cast = (pm_if_node_t *) node;
1126 if (cast->statements == NULL || cast->subsequent == NULL) {
1127 return NULL;
1128 }
1129 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1130 if (vn == NULL) {
1131 return NULL;
1132 }
1133 if (void_node == NULL) {
1134 void_node = vn;
1135 }
1136 node = cast->subsequent;
1137 break;
1138 }
1139 case PM_UNLESS_NODE: {
1140 pm_unless_node_t *cast = (pm_unless_node_t *) node;
1141 if (cast->statements == NULL || cast->else_clause == NULL) {
1142 return NULL;
1143 }
1144 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1145 if (vn == NULL) {
1146 return NULL;
1147 }
1148 if (void_node == NULL) {
1149 void_node = vn;
1150 }
1151 node = UP(cast->else_clause);
1152 break;
1153 }
1154 case PM_ELSE_NODE: {
1155 pm_else_node_t *cast = (pm_else_node_t *) node;
1156 node = UP(cast->statements);
1157 break;
1158 }
1159 case PM_AND_NODE: {
1160 pm_and_node_t *cast = (pm_and_node_t *) node;
1161 node = cast->left;
1162 break;
1163 }
1164 case PM_OR_NODE: {
1165 pm_or_node_t *cast = (pm_or_node_t *) node;
1166 node = cast->left;
1167 break;
1168 }
1169 case PM_LOCAL_VARIABLE_WRITE_NODE: {
1170 pm_local_variable_write_node_t *cast = (pm_local_variable_write_node_t *) node;
1171
1172 pm_scope_t *scope = parser->current_scope;
1173 for (uint32_t depth = 0; depth < cast->depth; depth++) scope = scope->previous;
1174
1175 pm_locals_read(&scope->locals, cast->name);
1176 return NULL;
1177 }
1178 default:
1179 return NULL;
1180 }
1181 }
1182
1183 return NULL;
1184}
1185
1186static inline void
1187pm_assert_value_expression(pm_parser_t *parser, pm_node_t *node) {
1188 pm_node_t *void_node = pm_check_value_expression(parser, node);
1189 if (void_node != NULL) {
1190 pm_parser_err_node(parser, void_node, PM_ERR_VOID_EXPRESSION);
1191 }
1192}
1193
1197static void
1198pm_void_statement_check(pm_parser_t *parser, const pm_node_t *node) {
1199 const char *type = NULL;
1200 int length = 0;
1201
1202 switch (PM_NODE_TYPE(node)) {
1203 case PM_BACK_REFERENCE_READ_NODE:
1204 case PM_CLASS_VARIABLE_READ_NODE:
1205 case PM_GLOBAL_VARIABLE_READ_NODE:
1206 case PM_INSTANCE_VARIABLE_READ_NODE:
1207 case PM_LOCAL_VARIABLE_READ_NODE:
1208 case PM_NUMBERED_REFERENCE_READ_NODE:
1209 type = "a variable";
1210 length = 10;
1211 break;
1212 case PM_CALL_NODE: {
1213 const pm_call_node_t *cast = (const pm_call_node_t *) node;
1214 if (cast->call_operator_loc.start != NULL || cast->message_loc.start == NULL) break;
1215
1216 const pm_constant_t *message = pm_constant_pool_id_to_constant(&parser->constant_pool, cast->name);
1217 switch (message->length) {
1218 case 1:
1219 switch (message->start[0]) {
1220 case '+':
1221 case '-':
1222 case '*':
1223 case '/':
1224 case '%':
1225 case '|':
1226 case '^':
1227 case '&':
1228 case '>':
1229 case '<':
1230 type = (const char *) message->start;
1231 length = 1;
1232 break;
1233 }
1234 break;
1235 case 2:
1236 switch (message->start[1]) {
1237 case '=':
1238 if (message->start[0] == '<' || message->start[0] == '>' || message->start[0] == '!' || message->start[0] == '=') {
1239 type = (const char *) message->start;
1240 length = 2;
1241 }
1242 break;
1243 case '@':
1244 if (message->start[0] == '+' || message->start[0] == '-') {
1245 type = (const char *) message->start;
1246 length = 2;
1247 }
1248 break;
1249 case '*':
1250 if (message->start[0] == '*') {
1251 type = (const char *) message->start;
1252 length = 2;
1253 }
1254 break;
1255 }
1256 break;
1257 case 3:
1258 if (memcmp(message->start, "<=>", 3) == 0) {
1259 type = "<=>";
1260 length = 3;
1261 }
1262 break;
1263 }
1264
1265 break;
1266 }
1267 case PM_CONSTANT_PATH_NODE:
1268 type = "::";
1269 length = 2;
1270 break;
1271 case PM_CONSTANT_READ_NODE:
1272 type = "a constant";
1273 length = 10;
1274 break;
1275 case PM_DEFINED_NODE:
1276 type = "defined?";
1277 length = 8;
1278 break;
1279 case PM_FALSE_NODE:
1280 type = "false";
1281 length = 5;
1282 break;
1283 case PM_FLOAT_NODE:
1284 case PM_IMAGINARY_NODE:
1285 case PM_INTEGER_NODE:
1286 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1287 case PM_INTERPOLATED_STRING_NODE:
1288 case PM_RATIONAL_NODE:
1289 case PM_REGULAR_EXPRESSION_NODE:
1290 case PM_SOURCE_ENCODING_NODE:
1291 case PM_SOURCE_FILE_NODE:
1292 case PM_SOURCE_LINE_NODE:
1293 case PM_STRING_NODE:
1294 case PM_SYMBOL_NODE:
1295 type = "a literal";
1296 length = 9;
1297 break;
1298 case PM_NIL_NODE:
1299 type = "nil";
1300 length = 3;
1301 break;
1302 case PM_RANGE_NODE: {
1303 const pm_range_node_t *cast = (const pm_range_node_t *) node;
1304
1305 if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) {
1306 type = "...";
1307 length = 3;
1308 } else {
1309 type = "..";
1310 length = 2;
1311 }
1312
1313 break;
1314 }
1315 case PM_SELF_NODE:
1316 type = "self";
1317 length = 4;
1318 break;
1319 case PM_TRUE_NODE:
1320 type = "true";
1321 length = 4;
1322 break;
1323 default:
1324 break;
1325 }
1326
1327 if (type != NULL) {
1328 PM_PARSER_WARN_NODE_FORMAT(parser, node, PM_WARN_VOID_STATEMENT, length, type);
1329 }
1330}
1331
1336static void
1337pm_void_statements_check(pm_parser_t *parser, const pm_statements_node_t *node, bool last_value) {
1338 assert(node->body.size > 0);
1339 const size_t size = node->body.size - (last_value ? 1 : 0);
1340 for (size_t index = 0; index < size; index++) {
1341 pm_void_statement_check(parser, node->body.nodes[index]);
1342 }
1343}
1344
1350typedef enum {
1351 PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL,
1352 PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP,
1353 PM_CONDITIONAL_PREDICATE_TYPE_NOT
1354} pm_conditional_predicate_type_t;
1355
1359static void
1360pm_parser_warn_conditional_predicate_literal(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type, pm_diagnostic_id_t diag_id, const char *prefix) {
1361 switch (type) {
1362 case PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL:
1363 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "condition");
1364 break;
1365 case PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP:
1366 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "flip-flop");
1367 break;
1368 case PM_CONDITIONAL_PREDICATE_TYPE_NOT:
1369 break;
1370 }
1371}
1372
1377static bool
1378pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) {
1379 switch (PM_NODE_TYPE(node)) {
1380 case PM_ARRAY_NODE: {
1381 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1382
1383 const pm_array_node_t *cast = (const pm_array_node_t *) node;
1384 for (size_t index = 0; index < cast->elements.size; index++) {
1385 if (!pm_conditional_predicate_warn_write_literal_p(cast->elements.nodes[index])) return false;
1386 }
1387
1388 return true;
1389 }
1390 case PM_HASH_NODE: {
1391 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1392
1393 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
1394 for (size_t index = 0; index < cast->elements.size; index++) {
1395 const pm_node_t *element = cast->elements.nodes[index];
1396 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return false;
1397
1398 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1399 if (!pm_conditional_predicate_warn_write_literal_p(assoc->key) || !pm_conditional_predicate_warn_write_literal_p(assoc->value)) return false;
1400 }
1401
1402 return true;
1403 }
1404 case PM_FALSE_NODE:
1405 case PM_FLOAT_NODE:
1406 case PM_IMAGINARY_NODE:
1407 case PM_INTEGER_NODE:
1408 case PM_NIL_NODE:
1409 case PM_RATIONAL_NODE:
1410 case PM_REGULAR_EXPRESSION_NODE:
1411 case PM_SOURCE_ENCODING_NODE:
1412 case PM_SOURCE_FILE_NODE:
1413 case PM_SOURCE_LINE_NODE:
1414 case PM_STRING_NODE:
1415 case PM_SYMBOL_NODE:
1416 case PM_TRUE_NODE:
1417 return true;
1418 default:
1419 return false;
1420 }
1421}
1422
1427static inline void
1428pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) {
1429 if (pm_conditional_predicate_warn_write_literal_p(node)) {
1430 pm_parser_warn_node(parser, node, parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL);
1431 }
1432}
1433
1446static void
1447pm_conditional_predicate(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type) {
1448 switch (PM_NODE_TYPE(node)) {
1449 case PM_AND_NODE: {
1450 pm_and_node_t *cast = (pm_and_node_t *) node;
1451 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1452 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1453 break;
1454 }
1455 case PM_OR_NODE: {
1456 pm_or_node_t *cast = (pm_or_node_t *) node;
1457 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1458 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1459 break;
1460 }
1461 case PM_PARENTHESES_NODE: {
1462 pm_parentheses_node_t *cast = (pm_parentheses_node_t *) node;
1463
1464 if ((cast->body != NULL) && PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE)) {
1465 pm_statements_node_t *statements = (pm_statements_node_t *) cast->body;
1466 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1467 }
1468
1469 break;
1470 }
1471 case PM_BEGIN_NODE: {
1472 pm_begin_node_t *cast = (pm_begin_node_t *) node;
1473 if (cast->statements != NULL) {
1474 pm_statements_node_t *statements = cast->statements;
1475 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1476 }
1477 break;
1478 }
1479 case PM_RANGE_NODE: {
1480 pm_range_node_t *cast = (pm_range_node_t *) node;
1481
1482 if (cast->left != NULL) pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1483 if (cast->right != NULL) pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1484
1485 // Here we change the range node into a flip flop node. We can do
1486 // this since the nodes are exactly the same except for the type.
1487 // We're only asserting against the size when we should probably
1488 // assert against the entire layout, but we'll assume tests will
1489 // catch this.
1490 assert(sizeof(pm_range_node_t) == sizeof(pm_flip_flop_node_t));
1491 node->type = PM_FLIP_FLOP_NODE;
1492
1493 break;
1494 }
1495 case PM_REGULAR_EXPRESSION_NODE:
1496 // Here we change the regular expression node into a match last line
1497 // node. We can do this since the nodes are exactly the same except
1498 // for the type.
1499 assert(sizeof(pm_regular_expression_node_t) == sizeof(pm_match_last_line_node_t));
1500 node->type = PM_MATCH_LAST_LINE_NODE;
1501
1502 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1503 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "regex ");
1504 }
1505
1506 break;
1507 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1508 // Here we change the interpolated regular expression node into an
1509 // interpolated match last line node. We can do this since the nodes
1510 // are exactly the same except for the type.
1511 assert(sizeof(pm_interpolated_regular_expression_node_t) == sizeof(pm_interpolated_match_last_line_node_t));
1512 node->type = PM_INTERPOLATED_MATCH_LAST_LINE_NODE;
1513
1514 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1515 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "regex ");
1516 }
1517
1518 break;
1519 case PM_INTEGER_NODE:
1520 if (type == PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP) {
1521 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1522 pm_parser_warn_node(parser, node, PM_WARN_INTEGER_IN_FLIP_FLOP);
1523 }
1524 } else {
1525 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1526 }
1527 break;
1528 case PM_STRING_NODE:
1529 case PM_SOURCE_FILE_NODE:
1530 case PM_INTERPOLATED_STRING_NODE:
1531 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "string ");
1532 break;
1533 case PM_SYMBOL_NODE:
1534 case PM_INTERPOLATED_SYMBOL_NODE:
1535 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "symbol ");
1536 break;
1537 case PM_SOURCE_LINE_NODE:
1538 case PM_SOURCE_ENCODING_NODE:
1539 case PM_FLOAT_NODE:
1540 case PM_RATIONAL_NODE:
1541 case PM_IMAGINARY_NODE:
1542 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1543 break;
1544 case PM_CLASS_VARIABLE_WRITE_NODE:
1545 pm_conditional_predicate_warn_write_literal(parser, ((pm_class_variable_write_node_t *) node)->value);
1546 break;
1547 case PM_CONSTANT_WRITE_NODE:
1548 pm_conditional_predicate_warn_write_literal(parser, ((pm_constant_write_node_t *) node)->value);
1549 break;
1550 case PM_GLOBAL_VARIABLE_WRITE_NODE:
1551 pm_conditional_predicate_warn_write_literal(parser, ((pm_global_variable_write_node_t *) node)->value);
1552 break;
1553 case PM_INSTANCE_VARIABLE_WRITE_NODE:
1554 pm_conditional_predicate_warn_write_literal(parser, ((pm_instance_variable_write_node_t *) node)->value);
1555 break;
1556 case PM_LOCAL_VARIABLE_WRITE_NODE:
1557 pm_conditional_predicate_warn_write_literal(parser, ((pm_local_variable_write_node_t *) node)->value);
1558 break;
1559 case PM_MULTI_WRITE_NODE:
1560 pm_conditional_predicate_warn_write_literal(parser, ((pm_multi_write_node_t *) node)->value);
1561 break;
1562 default:
1563 break;
1564 }
1565}
1566
1575static inline pm_token_t
1576not_provided(pm_parser_t *parser) {
1577 return (pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start };
1578}
1579
1586typedef struct {
1589
1591 pm_arguments_node_t *arguments;
1592
1595
1597 pm_node_t *block;
1598
1602
1606static inline const uint8_t *
1607pm_arguments_end(pm_arguments_t *arguments) {
1608 if (arguments->block != NULL) {
1609 const uint8_t *end = arguments->block->location.end;
1610 if (arguments->closing_loc.start != NULL && arguments->closing_loc.end > end) {
1611 end = arguments->closing_loc.end;
1612 }
1613 return end;
1614 }
1615 if (arguments->closing_loc.start != NULL) {
1616 return arguments->closing_loc.end;
1617 }
1618 if (arguments->arguments != NULL) {
1619 return arguments->arguments->base.location.end;
1620 }
1621 return arguments->closing_loc.end;
1622}
1623
1628static void
1629pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_block_node_t *block) {
1630 // First, check that we have arguments and that we don't have a closing
1631 // location for them.
1632 if (arguments->arguments == NULL || arguments->closing_loc.start != NULL) {
1633 return;
1634 }
1635
1636 // Next, check that we don't have a single parentheses argument. This would
1637 // look like:
1638 //
1639 // foo (1) {}
1640 //
1641 // In this case, it's actually okay for the block to be attached to the
1642 // call, even though it looks like it's attached to the argument.
1643 if (arguments->arguments->arguments.size == 1 && PM_NODE_TYPE_P(arguments->arguments->arguments.nodes[0], PM_PARENTHESES_NODE)) {
1644 return;
1645 }
1646
1647 // If we didn't hit a case before this check, then at this point we need to
1648 // add a syntax error.
1649 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_UNEXPECTED_BLOCK);
1650}
1651
1652/******************************************************************************/
1653/* Basic character checks */
1654/******************************************************************************/
1655
1662static inline size_t
1663char_is_identifier_start(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
1664 if (n <= 0) return 0;
1665
1666 if (parser->encoding_changed) {
1667 size_t width;
1668
1669 if ((width = parser->encoding->alpha_char(b, n)) != 0) {
1670 return width;
1671 } else if (*b == '_') {
1672 return 1;
1673 } else if (*b >= 0x80) {
1674 return parser->encoding->char_width(b, n);
1675 } else {
1676 return 0;
1677 }
1678 } else if (*b < 0x80) {
1679 return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT ? 1 : 0) || (*b == '_');
1680 } else {
1681 return pm_encoding_utf_8_char_width(b, n);
1682 }
1683}
1684
1689static inline size_t
1690char_is_identifier_utf8(const uint8_t *b, ptrdiff_t n) {
1691 if (n <= 0) {
1692 return 0;
1693 } else if (*b < 0x80) {
1694 return (*b == '_') || (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT ? 1 : 0);
1695 } else {
1696 return pm_encoding_utf_8_char_width(b, n);
1697 }
1698}
1699
1705static inline size_t
1706char_is_identifier(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
1707 if (n <= 0) {
1708 return 0;
1709 } else if (parser->encoding_changed) {
1710 size_t width;
1711
1712 if ((width = parser->encoding->alnum_char(b, n)) != 0) {
1713 return width;
1714 } else if (*b == '_') {
1715 return 1;
1716 } else if (*b >= 0x80) {
1717 return parser->encoding->char_width(b, n);
1718 } else {
1719 return 0;
1720 }
1721 } else {
1722 return char_is_identifier_utf8(b, n);
1723 }
1724}
1725
1726// Here we're defining a perfect hash for the characters that are allowed in
1727// global names. This is used to quickly check the next character after a $ to
1728// see if it's a valid character for a global name.
1729#define BIT(c, idx) (((c) / 32 - 1 == idx) ? (1U << ((c) % 32)) : 0)
1730#define PUNCT(idx) ( \
1731 BIT('~', idx) | BIT('*', idx) | BIT('$', idx) | BIT('?', idx) | \
1732 BIT('!', idx) | BIT('@', idx) | BIT('/', idx) | BIT('\\', idx) | \
1733 BIT(';', idx) | BIT(',', idx) | BIT('.', idx) | BIT('=', idx) | \
1734 BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \
1735 BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \
1736 BIT('0', idx))
1737
1738const unsigned int pm_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { PUNCT(0), PUNCT(1), PUNCT(2) };
1739
1740#undef BIT
1741#undef PUNCT
1742
1743static inline bool
1744char_is_global_name_punctuation(const uint8_t b) {
1745 const unsigned int i = (const unsigned int) b;
1746 if (i <= 0x20 || 0x7e < i) return false;
1747
1748 return (pm_global_name_punctuation_hash[(i - 0x20) / 32] >> (i % 32)) & 1;
1749}
1750
1751static inline bool
1752token_is_setter_name(pm_token_t *token) {
1753 return (
1754 (token->type == PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL) ||
1755 ((token->type == PM_TOKEN_IDENTIFIER) &&
1756 (token->end - token->start >= 2) &&
1757 (token->end[-1] == '='))
1758 );
1759}
1760
1764static bool
1765pm_local_is_keyword(const char *source, size_t length) {
1766#define KEYWORD(name) if (memcmp(source, name, length) == 0) return true
1767
1768 switch (length) {
1769 case 2:
1770 switch (source[0]) {
1771 case 'd': KEYWORD("do"); return false;
1772 case 'i': KEYWORD("if"); KEYWORD("in"); return false;
1773 case 'o': KEYWORD("or"); return false;
1774 default: return false;
1775 }
1776 case 3:
1777 switch (source[0]) {
1778 case 'a': KEYWORD("and"); return false;
1779 case 'd': KEYWORD("def"); return false;
1780 case 'e': KEYWORD("end"); return false;
1781 case 'f': KEYWORD("for"); return false;
1782 case 'n': KEYWORD("nil"); KEYWORD("not"); return false;
1783 default: return false;
1784 }
1785 case 4:
1786 switch (source[0]) {
1787 case 'c': KEYWORD("case"); return false;
1788 case 'e': KEYWORD("else"); return false;
1789 case 'n': KEYWORD("next"); return false;
1790 case 'r': KEYWORD("redo"); return false;
1791 case 's': KEYWORD("self"); return false;
1792 case 't': KEYWORD("then"); KEYWORD("true"); return false;
1793 case 'w': KEYWORD("when"); return false;
1794 default: return false;
1795 }
1796 case 5:
1797 switch (source[0]) {
1798 case 'a': KEYWORD("alias"); return false;
1799 case 'b': KEYWORD("begin"); KEYWORD("break"); return false;
1800 case 'c': KEYWORD("class"); return false;
1801 case 'e': KEYWORD("elsif"); return false;
1802 case 'f': KEYWORD("false"); return false;
1803 case 'r': KEYWORD("retry"); return false;
1804 case 's': KEYWORD("super"); return false;
1805 case 'u': KEYWORD("undef"); KEYWORD("until"); return false;
1806 case 'w': KEYWORD("while"); return false;
1807 case 'y': KEYWORD("yield"); return false;
1808 default: return false;
1809 }
1810 case 6:
1811 switch (source[0]) {
1812 case 'e': KEYWORD("ensure"); return false;
1813 case 'm': KEYWORD("module"); return false;
1814 case 'r': KEYWORD("rescue"); KEYWORD("return"); return false;
1815 case 'u': KEYWORD("unless"); return false;
1816 default: return false;
1817 }
1818 case 8:
1819 KEYWORD("__LINE__");
1820 KEYWORD("__FILE__");
1821 return false;
1822 case 12:
1823 KEYWORD("__ENCODING__");
1824 return false;
1825 default:
1826 return false;
1827 }
1828
1829#undef KEYWORD
1830}
1831
1832/******************************************************************************/
1833/* Node flag handling functions */
1834/******************************************************************************/
1835
1839static inline void
1840pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) {
1841 node->flags |= flag;
1842}
1843
1847static inline void
1848pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) {
1849 node->flags &= (pm_node_flags_t) ~flag;
1850}
1851
1855static inline void
1856pm_node_flag_set_repeated_parameter(pm_node_t *node) {
1857 assert(PM_NODE_TYPE(node) == PM_BLOCK_LOCAL_VARIABLE_NODE ||
1858 PM_NODE_TYPE(node) == PM_BLOCK_PARAMETER_NODE ||
1859 PM_NODE_TYPE(node) == PM_KEYWORD_REST_PARAMETER_NODE ||
1860 PM_NODE_TYPE(node) == PM_OPTIONAL_KEYWORD_PARAMETER_NODE ||
1861 PM_NODE_TYPE(node) == PM_OPTIONAL_PARAMETER_NODE ||
1862 PM_NODE_TYPE(node) == PM_REQUIRED_KEYWORD_PARAMETER_NODE ||
1863 PM_NODE_TYPE(node) == PM_REQUIRED_PARAMETER_NODE ||
1864 PM_NODE_TYPE(node) == PM_REST_PARAMETER_NODE);
1865
1866 pm_node_flag_set(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER);
1867}
1868
1869/******************************************************************************/
1870/* Node creation functions */
1871/******************************************************************************/
1872
1878#define PM_REGULAR_EXPRESSION_ENCODING_MASK ~(PM_REGULAR_EXPRESSION_FLAGS_EUC_JP | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J | PM_REGULAR_EXPRESSION_FLAGS_UTF_8)
1879
1883static inline pm_node_flags_t
1884pm_regular_expression_flags_create(pm_parser_t *parser, const pm_token_t *closing) {
1885 pm_node_flags_t flags = 0;
1886
1887 if (closing->type == PM_TOKEN_REGEXP_END) {
1888 pm_buffer_t unknown_flags = { 0 };
1889
1890 for (const uint8_t *flag = closing->start + 1; flag < closing->end; flag++) {
1891 switch (*flag) {
1892 case 'i': flags |= PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE; break;
1893 case 'm': flags |= PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE; break;
1894 case 'x': flags |= PM_REGULAR_EXPRESSION_FLAGS_EXTENDED; break;
1895 case 'o': flags |= PM_REGULAR_EXPRESSION_FLAGS_ONCE; break;
1896
1897 case 'e': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_EUC_JP); break;
1898 case 'n': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT); break;
1899 case 's': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J); break;
1900 case 'u': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_UTF_8); break;
1901
1902 default: pm_buffer_append_byte(&unknown_flags, *flag);
1903 }
1904 }
1905
1906 size_t unknown_flags_length = pm_buffer_length(&unknown_flags);
1907 if (unknown_flags_length != 0) {
1908 const char *word = unknown_flags_length >= 2 ? "options" : "option";
1909 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_REGEXP_UNKNOWN_OPTIONS, word, unknown_flags_length, pm_buffer_value(&unknown_flags));
1910 }
1911 pm_buffer_free(&unknown_flags);
1912 }
1913
1914 return flags;
1915}
1916
1917#undef PM_REGULAR_EXPRESSION_ENCODING_MASK
1918
1919static pm_statements_node_t *
1920pm_statements_node_create(pm_parser_t *parser);
1921
1922static void
1923pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline);
1924
1925static size_t
1926pm_statements_node_body_length(pm_statements_node_t *node);
1927
1932static inline void *
1933pm_node_alloc(PRISM_ATTRIBUTE_UNUSED pm_parser_t *parser, size_t size) {
1934 void *memory = xcalloc(1, size);
1935 if (memory == NULL) {
1936 fprintf(stderr, "Failed to allocate %d bytes\n", (int) size);
1937 abort();
1938 }
1939 return memory;
1940}
1941
1942#define PM_NODE_ALLOC(parser_, type_) (type_ *) pm_node_alloc(parser_, sizeof(type_))
1943#define PM_NODE_INIT(parser_, type_, flags_, start_, end_) (pm_node_t) { \
1944 .type = (type_), \
1945 .flags = (flags_), \
1946 .node_id = ++(parser_)->node_id, \
1947 .location = { .start = (start_), .end = (end_) } \
1948}
1949
1950#define PM_NODE_INIT_UNSET(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, NULL, NULL)
1951#define PM_NODE_INIT_BASE(parser_, type_, flags_) PM_NODE_INIT(parser_, type_, flags_, (parser_)->start, (parser_)->start)
1952#define PM_NODE_INIT_TOKEN(parser_, type_, flags_, token_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(token_), PM_TOKEN_END(token_))
1953#define PM_NODE_INIT_NODE(parser_, type_, flags_, node_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(node_), PM_NODE_END(node_))
1954
1955#define PM_NODE_INIT_TOKENS(parser_, type_, flags_, left_, right_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(left_), PM_TOKEN_END(right_))
1956#define PM_NODE_INIT_NODES(parser_, type_, flags_, left_, right_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(left_), PM_NODE_END(right_))
1957#define PM_NODE_INIT_TOKEN_NODE(parser_, type_, flags_, token_, node_) PM_NODE_INIT(parser_, type_, flags_, PM_TOKEN_START(token_), PM_NODE_END(node_))
1958#define PM_NODE_INIT_NODE_TOKEN(parser_, type_, flags_, node_, token_) PM_NODE_INIT(parser_, type_, flags_, PM_NODE_START(node_), PM_TOKEN_END(token_))
1959
1963static pm_missing_node_t *
1964pm_missing_node_create(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
1965 pm_missing_node_t *node = PM_NODE_ALLOC(parser, pm_missing_node_t);
1966
1967 *node = (pm_missing_node_t) {
1968 .base = PM_NODE_INIT(parser, PM_MISSING_NODE, 0, start, end)
1969 };
1970
1971 return node;
1972}
1973
1977static pm_alias_global_variable_node_t *
1978pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
1979 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
1980 pm_alias_global_variable_node_t *node = PM_NODE_ALLOC(parser, pm_alias_global_variable_node_t);
1981
1982 *node = (pm_alias_global_variable_node_t) {
1983 .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_ALIAS_GLOBAL_VARIABLE_NODE, 0, keyword, old_name),
1984 .new_name = new_name,
1985 .old_name = old_name,
1986 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
1987 };
1988
1989 return node;
1990}
1991
1995static pm_alias_method_node_t *
1996pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
1997 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
1998 pm_alias_method_node_t *node = PM_NODE_ALLOC(parser, pm_alias_method_node_t);
1999
2000 *node = (pm_alias_method_node_t) {
2001 .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_ALIAS_METHOD_NODE, 0, keyword, old_name),
2002 .new_name = new_name,
2003 .old_name = old_name,
2004 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
2005 };
2006
2007 return node;
2008}
2009
2013static pm_alternation_pattern_node_t *
2014pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node_t *right, const pm_token_t *operator) {
2015 pm_alternation_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_alternation_pattern_node_t);
2016
2017 *node = (pm_alternation_pattern_node_t) {
2018 .base = PM_NODE_INIT_NODES(parser, PM_ALTERNATION_PATTERN_NODE, 0, left, right),
2019 .left = left,
2020 .right = right,
2021 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
2022 };
2023
2024 return node;
2025}
2026
2030static pm_and_node_t *
2031pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
2032 pm_assert_value_expression(parser, left);
2033
2034 pm_and_node_t *node = PM_NODE_ALLOC(parser, pm_and_node_t);
2035
2036 *node = (pm_and_node_t) {
2037 .base = PM_NODE_INIT_NODES(parser, PM_AND_NODE, 0, left, right),
2038 .left = left,
2039 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
2040 .right = right
2041 };
2042
2043 return node;
2044}
2045
2049static pm_arguments_node_t *
2050pm_arguments_node_create(pm_parser_t *parser) {
2051 pm_arguments_node_t *node = PM_NODE_ALLOC(parser, pm_arguments_node_t);
2052
2053 *node = (pm_arguments_node_t) {
2054 .base = PM_NODE_INIT_BASE(parser, PM_ARGUMENTS_NODE, 0),
2055 .arguments = { 0 }
2056 };
2057
2058 return node;
2059}
2060
2064static size_t
2065pm_arguments_node_size(pm_arguments_node_t *node) {
2066 return node->arguments.size;
2067}
2068
2072static void
2073pm_arguments_node_arguments_append(pm_arguments_node_t *node, pm_node_t *argument) {
2074 if (pm_arguments_node_size(node) == 0) {
2075 node->base.location.start = argument->location.start;
2076 }
2077
2078 if (node->base.location.end < argument->location.end) {
2079 node->base.location.end = argument->location.end;
2080 }
2081
2082 pm_node_list_append(&node->arguments, argument);
2083
2084 if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) {
2085 if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
2086 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS);
2087 } else {
2088 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT);
2089 }
2090 }
2091}
2092
2096static pm_array_node_t *
2097pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) {
2098 pm_array_node_t *node = PM_NODE_ALLOC(parser, pm_array_node_t);
2099
2100 *node = (pm_array_node_t) {
2101 .base = PM_NODE_INIT_TOKEN(parser, PM_ARRAY_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening),
2102 .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
2103 .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
2104 .elements = { 0 }
2105 };
2106
2107 return node;
2108}
2109
2113static inline void
2114pm_array_node_elements_append(pm_array_node_t *node, pm_node_t *element) {
2115 if (!node->elements.size && !node->opening_loc.start) {
2116 node->base.location.start = element->location.start;
2117 }
2118
2119 pm_node_list_append(&node->elements, element);
2120 node->base.location.end = element->location.end;
2121
2122 // If the element is not a static literal, then the array is not a static
2123 // literal. Turn that flag off.
2124 if (PM_NODE_TYPE_P(element, PM_ARRAY_NODE) || PM_NODE_TYPE_P(element, PM_HASH_NODE) || PM_NODE_TYPE_P(element, PM_RANGE_NODE) || !PM_NODE_FLAG_P(element, PM_NODE_FLAG_STATIC_LITERAL)) {
2125 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
2126 }
2127
2128 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
2129 pm_node_flag_set(UP(node), PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT);
2130 }
2131}
2132
2136static void
2137pm_array_node_close_set(pm_array_node_t *node, const pm_token_t *closing) {
2138 assert(closing->type == PM_TOKEN_BRACKET_RIGHT || closing->type == PM_TOKEN_STRING_END || closing->type == PM_TOKEN_MISSING || closing->type == PM_TOKEN_NOT_PROVIDED);
2139 node->base.location.end = closing->end;
2140 node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing);
2141}
2142
2147static pm_array_pattern_node_t *
2148pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *nodes) {
2149 pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t);
2150
2151 *node = (pm_array_pattern_node_t) {
2152 .base = PM_NODE_INIT_NODES(parser, PM_ARRAY_PATTERN_NODE, 0, nodes->nodes[0], nodes->nodes[nodes->size - 1]),
2153 .constant = NULL,
2154 .rest = NULL,
2155 .requireds = { 0 },
2156 .posts = { 0 },
2157 .opening_loc = { 0 },
2158 .closing_loc = { 0 }
2159 };
2160
2161 // For now we're going to just copy over each pointer manually. This could be
2162 // much more efficient, as we could instead resize the node list.
2163 bool found_rest = false;
2164 pm_node_t *child;
2165
2166 PM_NODE_LIST_FOREACH(nodes, index, child) {
2167 if (!found_rest && (PM_NODE_TYPE_P(child, PM_SPLAT_NODE) || PM_NODE_TYPE_P(child, PM_IMPLICIT_REST_NODE))) {
2168 node->rest = child;
2169 found_rest = true;
2170 } else if (found_rest) {
2171 pm_node_list_append(&node->posts, child);
2172 } else {
2173 pm_node_list_append(&node->requireds, child);
2174 }
2175 }
2176
2177 return node;
2178}
2179
2183static pm_array_pattern_node_t *
2184pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) {
2185 pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t);
2186
2187 *node = (pm_array_pattern_node_t) {
2188 .base = PM_NODE_INIT_NODE(parser, PM_ARRAY_PATTERN_NODE, 0, rest),
2189 .constant = NULL,
2190 .rest = rest,
2191 .requireds = { 0 },
2192 .posts = { 0 },
2193 .opening_loc = { 0 },
2194 .closing_loc = { 0 }
2195 };
2196
2197 return node;
2198}
2199
2204static pm_array_pattern_node_t *
2205pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, const pm_token_t *opening, const pm_token_t *closing) {
2206 pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t);
2207
2208 *node = (pm_array_pattern_node_t) {
2209 .base = PM_NODE_INIT_NODE_TOKEN(parser, PM_ARRAY_PATTERN_NODE, 0, constant, closing),
2210 .constant = constant,
2211 .rest = NULL,
2212 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
2213 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
2214 .requireds = { 0 },
2215 .posts = { 0 }
2216 };
2217
2218 return node;
2219}
2220
2225static pm_array_pattern_node_t *
2226pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
2227 pm_array_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_array_pattern_node_t);
2228
2229 *node = (pm_array_pattern_node_t) {
2230 .base = PM_NODE_INIT_TOKENS(parser, PM_ARRAY_PATTERN_NODE, 0, opening, closing),
2231 .constant = NULL,
2232 .rest = NULL,
2233 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
2234 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
2235 .requireds = { 0 },
2236 .posts = { 0 }
2237 };
2238
2239 return node;
2240}
2241
2242static inline void
2243pm_array_pattern_node_requireds_append(pm_array_pattern_node_t *node, pm_node_t *inner) {
2244 pm_node_list_append(&node->requireds, inner);
2245}
2246
2250static pm_assoc_node_t *
2251pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *operator, pm_node_t *value) {
2252 pm_assoc_node_t *node = PM_NODE_ALLOC(parser, pm_assoc_node_t);
2253 const uint8_t *end;
2254
2255 if (value != NULL && value->location.end > key->location.end) {
2256 end = value->location.end;
2257 } else if (operator->type != PM_TOKEN_NOT_PROVIDED) {
2258 end = operator->end;
2259 } else {
2260 end = key->location.end;
2261 }
2262
2263 // Hash string keys will be frozen, so we can mark them as frozen here so
2264 // that the compiler picks them up and also when we check for static literal
2265 // on the keys it gets factored in.
2266 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
2267 key->flags |= PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL;
2268 }
2269
2270 // If the key and value of this assoc node are both static literals, then
2271 // we can mark this node as a static literal.
2272 pm_node_flags_t flags = 0;
2273 if (
2274 !PM_NODE_TYPE_P(key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(key, PM_HASH_NODE) && !PM_NODE_TYPE_P(key, PM_RANGE_NODE) &&
2275 value && !PM_NODE_TYPE_P(value, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(value, PM_HASH_NODE) && !PM_NODE_TYPE_P(value, PM_RANGE_NODE)
2276 ) {
2277 flags = key->flags & value->flags & PM_NODE_FLAG_STATIC_LITERAL;
2278 }
2279
2280 *node = (pm_assoc_node_t) {
2281 .base = PM_NODE_INIT(parser, PM_ASSOC_NODE, flags, key->location.start, end),
2282 .key = key,
2283 .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
2284 .value = value
2285 };
2286
2287 return node;
2288}
2289
2293static pm_assoc_splat_node_t *
2294pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token_t *operator) {
2295 assert(operator->type == PM_TOKEN_USTAR_STAR);
2296 pm_assoc_splat_node_t *node = PM_NODE_ALLOC(parser, pm_assoc_splat_node_t);
2297
2298 *node = (pm_assoc_splat_node_t) {
2299 .base = (
2300 (value == NULL)
2301 ? PM_NODE_INIT_TOKEN(parser, PM_ASSOC_SPLAT_NODE, 0, operator)
2302 : PM_NODE_INIT_TOKEN_NODE(parser, PM_ASSOC_SPLAT_NODE, 0, operator, value)
2303 ),
2304 .value = value,
2305 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
2306 };
2307
2308 return node;
2309}
2310
2314static pm_back_reference_read_node_t *
2315pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
2316 assert(name->type == PM_TOKEN_BACK_REFERENCE);
2317 pm_back_reference_read_node_t *node = PM_NODE_ALLOC(parser, pm_back_reference_read_node_t);
2318
2319 *node = (pm_back_reference_read_node_t) {
2320 .base = PM_NODE_INIT_TOKEN(parser, PM_BACK_REFERENCE_READ_NODE, 0, name),
2321 .name = pm_parser_constant_id_token(parser, name)
2322 };
2323
2324 return node;
2325}
2326
2330static pm_begin_node_t *
2331pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_statements_node_t *statements) {
2332 pm_begin_node_t *node = PM_NODE_ALLOC(parser, pm_begin_node_t);
2333
2334 *node = (pm_begin_node_t) {
2335 .base = (
2336 (statements == NULL)
2337 ? PM_NODE_INIT_TOKEN(parser, PM_BEGIN_NODE, 0, begin_keyword)
2338 : PM_NODE_INIT_TOKEN_NODE(parser, PM_BEGIN_NODE, 0, begin_keyword, statements)
2339 ),
2340 .begin_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(begin_keyword),
2341 .statements = statements,
2342 .end_keyword_loc = { 0 }
2343 };
2344
2345 return node;
2346}
2347
2351static void
2352pm_begin_node_rescue_clause_set(pm_begin_node_t *node, pm_rescue_node_t *rescue_clause) {
2353 // If the begin keyword doesn't exist, we set the start on the begin_node
2354 if (!node->begin_keyword_loc.start) {
2355 node->base.location.start = rescue_clause->base.location.start;
2356 }
2357 node->base.location.end = rescue_clause->base.location.end;
2358 node->rescue_clause = rescue_clause;
2359}
2360
2364static void
2365pm_begin_node_else_clause_set(pm_begin_node_t *node, pm_else_node_t *else_clause) {
2366 node->base.location.end = else_clause->base.location.end;
2367 node->else_clause = else_clause;
2368}
2369
2373static void
2374pm_begin_node_ensure_clause_set(pm_begin_node_t *node, pm_ensure_node_t *ensure_clause) {
2375 node->base.location.end = ensure_clause->base.location.end;
2376 node->ensure_clause = ensure_clause;
2377}
2378
2382static void
2383pm_begin_node_end_keyword_set(pm_begin_node_t *node, const pm_token_t *end_keyword) {
2384 assert(end_keyword->type == PM_TOKEN_KEYWORD_END || end_keyword->type == PM_TOKEN_MISSING);
2385
2386 node->base.location.end = end_keyword->end;
2387 node->end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword);
2388}
2389
2393static pm_block_argument_node_t *
2394pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
2395 pm_block_argument_node_t *node = PM_NODE_ALLOC(parser, pm_block_argument_node_t);
2396
2397 *node = (pm_block_argument_node_t) {
2398 .base = (
2399 (expression == NULL)
2400 ? PM_NODE_INIT_TOKEN(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator)
2401 : PM_NODE_INIT_TOKEN_NODE(parser, PM_BLOCK_ARGUMENT_NODE, 0, operator, expression)
2402 ),
2403 .expression = expression,
2404 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
2405 };
2406
2407 return node;
2408}
2409
2413static pm_block_node_t *
2414pm_block_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *opening, pm_node_t *parameters, pm_node_t *body, const pm_token_t *closing) {
2415 pm_block_node_t *node = PM_NODE_ALLOC(parser, pm_block_node_t);
2416
2417 *node = (pm_block_node_t) {
2418 .base = PM_NODE_INIT_TOKENS(parser, PM_BLOCK_NODE, 0, opening, closing),
2419 .locals = *locals,
2420 .parameters = parameters,
2421 .body = body,
2422 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
2423 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
2424 };
2425
2426 return node;
2427}
2428
2432static pm_block_parameter_node_t *
2433pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator) {
2434 assert(operator->type == PM_TOKEN_NOT_PROVIDED || operator->type == PM_TOKEN_UAMPERSAND || operator->type == PM_TOKEN_AMPERSAND);
2435 pm_block_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_block_parameter_node_t);
2436
2437 *node = (pm_block_parameter_node_t) {
2438 .base = (
2439 (name->type == PM_TOKEN_NOT_PROVIDED)
2440 ? PM_NODE_INIT_TOKEN(parser, PM_BLOCK_PARAMETER_NODE, 0, operator)
2441 : PM_NODE_INIT_TOKENS(parser, PM_BLOCK_PARAMETER_NODE, 0, operator, name)
2442 ),
2443 .name = pm_parser_optional_constant_id_token(parser, name),
2444 .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name),
2445 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
2446 };
2447
2448 return node;
2449}
2450
2454static pm_block_parameters_node_t *
2455pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *parameters, const pm_token_t *opening) {
2456 pm_block_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_block_parameters_node_t);
2457
2458 const uint8_t *start;
2459 if (opening->type != PM_TOKEN_NOT_PROVIDED) {
2460 start = opening->start;
2461 } else if (parameters != NULL) {
2462 start = parameters->base.location.start;
2463 } else {
2464 start = NULL;
2465 }
2466
2467 const uint8_t *end;
2468 if (parameters != NULL) {
2469 end = parameters->base.location.end;
2470 } else if (opening->type != PM_TOKEN_NOT_PROVIDED) {
2471 end = opening->end;
2472 } else {
2473 end = NULL;
2474 }
2475
2476 *node = (pm_block_parameters_node_t) {
2477 .base = PM_NODE_INIT(parser, PM_BLOCK_PARAMETERS_NODE, 0, start, end),
2478 .parameters = parameters,
2479 .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
2480 .closing_loc = { 0 },
2481 .locals = { 0 }
2482 };
2483
2484 return node;
2485}
2486
2490static void
2491pm_block_parameters_node_closing_set(pm_block_parameters_node_t *node, const pm_token_t *closing) {
2492 assert(closing->type == PM_TOKEN_PIPE || closing->type == PM_TOKEN_PARENTHESIS_RIGHT || closing->type == PM_TOKEN_MISSING);
2493
2494 node->base.location.end = closing->end;
2495 node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing);
2496}
2497
2501static pm_block_local_variable_node_t *
2502pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) {
2503 pm_block_local_variable_node_t *node = PM_NODE_ALLOC(parser, pm_block_local_variable_node_t);
2504
2505 *node = (pm_block_local_variable_node_t) {
2506 .base = PM_NODE_INIT_TOKEN(parser, PM_BLOCK_LOCAL_VARIABLE_NODE, 0, name),
2507 .name = pm_parser_constant_id_token(parser, name)
2508 };
2509
2510 return node;
2511}
2512
2516static void
2517pm_block_parameters_node_append_local(pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) {
2518 pm_node_list_append(&node->locals, UP(local));
2519
2520 if (node->base.location.start == NULL) node->base.location.start = local->base.location.start;
2521 node->base.location.end = local->base.location.end;
2522}
2523
2527static pm_break_node_t *
2528pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
2529 assert(keyword->type == PM_TOKEN_KEYWORD_BREAK);
2530 pm_break_node_t *node = PM_NODE_ALLOC(parser, pm_break_node_t);
2531
2532 *node = (pm_break_node_t) {
2533 .base = (
2534 (arguments == NULL)
2535 ? PM_NODE_INIT_TOKEN(parser, PM_BREAK_NODE, 0, keyword)
2536 : PM_NODE_INIT_TOKEN_NODE(parser, PM_BREAK_NODE, 0, keyword, arguments)
2537 ),
2538 .arguments = arguments,
2539 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
2540 };
2541
2542 return node;
2543}
2544
2545// There are certain flags that we want to use internally but don't want to
2546// expose because they are not relevant beyond parsing. Therefore we'll define
2547// them here and not define them in config.yml/a header file.
2548static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2);
2549
2550static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1);
2551static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2);
2552static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3);
2553
2559static pm_call_node_t *
2560pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) {
2561 pm_call_node_t *node = PM_NODE_ALLOC(parser, pm_call_node_t);
2562
2563 *node = (pm_call_node_t) {
2564 .base = PM_NODE_INIT_BASE(parser, PM_CALL_NODE, flags),
2565 .receiver = NULL,
2566 .call_operator_loc = { 0 },
2567 .message_loc = { 0 },
2568 .opening_loc = { 0 },
2569 .arguments = NULL,
2570 .closing_loc = { 0 },
2571 .equal_loc = { 0 },
2572 .block = NULL,
2573 .name = 0
2574 };
2575
2576 return node;
2577}
2578
2583static inline pm_node_flags_t
2584pm_call_node_ignore_visibility_flag(const pm_node_t *receiver) {
2585 return PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY : 0;
2586}
2587
2592static pm_call_node_t *
2593pm_call_node_aref_create(pm_parser_t *parser, pm_node_t *receiver, pm_arguments_t *arguments) {
2594 pm_assert_value_expression(parser, receiver);
2595
2596 pm_node_flags_t flags = pm_call_node_ignore_visibility_flag(receiver);
2597 if (arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_ARGUMENT_NODE)) {
2598 flags |= PM_CALL_NODE_FLAGS_INDEX;
2599 }
2600
2601 pm_call_node_t *node = pm_call_node_create(parser, flags);
2602
2603 node->base.location.start = receiver->location.start;
2604 node->base.location.end = pm_arguments_end(arguments);
2605
2606 node->receiver = receiver;
2607 node->message_loc.start = arguments->opening_loc.start;
2608 node->message_loc.end = arguments->closing_loc.end;
2609
2610 node->opening_loc = arguments->opening_loc;
2611 node->arguments = arguments->arguments;
2612 node->closing_loc = arguments->closing_loc;
2613 node->block = arguments->block;
2614
2615 node->name = pm_parser_constant_id_constant(parser, "[]", 2);
2616 return node;
2617}
2618
2622static pm_call_node_t *
2623pm_call_node_binary_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_node_t *argument, pm_node_flags_t flags) {
2624 pm_assert_value_expression(parser, receiver);
2625 pm_assert_value_expression(parser, argument);
2626
2627 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver) | flags);
2628
2629 node->base.location.start = MIN(receiver->location.start, argument->location.start);
2630 node->base.location.end = MAX(receiver->location.end, argument->location.end);
2631
2632 node->receiver = receiver;
2633 node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
2634
2635 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
2636 pm_arguments_node_arguments_append(arguments, argument);
2637 node->arguments = arguments;
2638
2639 node->name = pm_parser_constant_id_token(parser, operator);
2640 return node;
2641}
2642
2643static const uint8_t * parse_operator_symbol_name(const pm_token_t *);
2644
2648static pm_call_node_t *
2649pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_token_t *message, pm_arguments_t *arguments) {
2650 pm_assert_value_expression(parser, receiver);
2651
2652 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
2653
2654 node->base.location.start = receiver->location.start;
2655 const uint8_t *end = pm_arguments_end(arguments);
2656 if (end == NULL) {
2657 end = message->end;
2658 }
2659 node->base.location.end = end;
2660
2661 node->receiver = receiver;
2662 node->call_operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
2663 node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message);
2664 node->opening_loc = arguments->opening_loc;
2665 node->arguments = arguments->arguments;
2666 node->closing_loc = arguments->closing_loc;
2667 node->block = arguments->block;
2668
2669 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
2670 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
2671 }
2672
2677 node->name = pm_parser_constant_id_location(parser, message->start, parse_operator_symbol_name(message));
2678 return node;
2679}
2680
2684static pm_call_node_t *
2685pm_call_node_call_synthesized_create(pm_parser_t *parser, pm_node_t *receiver, const char *message, pm_arguments_node_t *arguments) {
2686 pm_call_node_t *node = pm_call_node_create(parser, 0);
2687 node->base.location.start = parser->start;
2688 node->base.location.end = parser->end;
2689
2690 node->receiver = receiver;
2691 node->call_operator_loc = (pm_location_t) { .start = NULL, .end = NULL };
2692 node->message_loc = (pm_location_t) { .start = NULL, .end = NULL };
2693 node->arguments = arguments;
2694
2695 node->name = pm_parser_constant_id_constant(parser, message, strlen(message));
2696 return node;
2697}
2698
2703static pm_call_node_t *
2704pm_call_node_fcall_create(pm_parser_t *parser, pm_token_t *message, pm_arguments_t *arguments) {
2705 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
2706
2707 node->base.location.start = message->start;
2708 node->base.location.end = pm_arguments_end(arguments);
2709
2710 node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message);
2711 node->opening_loc = arguments->opening_loc;
2712 node->arguments = arguments->arguments;
2713 node->closing_loc = arguments->closing_loc;
2714 node->block = arguments->block;
2715
2716 node->name = pm_parser_constant_id_token(parser, message);
2717 return node;
2718}
2719
2724static pm_call_node_t *
2725pm_call_node_fcall_synthesized_create(pm_parser_t *parser, pm_arguments_node_t *arguments, pm_constant_id_t name) {
2726 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
2727
2728 node->base.location = PM_LOCATION_NULL_VALUE(parser);
2729 node->arguments = arguments;
2730
2731 node->name = name;
2732 return node;
2733}
2734
2738static pm_call_node_t *
2739pm_call_node_not_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *message, pm_arguments_t *arguments) {
2740 pm_assert_value_expression(parser, receiver);
2741 if (receiver != NULL) pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
2742
2743 pm_call_node_t *node = pm_call_node_create(parser, receiver == NULL ? 0 : pm_call_node_ignore_visibility_flag(receiver));
2744
2745 node->base.location.start = message->start;
2746 if (arguments->closing_loc.start != NULL) {
2747 node->base.location.end = arguments->closing_loc.end;
2748 } else {
2749 assert(receiver != NULL);
2750 node->base.location.end = receiver->location.end;
2751 }
2752
2753 node->receiver = receiver;
2754 node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message);
2755 node->opening_loc = arguments->opening_loc;
2756 node->arguments = arguments->arguments;
2757 node->closing_loc = arguments->closing_loc;
2758
2759 node->name = pm_parser_constant_id_constant(parser, "!", 1);
2760 return node;
2761}
2762
2766static pm_call_node_t *
2767pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_arguments_t *arguments) {
2768 pm_assert_value_expression(parser, receiver);
2769
2770 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
2771
2772 node->base.location.start = receiver->location.start;
2773 node->base.location.end = pm_arguments_end(arguments);
2774
2775 node->receiver = receiver;
2776 node->call_operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
2777 node->opening_loc = arguments->opening_loc;
2778 node->arguments = arguments->arguments;
2779 node->closing_loc = arguments->closing_loc;
2780 node->block = arguments->block;
2781
2782 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
2783 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
2784 }
2785
2786 node->name = pm_parser_constant_id_constant(parser, "call", 4);
2787 return node;
2788}
2789
2793static pm_call_node_t *
2794pm_call_node_unary_create(pm_parser_t *parser, pm_token_t *operator, pm_node_t *receiver, const char *name) {
2795 pm_assert_value_expression(parser, receiver);
2796
2797 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
2798
2799 node->base.location.start = operator->start;
2800 node->base.location.end = receiver->location.end;
2801
2802 node->receiver = receiver;
2803 node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
2804
2805 node->name = pm_parser_constant_id_constant(parser, name, strlen(name));
2806 return node;
2807}
2808
2813static pm_call_node_t *
2814pm_call_node_variable_call_create(pm_parser_t *parser, pm_token_t *message) {
2815 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
2816
2817 node->base.location = PM_LOCATION_TOKEN_VALUE(message);
2818 node->message_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(message);
2819
2820 node->name = pm_parser_constant_id_token(parser, message);
2821 return node;
2822}
2823
2828static inline bool
2829pm_call_node_writable_p(const pm_parser_t *parser, const pm_call_node_t *node) {
2830 return (
2831 (node->message_loc.start != NULL) &&
2832 (node->message_loc.end[-1] != '!') &&
2833 (node->message_loc.end[-1] != '?') &&
2834 char_is_identifier_start(parser, node->message_loc.start, parser->end - node->message_loc.start) &&
2835 (node->opening_loc.start == NULL) &&
2836 (node->arguments == NULL) &&
2837 (node->block == NULL)
2838 );
2839}
2840
2844static void
2845pm_call_write_read_name_init(pm_parser_t *parser, pm_constant_id_t *read_name, pm_constant_id_t *write_name) {
2846 pm_constant_t *write_constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *write_name);
2847
2848 if (write_constant->length > 0) {
2849 size_t length = write_constant->length - 1;
2850
2851 void *memory = xmalloc(length);
2852 memcpy(memory, write_constant->start, length);
2853
2854 *read_name = pm_constant_pool_insert_owned(&parser->constant_pool, (uint8_t *) memory, length);
2855 } else {
2856 // We can get here if the message was missing because of a syntax error.
2857 *read_name = pm_parser_constant_id_constant(parser, "", 0);
2858 }
2859}
2860
2864static pm_call_and_write_node_t *
2865pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
2866 assert(target->block == NULL);
2867 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
2868 pm_call_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_and_write_node_t);
2869
2870 *node = (pm_call_and_write_node_t) {
2871 .base = PM_NODE_INIT_NODES(parser, PM_CALL_AND_WRITE_NODE, FL(target), target, value),
2872 .receiver = target->receiver,
2873 .call_operator_loc = target->call_operator_loc,
2874 .message_loc = target->message_loc,
2875 .read_name = 0,
2876 .write_name = target->name,
2877 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
2878 .value = value
2879 };
2880
2881 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
2882
2883 // Here we're going to free the target, since it is no longer necessary.
2884 // However, we don't want to call `pm_node_destroy` because we want to keep
2885 // around all of its children since we just reused them.
2886 xfree(target);
2887
2888 return node;
2889}
2890
2895static void
2896pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) {
2897 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
2898 if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) {
2899 pm_node_t *node;
2900 PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) {
2901 if (PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)) {
2902 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_INDEX_KEYWORDS);
2903 break;
2904 }
2905 }
2906 }
2907
2908 if (block != NULL) {
2909 pm_parser_err_node(parser, block, PM_ERR_UNEXPECTED_INDEX_BLOCK);
2910 }
2911 }
2912}
2913
2917static pm_index_and_write_node_t *
2918pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
2919 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
2920 pm_index_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_index_and_write_node_t);
2921
2922 pm_index_arguments_check(parser, target->arguments, target->block);
2923
2924 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
2925 *node = (pm_index_and_write_node_t) {
2926 .base = PM_NODE_INIT_NODES(parser, PM_INDEX_AND_WRITE_NODE, FL(target), target, value),
2927 .receiver = target->receiver,
2928 .call_operator_loc = target->call_operator_loc,
2929 .opening_loc = target->opening_loc,
2930 .arguments = target->arguments,
2931 .closing_loc = target->closing_loc,
2932 .block = (pm_block_argument_node_t *) target->block,
2933 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
2934 .value = value
2935 };
2936
2937 // Here we're going to free the target, since it is no longer necessary.
2938 // However, we don't want to call `pm_node_destroy` because we want to keep
2939 // around all of its children since we just reused them.
2940 xfree(target);
2941
2942 return node;
2943}
2944
2948static pm_call_operator_write_node_t *
2949pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
2950 assert(target->block == NULL);
2951 pm_call_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_operator_write_node_t);
2952
2953 *node = (pm_call_operator_write_node_t) {
2954 .base = PM_NODE_INIT_NODES(parser, PM_CALL_OPERATOR_WRITE_NODE, FL(target), target, value),
2955 .receiver = target->receiver,
2956 .call_operator_loc = target->call_operator_loc,
2957 .message_loc = target->message_loc,
2958 .read_name = 0,
2959 .write_name = target->name,
2960 .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1),
2961 .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
2962 .value = value
2963 };
2964
2965 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
2966
2967 // Here we're going to free the target, since it is no longer necessary.
2968 // However, we don't want to call `pm_node_destroy` because we want to keep
2969 // around all of its children since we just reused them.
2970 xfree(target);
2971
2972 return node;
2973}
2974
2978static pm_index_operator_write_node_t *
2979pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
2980 pm_index_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_index_operator_write_node_t);
2981
2982 pm_index_arguments_check(parser, target->arguments, target->block);
2983
2984 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
2985 *node = (pm_index_operator_write_node_t) {
2986 .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OPERATOR_WRITE_NODE, FL(target), target, value),
2987 .receiver = target->receiver,
2988 .call_operator_loc = target->call_operator_loc,
2989 .opening_loc = target->opening_loc,
2990 .arguments = target->arguments,
2991 .closing_loc = target->closing_loc,
2992 .block = (pm_block_argument_node_t *) target->block,
2993 .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1),
2994 .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
2995 .value = value
2996 };
2997
2998 // Here we're going to free the target, since it is no longer necessary.
2999 // However, we don't want to call `pm_node_destroy` because we want to keep
3000 // around all of its children since we just reused them.
3001 xfree(target);
3002
3003 return node;
3004}
3005
3009static pm_call_or_write_node_t *
3010pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3011 assert(target->block == NULL);
3012 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3013 pm_call_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_call_or_write_node_t);
3014
3015 *node = (pm_call_or_write_node_t) {
3016 .base = PM_NODE_INIT_NODES(parser, PM_CALL_OR_WRITE_NODE, FL(target), target, value),
3017 .receiver = target->receiver,
3018 .call_operator_loc = target->call_operator_loc,
3019 .message_loc = target->message_loc,
3020 .read_name = 0,
3021 .write_name = target->name,
3022 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3023 .value = value
3024 };
3025
3026 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3027
3028 // Here we're going to free the target, since it is no longer necessary.
3029 // However, we don't want to call `pm_node_destroy` because we want to keep
3030 // around all of its children since we just reused them.
3031 xfree(target);
3032
3033 return node;
3034}
3035
3039static pm_index_or_write_node_t *
3040pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3041 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3042 pm_index_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_index_or_write_node_t);
3043
3044 pm_index_arguments_check(parser, target->arguments, target->block);
3045
3046 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3047 *node = (pm_index_or_write_node_t) {
3048 .base = PM_NODE_INIT_NODES(parser, PM_INDEX_OR_WRITE_NODE, FL(target), target, value),
3049 .receiver = target->receiver,
3050 .call_operator_loc = target->call_operator_loc,
3051 .opening_loc = target->opening_loc,
3052 .arguments = target->arguments,
3053 .closing_loc = target->closing_loc,
3054 .block = (pm_block_argument_node_t *) target->block,
3055 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3056 .value = value
3057 };
3058
3059 // Here we're going to free the target, since it is no longer necessary.
3060 // However, we don't want to call `pm_node_destroy` because we want to keep
3061 // around all of its children since we just reused them.
3062 xfree(target);
3063
3064 return node;
3065}
3066
3071static pm_call_target_node_t *
3072pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3073 pm_call_target_node_t *node = PM_NODE_ALLOC(parser, pm_call_target_node_t);
3074
3075 *node = (pm_call_target_node_t) {
3076 .base = PM_NODE_INIT_NODE(parser, PM_CALL_TARGET_NODE, FL(target), target),
3077 .receiver = target->receiver,
3078 .call_operator_loc = target->call_operator_loc,
3079 .name = target->name,
3080 .message_loc = target->message_loc
3081 };
3082
3083 /* It is possible to get here where we have parsed an invalid syntax tree
3084 * where the call operator was not present. In that case we will have a
3085 * problem because it is a required location. In this case we need to fill
3086 * it in with a fake location so that the syntax tree remains valid. */
3087 if (node->call_operator_loc.start == NULL) {
3088 node->call_operator_loc = (pm_location_t) {
3089 .start = target->base.location.start,
3090 .end = target->base.location.start
3091 };
3092 }
3093
3094 // Here we're going to free the target, since it is no longer necessary.
3095 // However, we don't want to call `pm_node_destroy` because we want to keep
3096 // around all of its children since we just reused them.
3097 xfree(target);
3098
3099 return node;
3100}
3101
3106static pm_index_target_node_t *
3107pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3108 pm_index_target_node_t *node = PM_NODE_ALLOC(parser, pm_index_target_node_t);
3109
3110 pm_index_arguments_check(parser, target->arguments, target->block);
3111 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3112
3113 *node = (pm_index_target_node_t) {
3114 .base = PM_NODE_INIT_NODE(parser, PM_INDEX_TARGET_NODE, FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE, target),
3115 .receiver = target->receiver,
3116 .opening_loc = target->opening_loc,
3117 .arguments = target->arguments,
3118 .closing_loc = target->closing_loc,
3119 .block = (pm_block_argument_node_t *) target->block,
3120 };
3121
3122 // Here we're going to free the target, since it is no longer necessary.
3123 // However, we don't want to call `pm_node_destroy` because we want to keep
3124 // around all of its children since we just reused them.
3125 xfree(target);
3126
3127 return node;
3128}
3129
3133static pm_capture_pattern_node_t *
3134pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_local_variable_target_node_t *target, const pm_token_t *operator) {
3135 pm_capture_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_capture_pattern_node_t);
3136
3137 *node = (pm_capture_pattern_node_t) {
3138 .base = PM_NODE_INIT_NODES(parser, PM_CAPTURE_PATTERN_NODE, 0, value, target),
3139 .value = value,
3140 .target = target,
3141 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
3142 };
3143
3144 return node;
3145}
3146
3150static pm_case_node_t *
3151pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) {
3152 pm_case_node_t *node = PM_NODE_ALLOC(parser, pm_case_node_t);
3153
3154 *node = (pm_case_node_t) {
3155 .base = PM_NODE_INIT_TOKENS(parser, PM_CASE_NODE, 0, case_keyword, end_keyword),
3156 .predicate = predicate,
3157 .else_clause = NULL,
3158 .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword),
3159 .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword),
3160 .conditions = { 0 }
3161 };
3162
3163 return node;
3164}
3165
3169static void
3170pm_case_node_condition_append(pm_case_node_t *node, pm_node_t *condition) {
3171 assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
3172
3173 pm_node_list_append(&node->conditions, condition);
3174 node->base.location.end = condition->location.end;
3175}
3176
3180static void
3181pm_case_node_else_clause_set(pm_case_node_t *node, pm_else_node_t *else_clause) {
3182 node->else_clause = else_clause;
3183 node->base.location.end = else_clause->base.location.end;
3184}
3185
3189static void
3190pm_case_node_end_keyword_loc_set(pm_case_node_t *node, const pm_token_t *end_keyword) {
3191 node->base.location.end = end_keyword->end;
3192 node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword);
3193}
3194
3198static pm_case_match_node_t *
3199pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) {
3200 pm_case_match_node_t *node = PM_NODE_ALLOC(parser, pm_case_match_node_t);
3201
3202 *node = (pm_case_match_node_t) {
3203 .base = PM_NODE_INIT_TOKENS(parser, PM_CASE_MATCH_NODE, 0, case_keyword, end_keyword),
3204 .predicate = predicate,
3205 .else_clause = NULL,
3206 .case_keyword_loc = PM_LOCATION_TOKEN_VALUE(case_keyword),
3207 .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword),
3208 .conditions = { 0 }
3209 };
3210
3211 return node;
3212}
3213
3217static void
3218pm_case_match_node_condition_append(pm_case_match_node_t *node, pm_node_t *condition) {
3219 assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
3220
3221 pm_node_list_append(&node->conditions, condition);
3222 node->base.location.end = condition->location.end;
3223}
3224
3228static void
3229pm_case_match_node_else_clause_set(pm_case_match_node_t *node, pm_else_node_t *else_clause) {
3230 node->else_clause = else_clause;
3231 node->base.location.end = else_clause->base.location.end;
3232}
3233
3237static void
3238pm_case_match_node_end_keyword_loc_set(pm_case_match_node_t *node, const pm_token_t *end_keyword) {
3239 node->base.location.end = end_keyword->end;
3240 node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword);
3241}
3242
3246static pm_class_node_t *
3247pm_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *class_keyword, pm_node_t *constant_path, const pm_token_t *name, const pm_token_t *inheritance_operator, pm_node_t *superclass, pm_node_t *body, const pm_token_t *end_keyword) {
3248 pm_class_node_t *node = PM_NODE_ALLOC(parser, pm_class_node_t);
3249
3250 *node = (pm_class_node_t) {
3251 .base = PM_NODE_INIT_TOKENS(parser, PM_CLASS_NODE, 0, class_keyword, end_keyword),
3252 .locals = *locals,
3253 .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword),
3254 .constant_path = constant_path,
3255 .inheritance_operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(inheritance_operator),
3256 .superclass = superclass,
3257 .body = body,
3258 .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword),
3259 .name = pm_parser_constant_id_token(parser, name)
3260 };
3261
3262 return node;
3263}
3264
3268static pm_class_variable_and_write_node_t *
3269pm_class_variable_and_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3270 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3271 pm_class_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_and_write_node_t);
3272
3273 *node = (pm_class_variable_and_write_node_t) {
3274 .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_AND_WRITE_NODE, 0, target, value),
3275 .name = target->name,
3276 .name_loc = target->base.location,
3277 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3278 .value = value
3279 };
3280
3281 return node;
3282}
3283
3287static pm_class_variable_operator_write_node_t *
3288pm_class_variable_operator_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3289 pm_class_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_operator_write_node_t);
3290
3291 *node = (pm_class_variable_operator_write_node_t) {
3292 .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value),
3293 .name = target->name,
3294 .name_loc = target->base.location,
3295 .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3296 .value = value,
3297 .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
3298 };
3299
3300 return node;
3301}
3302
3306static pm_class_variable_or_write_node_t *
3307pm_class_variable_or_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3308 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3309 pm_class_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_or_write_node_t);
3310
3311 *node = (pm_class_variable_or_write_node_t) {
3312 .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_OR_WRITE_NODE, 0, target, value),
3313 .name = target->name,
3314 .name_loc = target->base.location,
3315 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3316 .value = value
3317 };
3318
3319 return node;
3320}
3321
3325static pm_class_variable_read_node_t *
3326pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
3327 assert(token->type == PM_TOKEN_CLASS_VARIABLE);
3328 pm_class_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_read_node_t);
3329
3330 *node = (pm_class_variable_read_node_t) {
3331 .base = PM_NODE_INIT_TOKEN(parser, PM_CLASS_VARIABLE_READ_NODE, 0, token),
3332 .name = pm_parser_constant_id_token(parser, token)
3333 };
3334
3335 return node;
3336}
3337
3344static inline pm_node_flags_t
3345pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) {
3346 if (PM_NODE_TYPE_P(node, PM_ARRAY_NODE) && ((const pm_array_node_t *) node)->opening_loc.start == NULL) {
3347 return flags;
3348 }
3349 return 0;
3350}
3351
3355static pm_class_variable_write_node_t *
3356pm_class_variable_write_node_create(pm_parser_t *parser, pm_class_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) {
3357 pm_class_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_class_variable_write_node_t);
3358 pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY);
3359
3360 *node = (pm_class_variable_write_node_t) {
3361 .base = PM_NODE_INIT_NODES(parser, PM_CLASS_VARIABLE_WRITE_NODE, flags, read_node, value),
3362 .name = read_node->name,
3363 .name_loc = PM_LOCATION_NODE_VALUE(UP(read_node)),
3364 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3365 .value = value
3366 };
3367
3368 return node;
3369}
3370
3374static pm_constant_path_and_write_node_t *
3375pm_constant_path_and_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3376 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3377 pm_constant_path_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_and_write_node_t);
3378
3379 *node = (pm_constant_path_and_write_node_t) {
3380 .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_AND_WRITE_NODE, 0, target, value),
3381 .target = target,
3382 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3383 .value = value
3384 };
3385
3386 return node;
3387}
3388
3392static pm_constant_path_operator_write_node_t *
3393pm_constant_path_operator_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3394 pm_constant_path_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_operator_write_node_t);
3395
3396 *node = (pm_constant_path_operator_write_node_t) {
3397 .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_OPERATOR_WRITE_NODE, 0, target, value),
3398 .target = target,
3399 .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3400 .value = value,
3401 .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
3402 };
3403
3404 return node;
3405}
3406
3410static pm_constant_path_or_write_node_t *
3411pm_constant_path_or_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3412 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3413 pm_constant_path_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_or_write_node_t);
3414
3415 *node = (pm_constant_path_or_write_node_t) {
3416 .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_OR_WRITE_NODE, 0, target, value),
3417 .target = target,
3418 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3419 .value = value
3420 };
3421
3422 return node;
3423}
3424
3428static pm_constant_path_node_t *
3429pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_token_t *delimiter, const pm_token_t *name_token) {
3430 pm_assert_value_expression(parser, parent);
3431 pm_constant_path_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_node_t);
3432
3433 pm_constant_id_t name = PM_CONSTANT_ID_UNSET;
3434 if (name_token->type == PM_TOKEN_CONSTANT) {
3435 name = pm_parser_constant_id_token(parser, name_token);
3436 }
3437
3438 if (parent == NULL) {
3439 *node = (pm_constant_path_node_t) {
3440 .base = PM_NODE_INIT_TOKENS(parser, PM_CONSTANT_PATH_NODE, 0, delimiter, name_token),
3441 .parent = parent,
3442 .name = name,
3443 .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter),
3444 .name_loc = PM_LOCATION_TOKEN_VALUE(name_token)
3445 };
3446 } else {
3447 *node = (pm_constant_path_node_t) {
3448 .base = PM_NODE_INIT_NODE_TOKEN(parser, PM_CONSTANT_PATH_NODE, 0, parent, name_token),
3449 .parent = parent,
3450 .name = name,
3451 .delimiter_loc = PM_LOCATION_TOKEN_VALUE(delimiter),
3452 .name_loc = PM_LOCATION_TOKEN_VALUE(name_token)
3453 };
3454 }
3455
3456 return node;
3457}
3458
3462static pm_constant_path_write_node_t *
3463pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3464 pm_constant_path_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_path_write_node_t);
3465 pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY);
3466
3467 *node = (pm_constant_path_write_node_t) {
3468 .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_PATH_WRITE_NODE, flags, target, value),
3469 .target = target,
3470 .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
3471 .value = value
3472 };
3473
3474 return node;
3475}
3476
3480static pm_constant_and_write_node_t *
3481pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3482 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3483 pm_constant_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_and_write_node_t);
3484
3485 *node = (pm_constant_and_write_node_t) {
3486 .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_AND_WRITE_NODE, 0, target, value),
3487 .name = target->name,
3488 .name_loc = target->base.location,
3489 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3490 .value = value
3491 };
3492
3493 return node;
3494}
3495
3499static pm_constant_operator_write_node_t *
3500pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3501 pm_constant_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_operator_write_node_t);
3502
3503 *node = (pm_constant_operator_write_node_t) {
3504 .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_OPERATOR_WRITE_NODE, 0, target, value),
3505 .name = target->name,
3506 .name_loc = target->base.location,
3507 .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3508 .value = value,
3509 .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
3510 };
3511
3512 return node;
3513}
3514
3518static pm_constant_or_write_node_t *
3519pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3520 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3521 pm_constant_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_or_write_node_t);
3522
3523 *node = (pm_constant_or_write_node_t) {
3524 .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_OR_WRITE_NODE, 0, target, value),
3525 .name = target->name,
3526 .name_loc = target->base.location,
3527 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3528 .value = value
3529 };
3530
3531 return node;
3532}
3533
3537static pm_constant_read_node_t *
3538pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
3539 assert(name->type == PM_TOKEN_CONSTANT || name->type == PM_TOKEN_MISSING);
3540 pm_constant_read_node_t *node = PM_NODE_ALLOC(parser, pm_constant_read_node_t);
3541
3542 *node = (pm_constant_read_node_t) {
3543 .base = PM_NODE_INIT_TOKEN(parser, PM_CONSTANT_READ_NODE, 0, name),
3544 .name = pm_parser_constant_id_token(parser, name)
3545 };
3546
3547 return node;
3548}
3549
3553static pm_constant_write_node_t *
3554pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3555 pm_constant_write_node_t *node = PM_NODE_ALLOC(parser, pm_constant_write_node_t);
3556 pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY);
3557
3558 *node = (pm_constant_write_node_t) {
3559 .base = PM_NODE_INIT_NODES(parser, PM_CONSTANT_WRITE_NODE, flags, target, value),
3560 .name = target->name,
3561 .name_loc = target->base.location,
3562 .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
3563 .value = value
3564 };
3565
3566 return node;
3567}
3568
3572static void
3573pm_def_node_receiver_check(pm_parser_t *parser, const pm_node_t *node) {
3574 switch (PM_NODE_TYPE(node)) {
3575 case PM_BEGIN_NODE: {
3576 const pm_begin_node_t *cast = (pm_begin_node_t *) node;
3577 if (cast->statements != NULL) pm_def_node_receiver_check(parser, UP(cast->statements));
3578 break;
3579 }
3580 case PM_PARENTHESES_NODE: {
3581 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
3582 if (cast->body != NULL) pm_def_node_receiver_check(parser, cast->body);
3583 break;
3584 }
3585 case PM_STATEMENTS_NODE: {
3586 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3587 pm_def_node_receiver_check(parser, cast->body.nodes[cast->body.size - 1]);
3588 break;
3589 }
3590 case PM_ARRAY_NODE:
3591 case PM_FLOAT_NODE:
3592 case PM_IMAGINARY_NODE:
3593 case PM_INTEGER_NODE:
3594 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3595 case PM_INTERPOLATED_STRING_NODE:
3596 case PM_INTERPOLATED_SYMBOL_NODE:
3597 case PM_INTERPOLATED_X_STRING_NODE:
3598 case PM_RATIONAL_NODE:
3599 case PM_REGULAR_EXPRESSION_NODE:
3600 case PM_SOURCE_ENCODING_NODE:
3601 case PM_SOURCE_FILE_NODE:
3602 case PM_SOURCE_LINE_NODE:
3603 case PM_STRING_NODE:
3604 case PM_SYMBOL_NODE:
3605 case PM_X_STRING_NODE:
3606 pm_parser_err_node(parser, node, PM_ERR_SINGLETON_FOR_LITERALS);
3607 break;
3608 default:
3609 break;
3610 }
3611}
3612
3616static pm_def_node_t *
3617pm_def_node_create(
3618 pm_parser_t *parser,
3619 pm_constant_id_t name,
3620 const pm_token_t *name_loc,
3621 pm_node_t *receiver,
3622 pm_parameters_node_t *parameters,
3623 pm_node_t *body,
3624 pm_constant_id_list_t *locals,
3625 const pm_token_t *def_keyword,
3626 const pm_token_t *operator,
3627 const pm_token_t *lparen,
3628 const pm_token_t *rparen,
3629 const pm_token_t *equal,
3630 const pm_token_t *end_keyword
3631) {
3632 pm_def_node_t *node = PM_NODE_ALLOC(parser, pm_def_node_t);
3633
3634 if (receiver != NULL) {
3635 pm_def_node_receiver_check(parser, receiver);
3636 }
3637
3638 *node = (pm_def_node_t) {
3639 .base = (
3640 (end_keyword->type == PM_TOKEN_NOT_PROVIDED)
3641 ? PM_NODE_INIT_TOKEN_NODE(parser, PM_DEF_NODE, 0, def_keyword, body)
3642 : PM_NODE_INIT_TOKENS(parser, PM_DEF_NODE, 0, def_keyword, end_keyword)
3643 ),
3644 .name = name,
3645 .name_loc = PM_LOCATION_TOKEN_VALUE(name_loc),
3646 .receiver = receiver,
3647 .parameters = parameters,
3648 .body = body,
3649 .locals = *locals,
3650 .def_keyword_loc = PM_LOCATION_TOKEN_VALUE(def_keyword),
3651 .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
3652 .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen),
3653 .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen),
3654 .equal_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(equal),
3655 .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword)
3656 };
3657
3658 return node;
3659}
3660
3664static pm_defined_node_t *
3665pm_defined_node_create(pm_parser_t *parser, const pm_token_t *lparen, pm_node_t *value, const pm_token_t *rparen, const pm_token_t *keyword) {
3666 pm_defined_node_t *node = PM_NODE_ALLOC(parser, pm_defined_node_t);
3667
3668 *node = (pm_defined_node_t) {
3669 .base = (
3670 (rparen->type == PM_TOKEN_NOT_PROVIDED)
3671 ? PM_NODE_INIT_TOKEN_NODE(parser, PM_DEFINED_NODE, 0, keyword, value)
3672 : PM_NODE_INIT_TOKENS(parser, PM_DEFINED_NODE, 0, keyword, rparen)
3673 ),
3674 .lparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(lparen),
3675 .value = value,
3676 .rparen_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(rparen),
3677 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
3678 };
3679
3680 return node;
3681}
3682
3686static pm_else_node_t *
3687pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
3688 pm_else_node_t *node = PM_NODE_ALLOC(parser, pm_else_node_t);
3689
3690 *node = (pm_else_node_t) {
3691 .base = (
3692 ((end_keyword->type == PM_TOKEN_NOT_PROVIDED) && (statements != NULL))
3693 ? PM_NODE_INIT_TOKEN_NODE(parser, PM_ELSE_NODE, 0, else_keyword, statements)
3694 : PM_NODE_INIT_TOKENS(parser, PM_ELSE_NODE, 0, else_keyword, end_keyword)
3695 ),
3696 .else_keyword_loc = PM_LOCATION_TOKEN_VALUE(else_keyword),
3697 .statements = statements,
3698 .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword)
3699 };
3700
3701 return node;
3702}
3703
3707static pm_embedded_statements_node_t *
3708pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
3709 pm_embedded_statements_node_t *node = PM_NODE_ALLOC(parser, pm_embedded_statements_node_t);
3710
3711 *node = (pm_embedded_statements_node_t) {
3712 .base = PM_NODE_INIT_TOKENS(parser, PM_EMBEDDED_STATEMENTS_NODE, 0, opening, closing),
3713 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
3714 .statements = statements,
3715 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
3716 };
3717
3718 return node;
3719}
3720
3724static pm_embedded_variable_node_t *
3725pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
3726 pm_embedded_variable_node_t *node = PM_NODE_ALLOC(parser, pm_embedded_variable_node_t);
3727
3728 *node = (pm_embedded_variable_node_t) {
3729 .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_EMBEDDED_VARIABLE_NODE, 0, operator, variable),
3730 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
3731 .variable = variable
3732 };
3733
3734 return node;
3735}
3736
3740static pm_ensure_node_t *
3741pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
3742 pm_ensure_node_t *node = PM_NODE_ALLOC(parser, pm_ensure_node_t);
3743
3744 *node = (pm_ensure_node_t) {
3745 .base = PM_NODE_INIT_TOKENS(parser, PM_ENSURE_NODE, 0, ensure_keyword, end_keyword),
3746 .ensure_keyword_loc = PM_LOCATION_TOKEN_VALUE(ensure_keyword),
3747 .statements = statements,
3748 .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword)
3749 };
3750
3751 return node;
3752}
3753
3757static pm_false_node_t *
3758pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) {
3759 assert(token->type == PM_TOKEN_KEYWORD_FALSE);
3760 pm_false_node_t *node = PM_NODE_ALLOC(parser, pm_false_node_t);
3761
3762 *node = (pm_false_node_t) {
3763 .base = PM_NODE_INIT_TOKEN(parser, PM_FALSE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token)
3764 };
3765
3766 return node;
3767}
3768
3773static pm_find_pattern_node_t *
3774pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) {
3775 pm_find_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_find_pattern_node_t);
3776
3777 pm_node_t *left = nodes->nodes[0];
3778 assert(PM_NODE_TYPE_P(left, PM_SPLAT_NODE));
3779 pm_splat_node_t *left_splat_node = (pm_splat_node_t *) left;
3780
3781 pm_node_t *right;
3782
3783 if (nodes->size == 1) {
3784 right = UP(pm_missing_node_create(parser, left->location.end, left->location.end));
3785 } else {
3786 right = nodes->nodes[nodes->size - 1];
3787 assert(PM_NODE_TYPE_P(right, PM_SPLAT_NODE));
3788 }
3789
3790#if PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS
3791 // FindPatternNode#right is typed as SplatNode in this case, so replace the potential MissingNode with a SplatNode.
3792 // The resulting AST will anyway be ignored, but this file still needs to compile.
3793 pm_splat_node_t *right_splat_node = PM_NODE_TYPE_P(right, PM_SPLAT_NODE) ? (pm_splat_node_t *) right : left_splat_node;
3794#else
3795 pm_node_t *right_splat_node = right;
3796#endif
3797 *node = (pm_find_pattern_node_t) {
3798 .base = PM_NODE_INIT_NODES(parser, PM_FIND_PATTERN_NODE, 0, left, right),
3799 .constant = NULL,
3800 .left = left_splat_node,
3801 .right = right_splat_node,
3802 .requireds = { 0 },
3803 .opening_loc = { 0 },
3804 .closing_loc = { 0 }
3805 };
3806
3807 // For now we're going to just copy over each pointer manually. This could be
3808 // much more efficient, as we could instead resize the node list to only point
3809 // to 1...-1.
3810 for (size_t index = 1; index < nodes->size - 1; index++) {
3811 pm_node_list_append(&node->requireds, nodes->nodes[index]);
3812 }
3813
3814 return node;
3815}
3816
3821static double
3822pm_double_parse(pm_parser_t *parser, const pm_token_t *token) {
3823 ptrdiff_t diff = token->end - token->start;
3824 if (diff <= 0) return 0.0;
3825
3826 // First, get a buffer of the content.
3827 size_t length = (size_t) diff;
3828 char *buffer = xmalloc(sizeof(char) * (length + 1));
3829 memcpy((void *) buffer, token->start, length);
3830
3831 // Next, determine if we need to replace the decimal point because of
3832 // locale-specific options, and then normalize them if we have to.
3833 char decimal_point = *localeconv()->decimal_point;
3834 if (decimal_point != '.') {
3835 for (size_t index = 0; index < length; index++) {
3836 if (buffer[index] == '.') buffer[index] = decimal_point;
3837 }
3838 }
3839
3840 // Next, handle underscores by removing them from the buffer.
3841 for (size_t index = 0; index < length; index++) {
3842 if (buffer[index] == '_') {
3843 memmove((void *) (buffer + index), (void *) (buffer + index + 1), length - index);
3844 length--;
3845 }
3846 }
3847
3848 // Null-terminate the buffer so that strtod cannot read off the end.
3849 buffer[length] = '\0';
3850
3851 // Now, call strtod to parse the value. Note that CRuby has their own
3852 // version of strtod which avoids locales. We're okay using the locale-aware
3853 // version because we've already validated through the parser that the token
3854 // is in a valid format.
3855 errno = 0;
3856 char *eptr;
3857 double value = strtod(buffer, &eptr);
3858
3859 // This should never happen, because we've already checked that the token
3860 // is in a valid format. However it's good to be safe.
3861 if ((eptr != buffer + length) || (errno != 0 && errno != ERANGE)) {
3862 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, (*token), PM_ERR_FLOAT_PARSE);
3863 xfree((void *) buffer);
3864 return 0.0;
3865 }
3866
3867 // If errno is set, then it should only be ERANGE. At this point we need to
3868 // check if it's infinity (it should be).
3869 if (errno == ERANGE && PRISM_ISINF(value)) {
3870 int warn_width;
3871 const char *ellipsis;
3872
3873 if (length > 20) {
3874 warn_width = 20;
3875 ellipsis = "...";
3876 } else {
3877 warn_width = (int) length;
3878 ellipsis = "";
3879 }
3880
3881 pm_diagnostic_list_append_format(&parser->warning_list, token->start, token->end, PM_WARN_FLOAT_OUT_OF_RANGE, warn_width, (const char *) token->start, ellipsis);
3882 value = (value < 0.0) ? -HUGE_VAL : HUGE_VAL;
3883 }
3884
3885 // Finally we can free the buffer and return the value.
3886 xfree((void *) buffer);
3887 return value;
3888}
3889
3893static pm_float_node_t *
3894pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) {
3895 assert(token->type == PM_TOKEN_FLOAT);
3896 pm_float_node_t *node = PM_NODE_ALLOC(parser, pm_float_node_t);
3897
3898 *node = (pm_float_node_t) {
3899 .base = PM_NODE_INIT_TOKEN(parser, PM_FLOAT_NODE, PM_NODE_FLAG_STATIC_LITERAL, token),
3900 .value = pm_double_parse(parser, token)
3901 };
3902
3903 return node;
3904}
3905
3909static pm_imaginary_node_t *
3910pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
3911 assert(token->type == PM_TOKEN_FLOAT_IMAGINARY);
3912
3913 pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t);
3914 *node = (pm_imaginary_node_t) {
3915 .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token),
3916 .numeric = UP(pm_float_node_create(parser, &((pm_token_t) {
3917 .type = PM_TOKEN_FLOAT,
3918 .start = token->start,
3919 .end = token->end - 1
3920 })))
3921 };
3922
3923 return node;
3924}
3925
3929static pm_rational_node_t *
3930pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
3931 assert(token->type == PM_TOKEN_FLOAT_RATIONAL);
3932
3933 pm_rational_node_t *node = PM_NODE_ALLOC(parser, pm_rational_node_t);
3934 *node = (pm_rational_node_t) {
3935 .base = PM_NODE_INIT_TOKEN(parser, PM_RATIONAL_NODE, PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL, token),
3936 .numerator = { 0 },
3937 .denominator = { 0 }
3938 };
3939
3940 const uint8_t *start = token->start;
3941 const uint8_t *end = token->end - 1; // r
3942
3943 while (start < end && *start == '0') start++; // 0.1 -> .1
3944 while (end > start && end[-1] == '0') end--; // 1.0 -> 1.
3945
3946 size_t length = (size_t) (end - start);
3947 if (length == 1) {
3948 node->denominator.value = 1;
3949 return node;
3950 }
3951
3952 const uint8_t *point = memchr(start, '.', length);
3953 assert(point && "should have a decimal point");
3954
3955 uint8_t *digits = xmalloc(length);
3956 if (digits == NULL) {
3957 fputs("[pm_float_node_rational_create] Failed to allocate memory", stderr);
3958 abort();
3959 }
3960
3961 memcpy(digits, start, (unsigned long) (point - start));
3962 memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1));
3963 pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1);
3964
3965 size_t fract_length = 0;
3966 for (const uint8_t *fract = point; fract < end; ++fract) {
3967 if (*fract != '_') ++fract_length;
3968 }
3969 digits[0] = '1';
3970 if (fract_length > 1) memset(digits + 1, '0', fract_length - 1);
3971 pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + fract_length);
3972 xfree(digits);
3973
3974 pm_integers_reduce(&node->numerator, &node->denominator);
3975 return node;
3976}
3977
3982static pm_imaginary_node_t *
3983pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
3984 assert(token->type == PM_TOKEN_FLOAT_RATIONAL_IMAGINARY);
3985
3986 pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t);
3987 *node = (pm_imaginary_node_t) {
3988 .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token),
3989 .numeric = UP(pm_float_node_rational_create(parser, &((pm_token_t) {
3990 .type = PM_TOKEN_FLOAT_RATIONAL,
3991 .start = token->start,
3992 .end = token->end - 1
3993 })))
3994 };
3995
3996 return node;
3997}
3998
4002static pm_for_node_t *
4003pm_for_node_create(
4004 pm_parser_t *parser,
4005 pm_node_t *index,
4006 pm_node_t *collection,
4007 pm_statements_node_t *statements,
4008 const pm_token_t *for_keyword,
4009 const pm_token_t *in_keyword,
4010 const pm_token_t *do_keyword,
4011 const pm_token_t *end_keyword
4012) {
4013 pm_for_node_t *node = PM_NODE_ALLOC(parser, pm_for_node_t);
4014
4015 *node = (pm_for_node_t) {
4016 .base = PM_NODE_INIT_TOKENS(parser, PM_FOR_NODE, 0, for_keyword, end_keyword),
4017 .index = index,
4018 .collection = collection,
4019 .statements = statements,
4020 .for_keyword_loc = PM_LOCATION_TOKEN_VALUE(for_keyword),
4021 .in_keyword_loc = PM_LOCATION_TOKEN_VALUE(in_keyword),
4022 .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword),
4023 .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword)
4024 };
4025
4026 return node;
4027}
4028
4032static pm_forwarding_arguments_node_t *
4033pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token) {
4034 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4035 pm_forwarding_arguments_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_arguments_node_t);
4036
4037 *node = (pm_forwarding_arguments_node_t) {
4038 .base = PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_ARGUMENTS_NODE, 0, token)
4039 };
4040
4041 return node;
4042}
4043
4047static pm_forwarding_parameter_node_t *
4048pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
4049 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4050 pm_forwarding_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_parameter_node_t);
4051
4052 *node = (pm_forwarding_parameter_node_t) {
4053 .base = PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_PARAMETER_NODE, 0, token)
4054 };
4055
4056 return node;
4057}
4058
4062static pm_forwarding_super_node_t *
4063pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm_arguments_t *arguments) {
4064 assert(arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_NODE));
4065 assert(token->type == PM_TOKEN_KEYWORD_SUPER);
4066 pm_forwarding_super_node_t *node = PM_NODE_ALLOC(parser, pm_forwarding_super_node_t);
4067
4068 pm_block_node_t *block = NULL;
4069 if (arguments->block != NULL) {
4070 block = (pm_block_node_t *) arguments->block;
4071 }
4072
4073 *node = (pm_forwarding_super_node_t) {
4074 .base = (
4075 (block == NULL)
4076 ? PM_NODE_INIT_TOKEN(parser, PM_FORWARDING_SUPER_NODE, 0, token)
4077 : PM_NODE_INIT_TOKEN_NODE(parser, PM_FORWARDING_SUPER_NODE, 0, token, block)
4078 ),
4079 .block = block
4080 };
4081
4082 return node;
4083}
4084
4089static pm_hash_pattern_node_t *
4090pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
4091 pm_hash_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_hash_pattern_node_t);
4092
4093 *node = (pm_hash_pattern_node_t) {
4094 .base = PM_NODE_INIT_TOKENS(parser, PM_HASH_PATTERN_NODE, 0, opening, closing),
4095 .constant = NULL,
4096 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
4097 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
4098 .elements = { 0 },
4099 .rest = NULL
4100 };
4101
4102 return node;
4103}
4104
4108static pm_hash_pattern_node_t *
4109pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *elements, pm_node_t *rest) {
4110 pm_hash_pattern_node_t *node = PM_NODE_ALLOC(parser, pm_hash_pattern_node_t);
4111
4112 const uint8_t *start;
4113 const uint8_t *end;
4114
4115 if (elements->size > 0) {
4116 if (rest) {
4117 start = MIN(rest->location.start, elements->nodes[0]->location.start);
4118 end = MAX(rest->location.end, elements->nodes[elements->size - 1]->location.end);
4119 } else {
4120 start = elements->nodes[0]->location.start;
4121 end = elements->nodes[elements->size - 1]->location.end;
4122 }
4123 } else {
4124 assert(rest != NULL);
4125 start = rest->location.start;
4126 end = rest->location.end;
4127 }
4128
4129 *node = (pm_hash_pattern_node_t) {
4130 .base = PM_NODE_INIT(parser, PM_HASH_PATTERN_NODE, 0, start, end),
4131 .constant = NULL,
4132 .elements = { 0 },
4133 .rest = rest,
4134 .opening_loc = { 0 },
4135 .closing_loc = { 0 }
4136 };
4137
4138 pm_node_list_concat(&node->elements, elements);
4139 return node;
4140}
4141
4145static pm_constant_id_t
4146pm_global_variable_write_name(pm_parser_t *parser, const pm_node_t *target) {
4147 switch (PM_NODE_TYPE(target)) {
4148 case PM_GLOBAL_VARIABLE_READ_NODE:
4149 return ((pm_global_variable_read_node_t *) target)->name;
4150 case PM_BACK_REFERENCE_READ_NODE:
4151 return ((pm_back_reference_read_node_t *) target)->name;
4152 case PM_NUMBERED_REFERENCE_READ_NODE:
4153 // This will only ever happen in the event of a syntax error, but we
4154 // still need to provide something for the node.
4155 return pm_parser_constant_id_location(parser, target->location.start, target->location.end);
4156 default:
4157 assert(false && "unreachable");
4158 return (pm_constant_id_t) -1;
4159 }
4160}
4161
4165static pm_global_variable_and_write_node_t *
4166pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4167 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4168 pm_global_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_and_write_node_t);
4169
4170 *node = (pm_global_variable_and_write_node_t) {
4171 .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_AND_WRITE_NODE, 0, target, value),
4172 .name = pm_global_variable_write_name(parser, target),
4173 .name_loc = target->location,
4174 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
4175 .value = value
4176 };
4177
4178 return node;
4179}
4180
4184static pm_global_variable_operator_write_node_t *
4185pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4186 pm_global_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_operator_write_node_t);
4187
4188 *node = (pm_global_variable_operator_write_node_t) {
4189 .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value),
4190 .name = pm_global_variable_write_name(parser, target),
4191 .name_loc = target->location,
4192 .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
4193 .value = value,
4194 .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
4195 };
4196
4197 return node;
4198}
4199
4203static pm_global_variable_or_write_node_t *
4204pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4205 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4206 pm_global_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_or_write_node_t);
4207
4208 *node = (pm_global_variable_or_write_node_t) {
4209 .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_OR_WRITE_NODE, 0, target, value),
4210 .name = pm_global_variable_write_name(parser, target),
4211 .name_loc = target->location,
4212 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
4213 .value = value
4214 };
4215
4216 return node;
4217}
4218
4222static pm_global_variable_read_node_t *
4223pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
4224 pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t);
4225
4226 *node = (pm_global_variable_read_node_t) {
4227 .base = PM_NODE_INIT_TOKEN(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0, name),
4228 .name = pm_parser_constant_id_token(parser, name)
4229 };
4230
4231 return node;
4232}
4233
4237static pm_global_variable_read_node_t *
4238pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name) {
4239 pm_global_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_read_node_t);
4240
4241 *node = (pm_global_variable_read_node_t) {
4242 .base = PM_NODE_INIT_BASE(parser, PM_GLOBAL_VARIABLE_READ_NODE, 0),
4243 .name = name
4244 };
4245
4246 return node;
4247}
4248
4252static pm_global_variable_write_node_t *
4253pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4254 pm_global_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_write_node_t);
4255 pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY);
4256
4257 *node = (pm_global_variable_write_node_t) {
4258 .base = PM_NODE_INIT_NODES(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, flags, target, value),
4259 .name = pm_global_variable_write_name(parser, target),
4260 .name_loc = PM_LOCATION_NODE_VALUE(target),
4261 .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
4262 .value = value
4263 };
4264
4265 return node;
4266}
4267
4271static pm_global_variable_write_node_t *
4272pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name, pm_node_t *value) {
4273 pm_global_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_global_variable_write_node_t);
4274
4275 *node = (pm_global_variable_write_node_t) {
4276 .base = PM_NODE_INIT_BASE(parser, PM_GLOBAL_VARIABLE_WRITE_NODE, 0),
4277 .name = name,
4278 .name_loc = PM_LOCATION_NULL_VALUE(parser),
4279 .operator_loc = PM_LOCATION_NULL_VALUE(parser),
4280 .value = value
4281 };
4282
4283 return node;
4284}
4285
4289static pm_hash_node_t *
4290pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) {
4291 assert(opening != NULL);
4292 pm_hash_node_t *node = PM_NODE_ALLOC(parser, pm_hash_node_t);
4293
4294 *node = (pm_hash_node_t) {
4295 .base = PM_NODE_INIT_TOKEN(parser, PM_HASH_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening),
4296 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
4297 .closing_loc = PM_LOCATION_NULL_VALUE(parser),
4298 .elements = { 0 }
4299 };
4300
4301 return node;
4302}
4303
4307static inline void
4308pm_hash_node_elements_append(pm_hash_node_t *hash, pm_node_t *element) {
4309 pm_node_list_append(&hash->elements, element);
4310
4311 bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE);
4312 if (static_literal) {
4313 pm_assoc_node_t *assoc = (pm_assoc_node_t *) element;
4314 static_literal = !PM_NODE_TYPE_P(assoc->key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(assoc->key, PM_HASH_NODE) && !PM_NODE_TYPE_P(assoc->key, PM_RANGE_NODE);
4315 static_literal = static_literal && PM_NODE_FLAG_P(assoc->key, PM_NODE_FLAG_STATIC_LITERAL);
4316 static_literal = static_literal && PM_NODE_FLAG_P(assoc, PM_NODE_FLAG_STATIC_LITERAL);
4317 }
4318
4319 if (!static_literal) {
4320 pm_node_flag_unset(UP(hash), PM_NODE_FLAG_STATIC_LITERAL);
4321 }
4322}
4323
4324static inline void
4325pm_hash_node_closing_loc_set(pm_hash_node_t *hash, pm_token_t *token) {
4326 hash->base.location.end = token->end;
4327 hash->closing_loc = PM_LOCATION_TOKEN_VALUE(token);
4328}
4329
4333static pm_if_node_t *
4334pm_if_node_create(pm_parser_t *parser,
4335 const pm_token_t *if_keyword,
4336 pm_node_t *predicate,
4337 const pm_token_t *then_keyword,
4338 pm_statements_node_t *statements,
4339 pm_node_t *subsequent,
4340 const pm_token_t *end_keyword
4341) {
4342 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4343 pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t);
4344
4345 const uint8_t *end;
4346 if (end_keyword->type != PM_TOKEN_NOT_PROVIDED) {
4347 end = end_keyword->end;
4348 } else if (subsequent != NULL) {
4349 end = subsequent->location.end;
4350 } else if (pm_statements_node_body_length(statements) != 0) {
4351 end = statements->base.location.end;
4352 } else {
4353 end = predicate->location.end;
4354 }
4355
4356 *node = (pm_if_node_t) {
4357 .base = PM_NODE_INIT(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, if_keyword->start, end),
4358 .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword),
4359 .predicate = predicate,
4360 .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword),
4361 .statements = statements,
4362 .subsequent = subsequent,
4363 .end_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword)
4364 };
4365
4366 return node;
4367}
4368
4372static pm_if_node_t *
4373pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *if_keyword, pm_node_t *predicate) {
4374 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4375 pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t);
4376
4377 pm_statements_node_t *statements = pm_statements_node_create(parser);
4378 pm_statements_node_body_append(parser, statements, statement, true);
4379
4380 *node = (pm_if_node_t) {
4381 .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate),
4382 .if_keyword_loc = PM_LOCATION_TOKEN_VALUE(if_keyword),
4383 .predicate = predicate,
4384 .then_keyword_loc = { 0 },
4385 .statements = statements,
4386 .subsequent = NULL,
4387 .end_keyword_loc = { 0 }
4388 };
4389
4390 return node;
4391}
4392
4396static pm_if_node_t *
4397pm_if_node_ternary_create(pm_parser_t *parser, pm_node_t *predicate, const pm_token_t *qmark, pm_node_t *true_expression, const pm_token_t *colon, pm_node_t *false_expression) {
4398 pm_assert_value_expression(parser, predicate);
4399 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4400
4401 pm_statements_node_t *if_statements = pm_statements_node_create(parser);
4402 pm_statements_node_body_append(parser, if_statements, true_expression, true);
4403
4404 pm_statements_node_t *else_statements = pm_statements_node_create(parser);
4405 pm_statements_node_body_append(parser, else_statements, false_expression, true);
4406
4407 pm_token_t end_keyword = not_provided(parser);
4408 pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, &end_keyword);
4409
4410 pm_if_node_t *node = PM_NODE_ALLOC(parser, pm_if_node_t);
4411
4412 *node = (pm_if_node_t) {
4413 .base = PM_NODE_INIT_NODES(parser, PM_IF_NODE, PM_NODE_FLAG_NEWLINE, predicate, false_expression),
4414 .if_keyword_loc = { 0 },
4415 .predicate = predicate,
4416 .then_keyword_loc = PM_LOCATION_TOKEN_VALUE(qmark),
4417 .statements = if_statements,
4418 .subsequent = UP(else_node),
4419 .end_keyword_loc = { 0 }
4420 };
4421
4422 return node;
4423
4424}
4425
4426static inline void
4427pm_if_node_end_keyword_loc_set(pm_if_node_t *node, const pm_token_t *keyword) {
4428 node->base.location.end = keyword->end;
4429 node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword);
4430}
4431
4432static inline void
4433pm_else_node_end_keyword_loc_set(pm_else_node_t *node, const pm_token_t *keyword) {
4434 node->base.location.end = keyword->end;
4435 node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword);
4436}
4437
4441static pm_implicit_node_t *
4442pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) {
4443 pm_implicit_node_t *node = PM_NODE_ALLOC(parser, pm_implicit_node_t);
4444
4445 *node = (pm_implicit_node_t) {
4446 .base = PM_NODE_INIT_NODE(parser, PM_IMPLICIT_NODE, 0, value),
4447 .value = value
4448 };
4449
4450 return node;
4451}
4452
4456static pm_implicit_rest_node_t *
4457pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) {
4458 assert(token->type == PM_TOKEN_COMMA);
4459
4460 pm_implicit_rest_node_t *node = PM_NODE_ALLOC(parser, pm_implicit_rest_node_t);
4461
4462 *node = (pm_implicit_rest_node_t) {
4463 .base = PM_NODE_INIT_TOKEN(parser, PM_IMPLICIT_REST_NODE, 0, token)
4464 };
4465
4466 return node;
4467}
4468
4472static pm_integer_node_t *
4473pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4474 assert(token->type == PM_TOKEN_INTEGER);
4475 pm_integer_node_t *node = PM_NODE_ALLOC(parser, pm_integer_node_t);
4476
4477 *node = (pm_integer_node_t) {
4478 .base = PM_NODE_INIT_TOKEN(parser, PM_INTEGER_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token),
4479 .value = { 0 }
4480 };
4481
4482 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4483 switch (base) {
4484 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4485 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4486 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4487 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4488 default: assert(false && "unreachable"); break;
4489 }
4490
4491 pm_integer_parse(&node->value, integer_base, token->start, token->end);
4492 return node;
4493}
4494
4499static pm_imaginary_node_t *
4500pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4501 assert(token->type == PM_TOKEN_INTEGER_IMAGINARY);
4502
4503 pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t);
4504 *node = (pm_imaginary_node_t) {
4505 .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token),
4506 .numeric = UP(pm_integer_node_create(parser, base, &((pm_token_t) {
4507 .type = PM_TOKEN_INTEGER,
4508 .start = token->start,
4509 .end = token->end - 1
4510 })))
4511 };
4512
4513 return node;
4514}
4515
4520static pm_rational_node_t *
4521pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4522 assert(token->type == PM_TOKEN_INTEGER_RATIONAL);
4523
4524 pm_rational_node_t *node = PM_NODE_ALLOC(parser, pm_rational_node_t);
4525 *node = (pm_rational_node_t) {
4526 .base = PM_NODE_INIT_TOKEN(parser, PM_RATIONAL_NODE, base | PM_NODE_FLAG_STATIC_LITERAL, token),
4527 .numerator = { 0 },
4528 .denominator = { .value = 1, 0 }
4529 };
4530
4531 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4532 switch (base) {
4533 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4534 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4535 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4536 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4537 default: assert(false && "unreachable"); break;
4538 }
4539
4540 pm_integer_parse(&node->numerator, integer_base, token->start, token->end - 1);
4541
4542 return node;
4543}
4544
4549static pm_imaginary_node_t *
4550pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4551 assert(token->type == PM_TOKEN_INTEGER_RATIONAL_IMAGINARY);
4552
4553 pm_imaginary_node_t *node = PM_NODE_ALLOC(parser, pm_imaginary_node_t);
4554 *node = (pm_imaginary_node_t) {
4555 .base = PM_NODE_INIT_TOKEN(parser, PM_IMAGINARY_NODE, PM_NODE_FLAG_STATIC_LITERAL, token),
4556 .numeric = UP(pm_integer_node_rational_create(parser, base, &((pm_token_t) {
4557 .type = PM_TOKEN_INTEGER_RATIONAL,
4558 .start = token->start,
4559 .end = token->end - 1
4560 })))
4561 };
4562
4563 return node;
4564}
4565
4569static pm_in_node_t *
4570pm_in_node_create(pm_parser_t *parser, pm_node_t *pattern, pm_statements_node_t *statements, const pm_token_t *in_keyword, const pm_token_t *then_keyword) {
4571 pm_in_node_t *node = PM_NODE_ALLOC(parser, pm_in_node_t);
4572
4573 const uint8_t *end;
4574 if (statements != NULL) {
4575 end = statements->base.location.end;
4576 } else if (then_keyword->type != PM_TOKEN_NOT_PROVIDED) {
4577 end = then_keyword->end;
4578 } else {
4579 end = pattern->location.end;
4580 }
4581
4582 *node = (pm_in_node_t) {
4583 .base = PM_NODE_INIT(parser, PM_IN_NODE, 0, in_keyword->start, end),
4584 .pattern = pattern,
4585 .statements = statements,
4586 .in_loc = PM_LOCATION_TOKEN_VALUE(in_keyword),
4587 .then_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword)
4588 };
4589
4590 return node;
4591}
4592
4596static pm_instance_variable_and_write_node_t *
4597pm_instance_variable_and_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4598 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4599 pm_instance_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_and_write_node_t);
4600
4601 *node = (pm_instance_variable_and_write_node_t) {
4602 .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_AND_WRITE_NODE, 0, target, value),
4603 .name = target->name,
4604 .name_loc = target->base.location,
4605 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
4606 .value = value
4607 };
4608
4609 return node;
4610}
4611
4615static pm_instance_variable_operator_write_node_t *
4616pm_instance_variable_operator_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4617 pm_instance_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_operator_write_node_t);
4618
4619 *node = (pm_instance_variable_operator_write_node_t) {
4620 .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value),
4621 .name = target->name,
4622 .name_loc = target->base.location,
4623 .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
4624 .value = value,
4625 .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1)
4626 };
4627
4628 return node;
4629}
4630
4634static pm_instance_variable_or_write_node_t *
4635pm_instance_variable_or_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4636 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4637 pm_instance_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_or_write_node_t);
4638
4639 *node = (pm_instance_variable_or_write_node_t) {
4640 .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_OR_WRITE_NODE, 0, target, value),
4641 .name = target->name,
4642 .name_loc = target->base.location,
4643 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
4644 .value = value
4645 };
4646
4647 return node;
4648}
4649
4653static pm_instance_variable_read_node_t *
4654pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
4655 assert(token->type == PM_TOKEN_INSTANCE_VARIABLE);
4656 pm_instance_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_read_node_t);
4657
4658 *node = (pm_instance_variable_read_node_t) {
4659 .base = PM_NODE_INIT_TOKEN(parser, PM_INSTANCE_VARIABLE_READ_NODE, 0, token),
4660 .name = pm_parser_constant_id_token(parser, token)
4661 };
4662
4663 return node;
4664}
4665
4670static pm_instance_variable_write_node_t *
4671pm_instance_variable_write_node_create(pm_parser_t *parser, pm_instance_variable_read_node_t *read_node, pm_token_t *operator, pm_node_t *value) {
4672 pm_instance_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_instance_variable_write_node_t);
4673 pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY);
4674
4675 *node = (pm_instance_variable_write_node_t) {
4676 .base = PM_NODE_INIT_NODES(parser, PM_INSTANCE_VARIABLE_WRITE_NODE, flags, read_node, value),
4677 .name = read_node->name,
4678 .name_loc = PM_LOCATION_NODE_VALUE(read_node),
4679 .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
4680 .value = value
4681 };
4682
4683 return node;
4684}
4685
4691static void
4692pm_interpolated_node_append(pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) {
4693 switch (PM_NODE_TYPE(part)) {
4694 case PM_STRING_NODE:
4695 pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
4696 break;
4697 case PM_EMBEDDED_STATEMENTS_NODE: {
4698 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
4699 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
4700
4701 if (embedded == NULL) {
4702 // If there are no statements or more than one statement, then
4703 // we lose the static literal flag.
4704 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
4705 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
4706 // If the embedded statement is a string, then we can keep the
4707 // static literal flag and mark the string as frozen.
4708 pm_node_flag_set(embedded, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
4709 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
4710 // If the embedded statement is an interpolated string and it's
4711 // a static literal, then we can keep the static literal flag.
4712 } else {
4713 // Otherwise we lose the static literal flag.
4714 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
4715 }
4716
4717 break;
4718 }
4719 case PM_EMBEDDED_VARIABLE_NODE:
4720 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
4721 break;
4722 default:
4723 assert(false && "unexpected node type");
4724 break;
4725 }
4726
4727 pm_node_list_append(parts, part);
4728}
4729
4733static pm_interpolated_regular_expression_node_t *
4734pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening) {
4735 pm_interpolated_regular_expression_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_regular_expression_node_t);
4736
4737 *node = (pm_interpolated_regular_expression_node_t) {
4738 .base = PM_NODE_INIT_TOKEN(parser, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening),
4739 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
4740 .closing_loc = PM_LOCATION_TOKEN_VALUE(opening),
4741 .parts = { 0 }
4742 };
4743
4744 return node;
4745}
4746
4747static inline void
4748pm_interpolated_regular_expression_node_append(pm_interpolated_regular_expression_node_t *node, pm_node_t *part) {
4749 if (node->base.location.start > part->location.start) {
4750 node->base.location.start = part->location.start;
4751 }
4752 if (node->base.location.end < part->location.end) {
4753 node->base.location.end = part->location.end;
4754 }
4755
4756 pm_interpolated_node_append(UP(node), &node->parts, part);
4757}
4758
4759static inline void
4760pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) {
4761 node->closing_loc = PM_LOCATION_TOKEN_VALUE(closing);
4762 node->base.location.end = closing->end;
4763 pm_node_flag_set(UP(node), pm_regular_expression_flags_create(parser, closing));
4764}
4765
4789static inline void
4790pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_t *part) {
4791#define CLEAR_FLAGS(node) \
4792 node->base.flags = (pm_node_flags_t) (FL(node) & ~(PM_NODE_FLAG_STATIC_LITERAL | PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE))
4793
4794#define MUTABLE_FLAGS(node) \
4795 node->base.flags = (pm_node_flags_t) ((FL(node) | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN);
4796
4797 if (node->parts.size == 0 && node->opening_loc.start == NULL) {
4798 node->base.location.start = part->location.start;
4799 }
4800
4801 node->base.location.end = MAX(node->base.location.end, part->location.end);
4802
4803 switch (PM_NODE_TYPE(part)) {
4804 case PM_STRING_NODE:
4805 // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags,
4806 // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for
4807 // as long as this interpolation only consists of other string literals.
4808 if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
4809 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
4810 }
4811 part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
4812 break;
4813 case PM_INTERPOLATED_STRING_NODE:
4814 if (PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
4815 // If the string that we're concatenating is a static literal,
4816 // then we can keep the static literal flag for this string.
4817 } else {
4818 // Otherwise, we lose the static literal flag here and we should
4819 // also clear the mutability flags.
4820 CLEAR_FLAGS(node);
4821 }
4822 break;
4823 case PM_EMBEDDED_STATEMENTS_NODE: {
4824 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
4825 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
4826
4827 if (embedded == NULL) {
4828 // If we're embedding multiple statements or no statements, then
4829 // the string is not longer a static literal.
4830 CLEAR_FLAGS(node);
4831 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
4832 // If the embedded statement is a string, then we can make that
4833 // string as frozen and static literal, and not touch the static
4834 // literal status of this string.
4835 embedded->flags = (pm_node_flags_t) ((embedded->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
4836
4837 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
4838 MUTABLE_FLAGS(node);
4839 }
4840 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
4841 // If the embedded statement is an interpolated string, but that
4842 // string is marked as static literal, then we can keep our
4843 // static literal status for this string.
4844 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
4845 MUTABLE_FLAGS(node);
4846 }
4847 } else {
4848 // In all other cases, we lose the static literal flag here and
4849 // become mutable.
4850 CLEAR_FLAGS(node);
4851 }
4852
4853 break;
4854 }
4855 case PM_EMBEDDED_VARIABLE_NODE:
4856 // Embedded variables clear static literal, which means we also
4857 // should clear the mutability flags.
4858 CLEAR_FLAGS(node);
4859 break;
4860 case PM_X_STRING_NODE:
4861 case PM_INTERPOLATED_X_STRING_NODE:
4862 case PM_SYMBOL_NODE:
4863 case PM_INTERPOLATED_SYMBOL_NODE:
4864 // These will only happen in error cases. But we want to handle it
4865 // here so that we don't fail the assertion.
4866 CLEAR_FLAGS(node);
4867 break;
4868 default:
4869 assert(false && "unexpected node type");
4870 break;
4871 }
4872
4873 pm_node_list_append(&node->parts, part);
4874
4875#undef CLEAR_FLAGS
4876#undef MUTABLE_FLAGS
4877}
4878
4882static pm_interpolated_string_node_t *
4883pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
4884 pm_interpolated_string_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_string_node_t);
4885 pm_node_flags_t flags = PM_NODE_FLAG_STATIC_LITERAL;
4886
4887 switch (parser->frozen_string_literal) {
4888 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
4889 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE;
4890 break;
4891 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
4892 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN;
4893 break;
4894 }
4895
4896 *node = (pm_interpolated_string_node_t) {
4897 .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_STRING_NODE, flags, opening, closing),
4898 .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
4899 .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
4900 .parts = { 0 }
4901 };
4902
4903 if (parts != NULL) {
4904 pm_node_t *part;
4905 PM_NODE_LIST_FOREACH(parts, index, part) {
4906 pm_interpolated_string_node_append(node, part);
4907 }
4908 }
4909
4910 return node;
4911}
4912
4916static void
4917pm_interpolated_string_node_closing_set(pm_interpolated_string_node_t *node, const pm_token_t *closing) {
4918 node->closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing);
4919 node->base.location.end = closing->end;
4920}
4921
4922static void
4923pm_interpolated_symbol_node_append(pm_interpolated_symbol_node_t *node, pm_node_t *part) {
4924 if (node->parts.size == 0 && node->opening_loc.start == NULL) {
4925 node->base.location.start = part->location.start;
4926 }
4927
4928 pm_interpolated_node_append(UP(node), &node->parts, part);
4929 node->base.location.end = MAX(node->base.location.end, part->location.end);
4930}
4931
4932static void
4933pm_interpolated_symbol_node_closing_loc_set(pm_interpolated_symbol_node_t *node, const pm_token_t *closing) {
4934 node->closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing);
4935 node->base.location.end = closing->end;
4936}
4937
4941static pm_interpolated_symbol_node_t *
4942pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
4943 pm_interpolated_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_symbol_node_t);
4944
4945 *node = (pm_interpolated_symbol_node_t) {
4946 .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening, closing),
4947 .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
4948 .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
4949 .parts = { 0 }
4950 };
4951
4952 if (parts != NULL) {
4953 pm_node_t *part;
4954 PM_NODE_LIST_FOREACH(parts, index, part) {
4955 pm_interpolated_symbol_node_append(node, part);
4956 }
4957 }
4958
4959 return node;
4960}
4961
4965static pm_interpolated_x_string_node_t *
4966pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
4967 pm_interpolated_x_string_node_t *node = PM_NODE_ALLOC(parser, pm_interpolated_x_string_node_t);
4968
4969 *node = (pm_interpolated_x_string_node_t) {
4970 .base = PM_NODE_INIT_TOKENS(parser, PM_INTERPOLATED_X_STRING_NODE, 0, opening, closing),
4971 .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
4972 .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
4973 .parts = { 0 }
4974 };
4975
4976 return node;
4977}
4978
4979static inline void
4980pm_interpolated_xstring_node_append(pm_interpolated_x_string_node_t *node, pm_node_t *part) {
4981 pm_interpolated_node_append(UP(node), &node->parts, part);
4982 node->base.location.end = part->location.end;
4983}
4984
4985static inline void
4986pm_interpolated_xstring_node_closing_set(pm_interpolated_x_string_node_t *node, const pm_token_t *closing) {
4987 node->closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing);
4988 node->base.location.end = closing->end;
4989}
4990
4994static pm_it_local_variable_read_node_t *
4995pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
4996 pm_it_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_it_local_variable_read_node_t);
4997
4998 *node = (pm_it_local_variable_read_node_t) {
4999 .base = PM_NODE_INIT_TOKEN(parser, PM_IT_LOCAL_VARIABLE_READ_NODE, 0, name),
5000 };
5001
5002 return node;
5003}
5004
5008static pm_it_parameters_node_t *
5009pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
5010 pm_it_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_it_parameters_node_t);
5011
5012 *node = (pm_it_parameters_node_t) {
5013 .base = PM_NODE_INIT_TOKENS(parser, PM_IT_PARAMETERS_NODE, 0, opening, closing),
5014 };
5015
5016 return node;
5017}
5018
5022static pm_keyword_hash_node_t *
5023pm_keyword_hash_node_create(pm_parser_t *parser) {
5024 pm_keyword_hash_node_t *node = PM_NODE_ALLOC(parser, pm_keyword_hash_node_t);
5025
5026 *node = (pm_keyword_hash_node_t) {
5027 .base = PM_NODE_INIT_UNSET(parser, PM_KEYWORD_HASH_NODE, PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS),
5028 .elements = { 0 }
5029 };
5030
5031 return node;
5032}
5033
5037static void
5038pm_keyword_hash_node_elements_append(pm_keyword_hash_node_t *hash, pm_node_t *element) {
5039 // If the element being added is not an AssocNode or does not have a symbol
5040 // key, then we want to turn the SYMBOL_KEYS flag off.
5041 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_TYPE_P(((pm_assoc_node_t *) element)->key, PM_SYMBOL_NODE)) {
5042 pm_node_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS);
5043 }
5044
5045 pm_node_list_append(&hash->elements, element);
5046 if (hash->base.location.start == NULL) {
5047 hash->base.location.start = element->location.start;
5048 }
5049 hash->base.location.end = element->location.end;
5050}
5051
5055static pm_required_keyword_parameter_node_t *
5056pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name) {
5057 pm_required_keyword_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_required_keyword_parameter_node_t);
5058
5059 *node = (pm_required_keyword_parameter_node_t) {
5060 .base = PM_NODE_INIT_TOKEN(parser, PM_REQUIRED_KEYWORD_PARAMETER_NODE, 0, name),
5061 .name = pm_parser_constant_id_location(parser, name->start, name->end - 1),
5062 .name_loc = PM_LOCATION_TOKEN_VALUE(name),
5063 };
5064
5065 return node;
5066}
5067
5071static pm_optional_keyword_parameter_node_t *
5072pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, pm_node_t *value) {
5073 pm_optional_keyword_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_optional_keyword_parameter_node_t);
5074
5075 *node = (pm_optional_keyword_parameter_node_t) {
5076 .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_OPTIONAL_KEYWORD_PARAMETER_NODE, 0, name, value),
5077 .name = pm_parser_constant_id_location(parser, name->start, name->end - 1),
5078 .name_loc = PM_LOCATION_TOKEN_VALUE(name),
5079 .value = value
5080 };
5081
5082 return node;
5083}
5084
5088static pm_keyword_rest_parameter_node_t *
5089pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
5090 pm_keyword_rest_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_keyword_rest_parameter_node_t);
5091
5092 *node = (pm_keyword_rest_parameter_node_t) {
5093 .base = (
5094 (name->type == PM_TOKEN_NOT_PROVIDED)
5095 ? PM_NODE_INIT_TOKEN(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator)
5096 : PM_NODE_INIT_TOKENS(parser, PM_KEYWORD_REST_PARAMETER_NODE, 0, operator, name)
5097 ),
5098 .name = pm_parser_optional_constant_id_token(parser, name),
5099 .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name),
5100 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
5101 };
5102
5103 return node;
5104}
5105
5109static pm_lambda_node_t *
5110pm_lambda_node_create(
5111 pm_parser_t *parser,
5112 pm_constant_id_list_t *locals,
5113 const pm_token_t *operator,
5114 const pm_token_t *opening,
5115 const pm_token_t *closing,
5116 pm_node_t *parameters,
5117 pm_node_t *body
5118) {
5119 pm_lambda_node_t *node = PM_NODE_ALLOC(parser, pm_lambda_node_t);
5120
5121 *node = (pm_lambda_node_t) {
5122 .base = PM_NODE_INIT_TOKENS(parser, PM_LAMBDA_NODE, 0, operator, closing),
5123 .locals = *locals,
5124 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
5125 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
5126 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
5127 .parameters = parameters,
5128 .body = body
5129 };
5130
5131 return node;
5132}
5133
5137static pm_local_variable_and_write_node_t *
5138pm_local_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) {
5139 assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_IT_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_CALL_NODE));
5140 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
5141 pm_local_variable_and_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_and_write_node_t);
5142
5143 *node = (pm_local_variable_and_write_node_t) {
5144 .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_AND_WRITE_NODE, 0, target, value),
5145 .name_loc = target->location,
5146 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
5147 .value = value,
5148 .name = name,
5149 .depth = depth
5150 };
5151
5152 return node;
5153}
5154
5158static pm_local_variable_operator_write_node_t *
5159pm_local_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) {
5160 pm_local_variable_operator_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_operator_write_node_t);
5161
5162 *node = (pm_local_variable_operator_write_node_t) {
5163 .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, 0, target, value),
5164 .name_loc = target->location,
5165 .binary_operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
5166 .value = value,
5167 .name = name,
5168 .binary_operator = pm_parser_constant_id_location(parser, operator->start, operator->end - 1),
5169 .depth = depth
5170 };
5171
5172 return node;
5173}
5174
5178static pm_local_variable_or_write_node_t *
5179pm_local_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value, pm_constant_id_t name, uint32_t depth) {
5180 assert(PM_NODE_TYPE_P(target, PM_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_IT_LOCAL_VARIABLE_READ_NODE) || PM_NODE_TYPE_P(target, PM_CALL_NODE));
5181 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
5182 pm_local_variable_or_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_or_write_node_t);
5183
5184 *node = (pm_local_variable_or_write_node_t) {
5185 .base = PM_NODE_INIT_NODES(parser, PM_LOCAL_VARIABLE_OR_WRITE_NODE, 0, target, value),
5186 .name_loc = target->location,
5187 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
5188 .value = value,
5189 .name = name,
5190 .depth = depth
5191 };
5192
5193 return node;
5194}
5195
5199static pm_local_variable_read_node_t *
5200pm_local_variable_read_node_create_constant_id(pm_parser_t *parser, const pm_token_t *name, pm_constant_id_t name_id, uint32_t depth, bool missing) {
5201 if (!missing) pm_locals_read(&pm_parser_scope_find(parser, depth)->locals, name_id);
5202
5203 pm_local_variable_read_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_read_node_t);
5204
5205 *node = (pm_local_variable_read_node_t) {
5206 .base = PM_NODE_INIT_TOKEN(parser, PM_LOCAL_VARIABLE_READ_NODE, 0, name),
5207 .name = name_id,
5208 .depth = depth
5209 };
5210
5211 return node;
5212}
5213
5217static pm_local_variable_read_node_t *
5218pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5219 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5220 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, false);
5221}
5222
5227static pm_local_variable_read_node_t *
5228pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5229 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5230 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, true);
5231}
5232
5236static pm_local_variable_write_node_t *
5237pm_local_variable_write_node_create(pm_parser_t *parser, pm_constant_id_t name, uint32_t depth, pm_node_t *value, const pm_location_t *name_loc, const pm_token_t *operator) {
5238 pm_local_variable_write_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_write_node_t);
5239 pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY);
5240
5241 *node = (pm_local_variable_write_node_t) {
5242 .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_LOCAL_VARIABLE_WRITE_NODE, flags, name_loc, value),
5243 .name = name,
5244 .depth = depth,
5245 .value = value,
5246 .name_loc = *name_loc,
5247 .operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator)
5248 };
5249
5250 return node;
5251}
5252
5256static inline bool
5257pm_token_is_it(const uint8_t *start, const uint8_t *end) {
5258 return (end - start == 2) && (start[0] == 'i') && (start[1] == 't');
5259}
5260
5265static inline bool
5266pm_token_is_numbered_parameter(const uint8_t *start, const uint8_t *end) {
5267 return (end - start == 2) && (start[0] == '_') && (start[1] != '0') && (pm_char_is_decimal_digit(start[1]));
5268}
5269
5274static inline void
5275pm_refute_numbered_parameter(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
5276 if (pm_token_is_numbered_parameter(start, end)) {
5277 PM_PARSER_ERR_FORMAT(parser, start, end, PM_ERR_PARAMETER_NUMBERED_RESERVED, start);
5278 }
5279}
5280
5285static pm_local_variable_target_node_t *
5286pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *location, pm_constant_id_t name, uint32_t depth) {
5287 pm_refute_numbered_parameter(parser, location->start, location->end);
5288 pm_local_variable_target_node_t *node = PM_NODE_ALLOC(parser, pm_local_variable_target_node_t);
5289
5290 *node = (pm_local_variable_target_node_t) {
5291 .base = PM_NODE_INIT_TOKEN(parser, PM_LOCAL_VARIABLE_TARGET_NODE, 0, location),
5292 .name = name,
5293 .depth = depth
5294 };
5295
5296 return node;
5297}
5298
5302static pm_match_predicate_node_t *
5303pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5304 pm_assert_value_expression(parser, value);
5305
5306 pm_match_predicate_node_t *node = PM_NODE_ALLOC(parser, pm_match_predicate_node_t);
5307
5308 *node = (pm_match_predicate_node_t) {
5309 .base = PM_NODE_INIT_NODES(parser, PM_MATCH_PREDICATE_NODE, 0, value, pattern),
5310 .value = value,
5311 .pattern = pattern,
5312 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
5313 };
5314
5315 return node;
5316}
5317
5321static pm_match_required_node_t *
5322pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5323 pm_assert_value_expression(parser, value);
5324
5325 pm_match_required_node_t *node = PM_NODE_ALLOC(parser, pm_match_required_node_t);
5326
5327 *node = (pm_match_required_node_t) {
5328 .base = PM_NODE_INIT_NODES(parser, PM_MATCH_REQUIRED_NODE, 0, value, pattern),
5329 .value = value,
5330 .pattern = pattern,
5331 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
5332 };
5333
5334 return node;
5335}
5336
5340static pm_match_write_node_t *
5341pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) {
5342 pm_match_write_node_t *node = PM_NODE_ALLOC(parser, pm_match_write_node_t);
5343
5344 *node = (pm_match_write_node_t) {
5345 .base = PM_NODE_INIT_NODE(parser, PM_MATCH_WRITE_NODE, 0, call),
5346 .call = call,
5347 .targets = { 0 }
5348 };
5349
5350 return node;
5351}
5352
5356static pm_module_node_t *
5357pm_module_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *module_keyword, pm_node_t *constant_path, const pm_token_t *name, pm_node_t *body, const pm_token_t *end_keyword) {
5358 pm_module_node_t *node = PM_NODE_ALLOC(parser, pm_module_node_t);
5359
5360 *node = (pm_module_node_t) {
5361 .base = PM_NODE_INIT_TOKENS(parser, PM_MODULE_NODE, 0, module_keyword, end_keyword),
5362 .locals = (locals == NULL ? ((pm_constant_id_list_t) { .ids = NULL, .size = 0, .capacity = 0 }) : *locals),
5363 .module_keyword_loc = PM_LOCATION_TOKEN_VALUE(module_keyword),
5364 .constant_path = constant_path,
5365 .body = body,
5366 .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword),
5367 .name = pm_parser_constant_id_token(parser, name)
5368 };
5369
5370 return node;
5371}
5372
5376static pm_multi_target_node_t *
5377pm_multi_target_node_create(pm_parser_t *parser) {
5378 pm_multi_target_node_t *node = PM_NODE_ALLOC(parser, pm_multi_target_node_t);
5379
5380 *node = (pm_multi_target_node_t) {
5381 .base = PM_NODE_INIT_UNSET(parser, PM_MULTI_TARGET_NODE, 0),
5382 .lefts = { 0 },
5383 .rest = NULL,
5384 .rights = { 0 },
5385 .lparen_loc = { 0 },
5386 .rparen_loc = { 0 }
5387 };
5388
5389 return node;
5390}
5391
5395static void
5396pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t *node, pm_node_t *target) {
5397 if (PM_NODE_TYPE_P(target, PM_SPLAT_NODE)) {
5398 if (node->rest == NULL) {
5399 node->rest = target;
5400 } else {
5401 pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
5402 pm_node_list_append(&node->rights, target);
5403 }
5404 } else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
5405 if (node->rest == NULL) {
5406 node->rest = target;
5407 } else {
5408 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
5409 pm_node_list_append(&node->rights, target);
5410 }
5411 } else if (node->rest == NULL) {
5412 pm_node_list_append(&node->lefts, target);
5413 } else {
5414 pm_node_list_append(&node->rights, target);
5415 }
5416
5417 if (node->base.location.start == NULL || (node->base.location.start > target->location.start)) {
5418 node->base.location.start = target->location.start;
5419 }
5420
5421 if (node->base.location.end == NULL || (node->base.location.end < target->location.end)) {
5422 node->base.location.end = target->location.end;
5423 }
5424}
5425
5429static void
5430pm_multi_target_node_opening_set(pm_multi_target_node_t *node, const pm_token_t *lparen) {
5431 node->base.location.start = lparen->start;
5432 node->lparen_loc = PM_LOCATION_TOKEN_VALUE(lparen);
5433}
5434
5438static void
5439pm_multi_target_node_closing_set(pm_multi_target_node_t *node, const pm_token_t *rparen) {
5440 node->base.location.end = rparen->end;
5441 node->rparen_loc = PM_LOCATION_TOKEN_VALUE(rparen);
5442}
5443
5447static pm_multi_write_node_t *
5448pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, const pm_token_t *operator, pm_node_t *value) {
5449 pm_multi_write_node_t *node = PM_NODE_ALLOC(parser, pm_multi_write_node_t);
5450 pm_node_flags_t flags = pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY);
5451
5452 *node = (pm_multi_write_node_t) {
5453 .base = PM_NODE_INIT_NODES(parser, PM_MULTI_WRITE_NODE, flags, target, value),
5454 .lefts = target->lefts,
5455 .rest = target->rest,
5456 .rights = target->rights,
5457 .lparen_loc = target->lparen_loc,
5458 .rparen_loc = target->rparen_loc,
5459 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
5460 .value = value
5461 };
5462
5463 // Explicitly do not call pm_node_destroy here because we want to keep
5464 // around all of the information within the MultiWriteNode node.
5465 xfree(target);
5466
5467 return node;
5468}
5469
5473static pm_next_node_t *
5474pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
5475 assert(keyword->type == PM_TOKEN_KEYWORD_NEXT);
5476 pm_next_node_t *node = PM_NODE_ALLOC(parser, pm_next_node_t);
5477
5478 *node = (pm_next_node_t) {
5479 .base = (
5480 (arguments == NULL)
5481 ? PM_NODE_INIT_TOKEN(parser, PM_NEXT_NODE, 0, keyword)
5482 : PM_NODE_INIT_TOKEN_NODE(parser, PM_NEXT_NODE, 0, keyword, arguments)
5483 ),
5484 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
5485 .arguments = arguments
5486 };
5487
5488 return node;
5489}
5490
5494static pm_nil_node_t *
5495pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) {
5496 assert(token->type == PM_TOKEN_KEYWORD_NIL);
5497 pm_nil_node_t *node = PM_NODE_ALLOC(parser, pm_nil_node_t);
5498
5499 *node = (pm_nil_node_t) {
5500 .base = PM_NODE_INIT_TOKEN(parser, PM_NIL_NODE, PM_NODE_FLAG_STATIC_LITERAL, token)
5501 };
5502
5503 return node;
5504}
5505
5509static pm_no_keywords_parameter_node_t *
5510pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
5511 assert(operator->type == PM_TOKEN_USTAR_STAR || operator->type == PM_TOKEN_STAR_STAR);
5512 assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
5513 pm_no_keywords_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_no_keywords_parameter_node_t);
5514
5515 *node = (pm_no_keywords_parameter_node_t) {
5516 .base = PM_NODE_INIT_TOKENS(parser, PM_NO_KEYWORDS_PARAMETER_NODE, 0, operator, keyword),
5517 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
5518 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword)
5519 };
5520
5521 return node;
5522}
5523
5527static pm_numbered_parameters_node_t *
5528pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_location_t *location, uint8_t maximum) {
5529 pm_numbered_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_numbered_parameters_node_t);
5530
5531 *node = (pm_numbered_parameters_node_t) {
5532 .base = PM_NODE_INIT_TOKEN(parser, PM_NUMBERED_PARAMETERS_NODE, 0, location),
5533 .maximum = maximum
5534 };
5535
5536 return node;
5537}
5538
5543#define NTH_REF_MAX ((uint32_t) (INT_MAX >> 1))
5544
5551static uint32_t
5552pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *token) {
5553 const uint8_t *start = token->start + 1;
5554 const uint8_t *end = token->end;
5555
5556 ptrdiff_t diff = end - start;
5557 assert(diff > 0);
5558#if PTRDIFF_MAX > SIZE_MAX
5559 assert(diff < (ptrdiff_t) SIZE_MAX);
5560#endif
5561 size_t length = (size_t) diff;
5562
5563 char *digits = xcalloc(length + 1, sizeof(char));
5564 memcpy(digits, start, length);
5565 digits[length] = '\0';
5566
5567 char *endptr;
5568 errno = 0;
5569 unsigned long value = strtoul(digits, &endptr, 10);
5570
5571 if ((digits == endptr) || (*endptr != '\0')) {
5572 pm_parser_err(parser, start, end, PM_ERR_INVALID_NUMBER_DECIMAL);
5573 value = 0;
5574 }
5575
5576 xfree(digits);
5577
5578 if ((errno == ERANGE) || (value > NTH_REF_MAX)) {
5579 PM_PARSER_WARN_FORMAT(parser, start, end, PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start);
5580 value = 0;
5581 }
5582
5583 return (uint32_t) value;
5584}
5585
5586#undef NTH_REF_MAX
5587
5591static pm_numbered_reference_read_node_t *
5592pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
5593 assert(name->type == PM_TOKEN_NUMBERED_REFERENCE);
5594 pm_numbered_reference_read_node_t *node = PM_NODE_ALLOC(parser, pm_numbered_reference_read_node_t);
5595
5596 *node = (pm_numbered_reference_read_node_t) {
5597 .base = PM_NODE_INIT_TOKEN(parser, PM_NUMBERED_REFERENCE_READ_NODE, 0, name),
5598 .number = pm_numbered_reference_read_node_number(parser, name)
5599 };
5600
5601 return node;
5602}
5603
5607static pm_optional_parameter_node_t *
5608pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator, pm_node_t *value) {
5609 pm_optional_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_optional_parameter_node_t);
5610
5611 *node = (pm_optional_parameter_node_t) {
5612 .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_OPTIONAL_PARAMETER_NODE, 0, name, value),
5613 .name = pm_parser_constant_id_token(parser, name),
5614 .name_loc = PM_LOCATION_TOKEN_VALUE(name),
5615 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
5616 .value = value
5617 };
5618
5619 return node;
5620}
5621
5625static pm_or_node_t *
5626pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
5627 pm_assert_value_expression(parser, left);
5628
5629 pm_or_node_t *node = PM_NODE_ALLOC(parser, pm_or_node_t);
5630
5631 *node = (pm_or_node_t) {
5632 .base = PM_NODE_INIT_NODES(parser, PM_OR_NODE, 0, left, right),
5633 .left = left,
5634 .right = right,
5635 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
5636 };
5637
5638 return node;
5639}
5640
5644static pm_parameters_node_t *
5645pm_parameters_node_create(pm_parser_t *parser) {
5646 pm_parameters_node_t *node = PM_NODE_ALLOC(parser, pm_parameters_node_t);
5647
5648 *node = (pm_parameters_node_t) {
5649 .base = PM_NODE_INIT_UNSET(parser, PM_PARAMETERS_NODE, 0),
5650 .rest = NULL,
5651 .keyword_rest = NULL,
5652 .block = NULL,
5653 .requireds = { 0 },
5654 .optionals = { 0 },
5655 .posts = { 0 },
5656 .keywords = { 0 }
5657 };
5658
5659 return node;
5660}
5661
5665static void
5666pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param) {
5667 if (params->base.location.start == NULL) {
5668 params->base.location.start = param->location.start;
5669 } else {
5670 params->base.location.start = params->base.location.start < param->location.start ? params->base.location.start : param->location.start;
5671 }
5672
5673 if (params->base.location.end == NULL) {
5674 params->base.location.end = param->location.end;
5675 } else {
5676 params->base.location.end = params->base.location.end > param->location.end ? params->base.location.end : param->location.end;
5677 }
5678}
5679
5683static void
5684pm_parameters_node_requireds_append(pm_parameters_node_t *params, pm_node_t *param) {
5685 pm_parameters_node_location_set(params, param);
5686 pm_node_list_append(&params->requireds, param);
5687}
5688
5692static void
5693pm_parameters_node_optionals_append(pm_parameters_node_t *params, pm_optional_parameter_node_t *param) {
5694 pm_parameters_node_location_set(params, UP(param));
5695 pm_node_list_append(&params->optionals, UP(param));
5696}
5697
5701static void
5702pm_parameters_node_posts_append(pm_parameters_node_t *params, pm_node_t *param) {
5703 pm_parameters_node_location_set(params, param);
5704 pm_node_list_append(&params->posts, param);
5705}
5706
5710static void
5711pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
5712 pm_parameters_node_location_set(params, param);
5713 params->rest = param;
5714}
5715
5719static void
5720pm_parameters_node_keywords_append(pm_parameters_node_t *params, pm_node_t *param) {
5721 pm_parameters_node_location_set(params, param);
5722 pm_node_list_append(&params->keywords, param);
5723}
5724
5728static void
5729pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
5730 assert(params->keyword_rest == NULL);
5731 pm_parameters_node_location_set(params, param);
5732 params->keyword_rest = param;
5733}
5734
5738static void
5739pm_parameters_node_block_set(pm_parameters_node_t *params, pm_block_parameter_node_t *param) {
5740 assert(params->block == NULL);
5741 pm_parameters_node_location_set(params, UP(param));
5742 params->block = param;
5743}
5744
5748static pm_program_node_t *
5749pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) {
5750 pm_program_node_t *node = PM_NODE_ALLOC(parser, pm_program_node_t);
5751
5752 *node = (pm_program_node_t) {
5753 .base = PM_NODE_INIT_NODE(parser, PM_PROGRAM_NODE, 0, statements),
5754 .locals = *locals,
5755 .statements = statements
5756 };
5757
5758 return node;
5759}
5760
5764static pm_parentheses_node_t *
5765pm_parentheses_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_node_t *body, const pm_token_t *closing, pm_node_flags_t flags) {
5766 pm_parentheses_node_t *node = PM_NODE_ALLOC(parser, pm_parentheses_node_t);
5767
5768 *node = (pm_parentheses_node_t) {
5769 .base = PM_NODE_INIT_TOKENS(parser, PM_PARENTHESES_NODE, flags, opening, closing),
5770 .body = body,
5771 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
5772 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
5773 };
5774
5775 return node;
5776}
5777
5781static pm_pinned_expression_node_t *
5782pm_pinned_expression_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *operator, const pm_token_t *lparen, const pm_token_t *rparen) {
5783 pm_pinned_expression_node_t *node = PM_NODE_ALLOC(parser, pm_pinned_expression_node_t);
5784
5785 *node = (pm_pinned_expression_node_t) {
5786 .base = PM_NODE_INIT_TOKENS(parser, PM_PINNED_EXPRESSION_NODE, 0, operator, rparen),
5787 .expression = expression,
5788 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
5789 .lparen_loc = PM_LOCATION_TOKEN_VALUE(lparen),
5790 .rparen_loc = PM_LOCATION_TOKEN_VALUE(rparen)
5791 };
5792
5793 return node;
5794}
5795
5799static pm_pinned_variable_node_t *
5800pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
5801 pm_pinned_variable_node_t *node = PM_NODE_ALLOC(parser, pm_pinned_variable_node_t);
5802
5803 *node = (pm_pinned_variable_node_t) {
5804 .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_PINNED_VARIABLE_NODE, 0, operator, variable),
5805 .variable = variable,
5806 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
5807 };
5808
5809 return node;
5810}
5811
5815static pm_post_execution_node_t *
5816pm_post_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
5817 pm_post_execution_node_t *node = PM_NODE_ALLOC(parser, pm_post_execution_node_t);
5818
5819 *node = (pm_post_execution_node_t) {
5820 .base = PM_NODE_INIT_TOKENS(parser, PM_POST_EXECUTION_NODE, 0, keyword, closing),
5821 .statements = statements,
5822 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
5823 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
5824 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
5825 };
5826
5827 return node;
5828}
5829
5833static pm_pre_execution_node_t *
5834pm_pre_execution_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
5835 pm_pre_execution_node_t *node = PM_NODE_ALLOC(parser, pm_pre_execution_node_t);
5836
5837 *node = (pm_pre_execution_node_t) {
5838 .base = PM_NODE_INIT_TOKENS(parser, PM_PRE_EXECUTION_NODE, 0, keyword, closing),
5839 .statements = statements,
5840 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
5841 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
5842 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing)
5843 };
5844
5845 return node;
5846}
5847
5851static pm_range_node_t *
5852pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
5853 pm_assert_value_expression(parser, left);
5854 pm_assert_value_expression(parser, right);
5855
5856 pm_range_node_t *node = PM_NODE_ALLOC(parser, pm_range_node_t);
5857 pm_node_flags_t flags = 0;
5858
5859 // Indicate that this node is an exclusive range if the operator is `...`.
5860 if (operator->type == PM_TOKEN_DOT_DOT_DOT || operator->type == PM_TOKEN_UDOT_DOT_DOT) {
5861 flags |= PM_RANGE_FLAGS_EXCLUDE_END;
5862 }
5863
5864 // Indicate that this node is a static literal (i.e., can be compiled with
5865 // a putobject in CRuby) if the left and right are implicit nil, explicit
5866 // nil, or integers.
5867 if (
5868 (left == NULL || PM_NODE_TYPE_P(left, PM_NIL_NODE) || PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) &&
5869 (right == NULL || PM_NODE_TYPE_P(right, PM_NIL_NODE) || PM_NODE_TYPE_P(right, PM_INTEGER_NODE))
5870 ) {
5871 flags |= PM_NODE_FLAG_STATIC_LITERAL;
5872 }
5873
5874 *node = (pm_range_node_t) {
5875 .base = PM_NODE_INIT(parser, PM_RANGE_NODE, flags, (left == NULL ? operator->start : left->location.start), (right == NULL ? operator->end : right->location.end)),
5876 .left = left,
5877 .right = right,
5878 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
5879 };
5880
5881 return node;
5882}
5883
5887static pm_redo_node_t *
5888pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) {
5889 assert(token->type == PM_TOKEN_KEYWORD_REDO);
5890 pm_redo_node_t *node = PM_NODE_ALLOC(parser, pm_redo_node_t);
5891
5892 *node = (pm_redo_node_t) {
5893 .base = PM_NODE_INIT_TOKEN(parser, PM_REDO_NODE, 0, token)
5894 };
5895
5896 return node;
5897}
5898
5903static pm_regular_expression_node_t *
5904pm_regular_expression_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *unescaped) {
5905 pm_regular_expression_node_t *node = PM_NODE_ALLOC(parser, pm_regular_expression_node_t);
5906 pm_node_flags_t flags = pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL;
5907
5908 *node = (pm_regular_expression_node_t) {
5909 .base = PM_NODE_INIT_TOKENS(parser, PM_REGULAR_EXPRESSION_NODE, flags, opening, closing),
5910 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
5911 .content_loc = PM_LOCATION_TOKEN_VALUE(content),
5912 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
5913 .unescaped = *unescaped
5914 };
5915
5916 return node;
5917}
5918
5922static inline pm_regular_expression_node_t *
5923pm_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
5924 return pm_regular_expression_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
5925}
5926
5930static pm_required_parameter_node_t *
5931pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
5932 pm_required_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_required_parameter_node_t);
5933
5934 *node = (pm_required_parameter_node_t) {
5935 .base = PM_NODE_INIT_TOKEN(parser, PM_REQUIRED_PARAMETER_NODE, 0, token),
5936 .name = pm_parser_constant_id_token(parser, token)
5937 };
5938
5939 return node;
5940}
5941
5945static pm_rescue_modifier_node_t *
5946pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *keyword, pm_node_t *rescue_expression) {
5947 pm_rescue_modifier_node_t *node = PM_NODE_ALLOC(parser, pm_rescue_modifier_node_t);
5948
5949 *node = (pm_rescue_modifier_node_t) {
5950 .base = PM_NODE_INIT_NODES(parser, PM_RESCUE_MODIFIER_NODE, 0, expression, rescue_expression),
5951 .expression = expression,
5952 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
5953 .rescue_expression = rescue_expression
5954 };
5955
5956 return node;
5957}
5958
5962static pm_rescue_node_t *
5963pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
5964 pm_rescue_node_t *node = PM_NODE_ALLOC(parser, pm_rescue_node_t);
5965
5966 *node = (pm_rescue_node_t) {
5967 .base = PM_NODE_INIT_TOKEN(parser, PM_RESCUE_NODE, 0, keyword),
5968 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
5969 .operator_loc = { 0 },
5970 .then_keyword_loc = { 0 },
5971 .reference = NULL,
5972 .statements = NULL,
5973 .subsequent = NULL,
5974 .exceptions = { 0 }
5975 };
5976
5977 return node;
5978}
5979
5980static inline void
5981pm_rescue_node_operator_set(pm_rescue_node_t *node, const pm_token_t *operator) {
5982 node->operator_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(operator);
5983}
5984
5988static void
5989pm_rescue_node_reference_set(pm_rescue_node_t *node, pm_node_t *reference) {
5990 node->reference = reference;
5991 node->base.location.end = reference->location.end;
5992}
5993
5997static void
5998pm_rescue_node_statements_set(pm_rescue_node_t *node, pm_statements_node_t *statements) {
5999 node->statements = statements;
6000 if (pm_statements_node_body_length(statements) > 0) {
6001 node->base.location.end = statements->base.location.end;
6002 }
6003}
6004
6008static void
6009pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subsequent) {
6010 node->subsequent = subsequent;
6011 node->base.location.end = subsequent->base.location.end;
6012}
6013
6017static void
6018pm_rescue_node_exceptions_append(pm_rescue_node_t *node, pm_node_t *exception) {
6019 pm_node_list_append(&node->exceptions, exception);
6020 node->base.location.end = exception->location.end;
6021}
6022
6026static pm_rest_parameter_node_t *
6027pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
6028 pm_rest_parameter_node_t *node = PM_NODE_ALLOC(parser, pm_rest_parameter_node_t);
6029
6030 *node = (pm_rest_parameter_node_t) {
6031 .base = (
6032 (name->type == PM_TOKEN_NOT_PROVIDED)
6033 ? PM_NODE_INIT_TOKEN(parser, PM_REST_PARAMETER_NODE, 0, operator)
6034 : PM_NODE_INIT_TOKENS(parser, PM_REST_PARAMETER_NODE, 0, operator, name)
6035 ),
6036 .name = pm_parser_optional_constant_id_token(parser, name),
6037 .name_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(name),
6038 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator)
6039 };
6040
6041 return node;
6042}
6043
6047static pm_retry_node_t *
6048pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) {
6049 assert(token->type == PM_TOKEN_KEYWORD_RETRY);
6050 pm_retry_node_t *node = PM_NODE_ALLOC(parser, pm_retry_node_t);
6051
6052 *node = (pm_retry_node_t) {
6053 .base = PM_NODE_INIT_TOKEN(parser, PM_RETRY_NODE, 0, token)
6054 };
6055
6056 return node;
6057}
6058
6062static pm_return_node_t *
6063pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
6064 pm_return_node_t *node = PM_NODE_ALLOC(parser, pm_return_node_t);
6065
6066 *node = (pm_return_node_t) {
6067 .base = (
6068 (arguments == NULL)
6069 ? PM_NODE_INIT_TOKEN(parser, PM_RETURN_NODE, 0, keyword)
6070 : PM_NODE_INIT_TOKEN_NODE(parser, PM_RETURN_NODE, 0, keyword, arguments)
6071 ),
6072 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
6073 .arguments = arguments
6074 };
6075
6076 return node;
6077}
6078
6082static pm_self_node_t *
6083pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) {
6084 assert(token->type == PM_TOKEN_KEYWORD_SELF);
6085 pm_self_node_t *node = PM_NODE_ALLOC(parser, pm_self_node_t);
6086
6087 *node = (pm_self_node_t) {
6088 .base = PM_NODE_INIT_TOKEN(parser, PM_SELF_NODE, 0, token)
6089 };
6090
6091 return node;
6092}
6093
6097static pm_shareable_constant_node_t *
6098pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shareable_constant_value_t value) {
6099 pm_shareable_constant_node_t *node = PM_NODE_ALLOC(parser, pm_shareable_constant_node_t);
6100
6101 *node = (pm_shareable_constant_node_t) {
6102 .base = PM_NODE_INIT_NODE(parser, PM_SHAREABLE_CONSTANT_NODE, (pm_node_flags_t) value, write),
6103 .write = write
6104 };
6105
6106 return node;
6107}
6108
6112static pm_singleton_class_node_t *
6113pm_singleton_class_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, const pm_token_t *class_keyword, const pm_token_t *operator, pm_node_t *expression, pm_node_t *body, const pm_token_t *end_keyword) {
6114 pm_singleton_class_node_t *node = PM_NODE_ALLOC(parser, pm_singleton_class_node_t);
6115
6116 *node = (pm_singleton_class_node_t) {
6117 .base = PM_NODE_INIT_TOKENS(parser, PM_SINGLETON_CLASS_NODE, 0, class_keyword, end_keyword),
6118 .locals = *locals,
6119 .class_keyword_loc = PM_LOCATION_TOKEN_VALUE(class_keyword),
6120 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
6121 .expression = expression,
6122 .body = body,
6123 .end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword)
6124 };
6125
6126 return node;
6127}
6128
6132static pm_source_encoding_node_t *
6133pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) {
6134 assert(token->type == PM_TOKEN_KEYWORD___ENCODING__);
6135 pm_source_encoding_node_t *node = PM_NODE_ALLOC(parser, pm_source_encoding_node_t);
6136
6137 *node = (pm_source_encoding_node_t) {
6138 .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_ENCODING_NODE, PM_NODE_FLAG_STATIC_LITERAL, token)
6139 };
6140
6141 return node;
6142}
6143
6147static pm_source_file_node_t*
6148pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) {
6149 pm_source_file_node_t *node = PM_NODE_ALLOC(parser, pm_source_file_node_t);
6150 assert(file_keyword->type == PM_TOKEN_KEYWORD___FILE__);
6151
6152 pm_node_flags_t flags = 0;
6153
6154 switch (parser->frozen_string_literal) {
6155 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6156 flags |= PM_STRING_FLAGS_MUTABLE;
6157 break;
6158 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6159 flags |= PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6160 break;
6161 }
6162
6163 *node = (pm_source_file_node_t) {
6164 .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_FILE_NODE, flags, file_keyword),
6165 .filepath = parser->filepath
6166 };
6167
6168 return node;
6169}
6170
6174static pm_source_line_node_t *
6175pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) {
6176 assert(token->type == PM_TOKEN_KEYWORD___LINE__);
6177 pm_source_line_node_t *node = PM_NODE_ALLOC(parser, pm_source_line_node_t);
6178
6179 *node = (pm_source_line_node_t) {
6180 .base = PM_NODE_INIT_TOKEN(parser, PM_SOURCE_LINE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token)
6181 };
6182
6183 return node;
6184}
6185
6189static pm_splat_node_t *
6190pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
6191 pm_splat_node_t *node = PM_NODE_ALLOC(parser, pm_splat_node_t);
6192
6193 *node = (pm_splat_node_t) {
6194 .base = (
6195 (expression == NULL)
6196 ? PM_NODE_INIT_TOKEN(parser, PM_SPLAT_NODE, 0, operator)
6197 : PM_NODE_INIT_TOKEN_NODE(parser, PM_SPLAT_NODE, 0, operator, expression)
6198 ),
6199 .operator_loc = PM_LOCATION_TOKEN_VALUE(operator),
6200 .expression = expression
6201 };
6202
6203 return node;
6204}
6205
6209static pm_statements_node_t *
6210pm_statements_node_create(pm_parser_t *parser) {
6211 pm_statements_node_t *node = PM_NODE_ALLOC(parser, pm_statements_node_t);
6212
6213 *node = (pm_statements_node_t) {
6214 .base = PM_NODE_INIT_BASE(parser, PM_STATEMENTS_NODE, 0),
6215 .body = { 0 }
6216 };
6217
6218 return node;
6219}
6220
6224static size_t
6225pm_statements_node_body_length(pm_statements_node_t *node) {
6226 return node && node->body.size;
6227}
6228
6232static void
6233pm_statements_node_location_set(pm_statements_node_t *node, const uint8_t *start, const uint8_t *end) {
6234 node->base.location = (pm_location_t) { .start = start, .end = end };
6235}
6236
6241static inline void
6242pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement) {
6243 if (pm_statements_node_body_length(node) == 0 || statement->location.start < node->base.location.start) {
6244 node->base.location.start = statement->location.start;
6245 }
6246
6247 if (statement->location.end > node->base.location.end) {
6248 node->base.location.end = statement->location.end;
6249 }
6250}
6251
6255static void
6256pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline) {
6257 pm_statements_node_body_update(node, statement);
6258
6259 if (node->body.size > 0) {
6260 const pm_node_t *previous = node->body.nodes[node->body.size - 1];
6261
6262 switch (PM_NODE_TYPE(previous)) {
6263 case PM_BREAK_NODE:
6264 case PM_NEXT_NODE:
6265 case PM_REDO_NODE:
6266 case PM_RETRY_NODE:
6267 case PM_RETURN_NODE:
6268 pm_parser_warn_node(parser, statement, PM_WARN_UNREACHABLE_STATEMENT);
6269 break;
6270 default:
6271 break;
6272 }
6273 }
6274
6275 pm_node_list_append(&node->body, statement);
6276 if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6277}
6278
6282static void
6283pm_statements_node_body_prepend(pm_statements_node_t *node, pm_node_t *statement) {
6284 pm_statements_node_body_update(node, statement);
6285 pm_node_list_prepend(&node->body, statement);
6286 pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6287}
6288
6292static inline pm_string_node_t *
6293pm_string_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *string) {
6294 pm_string_node_t *node = PM_NODE_ALLOC(parser, pm_string_node_t);
6295 pm_node_flags_t flags = 0;
6296
6297 switch (parser->frozen_string_literal) {
6298 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6299 flags = PM_STRING_FLAGS_MUTABLE;
6300 break;
6301 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6302 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6303 break;
6304 }
6305
6306 const uint8_t *start = (opening->type == PM_TOKEN_NOT_PROVIDED ? content->start : opening->start);
6307 const uint8_t *end = (closing->type == PM_TOKEN_NOT_PROVIDED ? content->end : closing->end);
6308
6309 *node = (pm_string_node_t) {
6310 .base = PM_NODE_INIT(parser, PM_STRING_NODE, flags, start, end),
6311 .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
6312 .content_loc = PM_LOCATION_TOKEN_VALUE(content),
6313 .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
6314 .unescaped = *string
6315 };
6316
6317 return node;
6318}
6319
6323static pm_string_node_t *
6324pm_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6325 return pm_string_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6326}
6327
6332static pm_string_node_t *
6333pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6334 pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, &parser->current_string);
6335 parser->current_string = PM_STRING_EMPTY;
6336 return node;
6337}
6338
6342static pm_super_node_t *
6343pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_t *arguments) {
6344 assert(keyword->type == PM_TOKEN_KEYWORD_SUPER);
6345 pm_super_node_t *node = PM_NODE_ALLOC(parser, pm_super_node_t);
6346
6347 const uint8_t *end = pm_arguments_end(arguments);
6348 if (end == NULL) {
6349 assert(false && "unreachable");
6350 }
6351
6352 *node = (pm_super_node_t) {
6353 .base = PM_NODE_INIT(parser, PM_SUPER_NODE, 0, keyword->start, end),
6354 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
6355 .lparen_loc = arguments->opening_loc,
6356 .arguments = arguments->arguments,
6357 .rparen_loc = arguments->closing_loc,
6358 .block = arguments->block
6359 };
6360
6361 return node;
6362}
6363
6368static bool
6369pm_ascii_only_p(const pm_string_t *contents) {
6370 const size_t length = pm_string_length(contents);
6371 const uint8_t *source = pm_string_source(contents);
6372
6373 for (size_t index = 0; index < length; index++) {
6374 if (source[index] & 0x80) return false;
6375 }
6376
6377 return true;
6378}
6379
6383static void
6384parse_symbol_encoding_validate_utf8(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6385 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6386 size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor);
6387
6388 if (width == 0) {
6389 pm_parser_err(parser, location->start, location->end, PM_ERR_INVALID_SYMBOL);
6390 break;
6391 }
6392
6393 cursor += width;
6394 }
6395}
6396
6401static void
6402parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6403 const pm_encoding_t *encoding = parser->encoding;
6404
6405 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6406 size_t width = encoding->char_width(cursor, end - cursor);
6407
6408 if (width == 0) {
6409 pm_parser_err(parser, location->start, location->end, PM_ERR_INVALID_SYMBOL);
6410 break;
6411 }
6412
6413 cursor += width;
6414 }
6415}
6416
6426static inline pm_node_flags_t
6427parse_symbol_encoding(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents, bool validate) {
6428 if (parser->explicit_encoding != NULL) {
6429 // A Symbol may optionally have its encoding explicitly set. This will
6430 // happen if an escape sequence results in a non-ASCII code point.
6431 if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
6432 if (validate) parse_symbol_encoding_validate_utf8(parser, location, contents);
6433 return PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
6434 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
6435 return PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING;
6436 } else if (validate) {
6437 parse_symbol_encoding_validate_other(parser, location, contents);
6438 }
6439 } else if (pm_ascii_only_p(contents)) {
6440 // Ruby stipulates that all source files must use an ASCII-compatible
6441 // encoding. Thus, all symbols appearing in source are eligible for
6442 // "downgrading" to US-ASCII.
6443 return PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
6444 } else if (validate) {
6445 parse_symbol_encoding_validate_other(parser, location, contents);
6446 }
6447
6448 return 0;
6449}
6450
6451static pm_node_flags_t
6452parse_and_validate_regular_expression_encoding_modifier(pm_parser_t *parser, const pm_string_t *source, bool ascii_only, pm_node_flags_t flags, char modifier, const pm_encoding_t *modifier_encoding) {
6453 assert ((modifier == 'n' && modifier_encoding == PM_ENCODING_ASCII_8BIT_ENTRY) ||
6454 (modifier == 'u' && modifier_encoding == PM_ENCODING_UTF_8_ENTRY) ||
6455 (modifier == 'e' && modifier_encoding == PM_ENCODING_EUC_JP_ENTRY) ||
6456 (modifier == 's' && modifier_encoding == PM_ENCODING_WINDOWS_31J_ENTRY));
6457
6458 // There's special validation logic used if a string does not contain any character escape sequences.
6459 if (parser->explicit_encoding == NULL) {
6460 // If an ASCII-only string without character escapes is used with an encoding modifier, then resulting Regexp
6461 // has the modifier encoding, unless the ASCII-8BIT modifier is used, in which case the Regexp "downgrades" to
6462 // the US-ASCII encoding.
6463 if (ascii_only) {
6464 return modifier == 'n' ? PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING : flags;
6465 }
6466
6467 if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
6468 if (!ascii_only) {
6469 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name);
6470 }
6471 } else if (parser->encoding != modifier_encoding) {
6472 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH, modifier, parser->encoding->name);
6473
6474 if (modifier == 'n' && !ascii_only) {
6475 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_NON_ESCAPED_MBC, (int) pm_string_length(source), (const char *) pm_string_source(source));
6476 }
6477 }
6478
6479 return flags;
6480 }
6481
6482 // TODO (nirvdrum 21-Feb-2024): To validate regexp sources with character escape sequences we need to know whether hex or Unicode escape sequences were used and Prism doesn't currently provide that data. We handle a subset of unambiguous cases in the meanwhile.
6483 bool mixed_encoding = false;
6484
6485 if (mixed_encoding) {
6486 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_ESCAPE, (int) pm_string_length(source), (const char *) pm_string_source(source));
6487 } else if (modifier != 'n' && parser->explicit_encoding == PM_ENCODING_ASCII_8BIT_ENTRY) {
6488 // TODO (nirvdrum 21-Feb-2024): Validate the content is valid in the modifier encoding. Do this on-demand so we don't pay the cost of computation unnecessarily.
6489 bool valid_string_in_modifier_encoding = true;
6490
6491 if (!valid_string_in_modifier_encoding) {
6492 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_ESCAPE, (int) pm_string_length(source), (const char *) pm_string_source(source));
6493 }
6494 } else if (modifier != 'u' && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
6495 // TODO (nirvdrum 21-Feb-2024): There's currently no way to tell if the source used hex or Unicode character escapes from `explicit_encoding` alone. If the source encoding was already UTF-8, both character escape types would set `explicit_encoding` to UTF-8, but need to be processed differently. Skip for now.
6496 if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
6497 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING, (int) pm_string_length(source), (const char *) pm_string_source(source));
6498 }
6499 }
6500
6501 // We've determined the encoding would naturally be EUC-JP and there is no need to force the encoding to anything else.
6502 return flags;
6503}
6504
6511static pm_node_flags_t
6512parse_and_validate_regular_expression_encoding(pm_parser_t *parser, const pm_string_t *source, bool ascii_only, pm_node_flags_t flags) {
6513 // TODO (nirvdrum 22-Feb-2024): CRuby reports a special Regexp-specific error for invalid Unicode ranges. We either need to scan again or modify the "invalid Unicode escape sequence" message we already report.
6514 bool valid_unicode_range = true;
6515 if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && !valid_unicode_range) {
6516 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_REGEXP_INVALID_UNICODE_RANGE, (int) pm_string_length(source), (const char *) pm_string_source(source));
6517 return flags;
6518 }
6519
6520 // US-ASCII strings do not admit multi-byte character literals. However, character escape sequences corresponding
6521 // to multi-byte characters are allowed.
6522 if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY && parser->explicit_encoding == NULL && !ascii_only) {
6523 // CRuby will continue processing even though a SyntaxError has already been detected. It may result in the
6524 // following error message appearing twice. We do the same for compatibility.
6525 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->encoding->name);
6526 }
6527
6535
6536 if (flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) {
6537 return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 'n', PM_ENCODING_ASCII_8BIT_ENTRY);
6538 }
6539
6540 if (flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) {
6541 return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 'u', PM_ENCODING_UTF_8_ENTRY);
6542 }
6543
6544 if (flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) {
6545 return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 'e', PM_ENCODING_EUC_JP_ENTRY);
6546 }
6547
6548 if (flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) {
6549 return parse_and_validate_regular_expression_encoding_modifier(parser, source, ascii_only, flags, 's', PM_ENCODING_WINDOWS_31J_ENTRY);
6550 }
6551
6552 // At this point no encoding modifiers will be present on the regular expression as they would have already
6553 // been processed. Ruby stipulates that all source files must use an ASCII-compatible encoding. Thus, all
6554 // regular expressions without an encoding modifier appearing in source are eligible for "downgrading" to US-ASCII.
6555 if (ascii_only) {
6556 return PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING;
6557 }
6558
6559 // A Regexp may optionally have its encoding explicitly set via a character escape sequence in the source string
6560 // or by specifying a modifier.
6561 //
6562 // NB: an explicitly set encoding is ignored by Ruby if the Regexp consists of only US ASCII code points.
6563 if (parser->explicit_encoding != NULL) {
6564 if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
6565 return PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING;
6566 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
6567 return PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING;
6568 }
6569 }
6570
6571 return 0;
6572}
6573
6578static pm_symbol_node_t *
6579pm_symbol_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing, const pm_string_t *unescaped, pm_node_flags_t flags) {
6580 pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t);
6581
6582 const uint8_t *start = (opening->type == PM_TOKEN_NOT_PROVIDED ? value->start : opening->start);
6583 const uint8_t *end = (closing->type == PM_TOKEN_NOT_PROVIDED ? value->end : closing->end);
6584
6585 *node = (pm_symbol_node_t) {
6586 .base = PM_NODE_INIT(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | flags, start, end),
6587 .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
6588 .value_loc = PM_LOCATION_TOKEN_VALUE(value),
6589 .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
6590 .unescaped = *unescaped
6591 };
6592
6593 return node;
6594}
6595
6599static inline pm_symbol_node_t *
6600pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6601 return pm_symbol_node_create_unescaped(parser, opening, value, closing, &PM_STRING_EMPTY, 0);
6602}
6603
6607static pm_symbol_node_t *
6608pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6609 pm_symbol_node_t *node = pm_symbol_node_create_unescaped(parser, opening, value, closing, &parser->current_string, parse_symbol_encoding(parser, value, &parser->current_string, false));
6610 parser->current_string = PM_STRING_EMPTY;
6611 return node;
6612}
6613
6617static pm_symbol_node_t *
6618pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) {
6619 pm_symbol_node_t *node;
6620
6621 switch (token->type) {
6622 case PM_TOKEN_LABEL: {
6623 pm_token_t opening = not_provided(parser);
6624 pm_token_t closing = { .type = PM_TOKEN_LABEL_END, .start = token->end - 1, .end = token->end };
6625
6626 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end - 1 };
6627 node = pm_symbol_node_create(parser, &opening, &label, &closing);
6628
6629 assert((label.end - label.start) >= 0);
6630 pm_string_shared_init(&node->unescaped, label.start, label.end);
6631 pm_node_flag_set(UP(node), parse_symbol_encoding(parser, &label, &node->unescaped, false));
6632
6633 break;
6634 }
6635 case PM_TOKEN_MISSING: {
6636 pm_token_t opening = not_provided(parser);
6637 pm_token_t closing = not_provided(parser);
6638
6639 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end };
6640 node = pm_symbol_node_create(parser, &opening, &label, &closing);
6641 break;
6642 }
6643 default:
6644 assert(false && "unreachable");
6645 node = NULL;
6646 break;
6647 }
6648
6649 return node;
6650}
6651
6655static pm_symbol_node_t *
6656pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
6657 pm_symbol_node_t *node = PM_NODE_ALLOC(parser, pm_symbol_node_t);
6658
6659 *node = (pm_symbol_node_t) {
6660 .base = PM_NODE_INIT_BASE(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING),
6661 .value_loc = PM_LOCATION_NULL_VALUE(parser),
6662 .unescaped = { 0 }
6663 };
6664
6665 pm_string_constant_init(&node->unescaped, content, strlen(content));
6666 return node;
6667}
6668
6672static bool
6673pm_symbol_node_label_p(pm_node_t *node) {
6674 const uint8_t *end = NULL;
6675
6676 switch (PM_NODE_TYPE(node)) {
6677 case PM_SYMBOL_NODE:
6678 end = ((pm_symbol_node_t *) node)->closing_loc.end;
6679 break;
6680 case PM_INTERPOLATED_SYMBOL_NODE:
6681 end = ((pm_interpolated_symbol_node_t *) node)->closing_loc.end;
6682 break;
6683 default:
6684 return false;
6685 }
6686
6687 return (end != NULL) && (end[-1] == ':');
6688}
6689
6693static pm_symbol_node_t *
6694pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t *opening, const pm_token_t *closing) {
6695 pm_symbol_node_t *new_node = PM_NODE_ALLOC(parser, pm_symbol_node_t);
6696
6697 *new_node = (pm_symbol_node_t) {
6698 .base = PM_NODE_INIT_TOKENS(parser, PM_SYMBOL_NODE, PM_NODE_FLAG_STATIC_LITERAL, opening, closing),
6699 .opening_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
6700 .value_loc = node->content_loc,
6701 .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
6702 .unescaped = node->unescaped
6703 };
6704
6705 pm_token_t content = { .type = PM_TOKEN_IDENTIFIER, .start = node->content_loc.start, .end = node->content_loc.end };
6706 pm_node_flag_set(UP(new_node), parse_symbol_encoding(parser, &content, &node->unescaped, true));
6707
6708 // We are explicitly _not_ using pm_node_destroy here because we don't want
6709 // to trash the unescaped string. We could instead copy the string if we
6710 // know that it is owned, but we're taking the fast path for now.
6711 xfree(node);
6712
6713 return new_node;
6714}
6715
6719static pm_string_node_t *
6720pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) {
6721 pm_string_node_t *new_node = PM_NODE_ALLOC(parser, pm_string_node_t);
6722 pm_node_flags_t flags = 0;
6723
6724 switch (parser->frozen_string_literal) {
6725 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6726 flags = PM_STRING_FLAGS_MUTABLE;
6727 break;
6728 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6729 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6730 break;
6731 }
6732
6733 *new_node = (pm_string_node_t) {
6734 .base = PM_NODE_INIT_NODE(parser, PM_STRING_NODE, flags, node),
6735 .opening_loc = node->opening_loc,
6736 .content_loc = node->value_loc,
6737 .closing_loc = node->closing_loc,
6738 .unescaped = node->unescaped
6739 };
6740
6741 // We are explicitly _not_ using pm_node_destroy here because we don't want
6742 // to trash the unescaped string. We could instead copy the string if we
6743 // know that it is owned, but we're taking the fast path for now.
6744 xfree(node);
6745
6746 return new_node;
6747}
6748
6752static pm_true_node_t *
6753pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) {
6754 assert(token->type == PM_TOKEN_KEYWORD_TRUE);
6755 pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t);
6756
6757 *node = (pm_true_node_t) {
6758 .base = PM_NODE_INIT_TOKEN(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL, token)
6759 };
6760
6761 return node;
6762}
6763
6767static pm_true_node_t *
6768pm_true_node_synthesized_create(pm_parser_t *parser) {
6769 pm_true_node_t *node = PM_NODE_ALLOC(parser, pm_true_node_t);
6770
6771 *node = (pm_true_node_t) {
6772 .base = PM_NODE_INIT_BASE(parser, PM_TRUE_NODE, PM_NODE_FLAG_STATIC_LITERAL)
6773 };
6774
6775 return node;
6776}
6777
6781static pm_undef_node_t *
6782pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
6783 assert(token->type == PM_TOKEN_KEYWORD_UNDEF);
6784 pm_undef_node_t *node = PM_NODE_ALLOC(parser, pm_undef_node_t);
6785
6786 *node = (pm_undef_node_t) {
6787 .base = PM_NODE_INIT_TOKEN(parser, PM_UNDEF_NODE, 0, token),
6788 .keyword_loc = PM_LOCATION_TOKEN_VALUE(token),
6789 .names = { 0 }
6790 };
6791
6792 return node;
6793}
6794
6798static void
6799pm_undef_node_append(pm_undef_node_t *node, pm_node_t *name) {
6800 node->base.location.end = name->location.end;
6801 pm_node_list_append(&node->names, name);
6802}
6803
6807static pm_unless_node_t *
6808pm_unless_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, const pm_token_t *then_keyword, pm_statements_node_t *statements) {
6809 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6810
6811 pm_unless_node_t *node = PM_NODE_ALLOC(parser, pm_unless_node_t);
6812 pm_node_t *end = statements == NULL ? predicate : UP(statements);
6813
6814 *node = (pm_unless_node_t) {
6815 .base = PM_NODE_INIT_TOKEN_NODE(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, keyword, end),
6816 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
6817 .predicate = predicate,
6818 .then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword),
6819 .statements = statements,
6820 .else_clause = NULL,
6821 .end_keyword_loc = { 0 }
6822 };
6823
6824 return node;
6825}
6826
6830static pm_unless_node_t *
6831pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) {
6832 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6833 pm_unless_node_t *node = PM_NODE_ALLOC(parser, pm_unless_node_t);
6834
6835 pm_statements_node_t *statements = pm_statements_node_create(parser);
6836 pm_statements_node_body_append(parser, statements, statement, true);
6837
6838 *node = (pm_unless_node_t) {
6839 .base = PM_NODE_INIT_NODES(parser, PM_UNLESS_NODE, PM_NODE_FLAG_NEWLINE, statement, predicate),
6840 .keyword_loc = PM_LOCATION_TOKEN_VALUE(unless_keyword),
6841 .predicate = predicate,
6842 .then_keyword_loc = { 0 },
6843 .statements = statements,
6844 .else_clause = NULL,
6845 .end_keyword_loc = { 0 }
6846 };
6847
6848 return node;
6849}
6850
6851static inline void
6852pm_unless_node_end_keyword_loc_set(pm_unless_node_t *node, const pm_token_t *end_keyword) {
6853 node->end_keyword_loc = PM_LOCATION_TOKEN_VALUE(end_keyword);
6854 node->base.location.end = end_keyword->end;
6855}
6856
6862static void
6863pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statements) {
6864 assert(parser->current_block_exits != NULL);
6865
6866 // All of the block exits that we want to remove should be within the
6867 // statements, and since we are modifying the statements, we shouldn't have
6868 // to check the end location.
6869 const uint8_t *start = statements->base.location.start;
6870
6871 for (size_t index = parser->current_block_exits->size; index > 0; index--) {
6872 pm_node_t *block_exit = parser->current_block_exits->nodes[index - 1];
6873 if (block_exit->location.start < start) break;
6874
6875 // Implicitly remove from the list by lowering the size.
6876 parser->current_block_exits->size--;
6877 }
6878}
6879
6883static pm_until_node_t *
6884pm_until_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *do_keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
6885 pm_until_node_t *node = PM_NODE_ALLOC(parser, pm_until_node_t);
6886 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6887
6888 *node = (pm_until_node_t) {
6889 .base = PM_NODE_INIT_TOKENS(parser, PM_UNTIL_NODE, flags, keyword, closing),
6890 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
6891 .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword),
6892 .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
6893 .predicate = predicate,
6894 .statements = statements
6895 };
6896
6897 return node;
6898}
6899
6903static pm_until_node_t *
6904pm_until_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
6905 pm_until_node_t *node = PM_NODE_ALLOC(parser, pm_until_node_t);
6906 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6907 pm_loop_modifier_block_exits(parser, statements);
6908
6909 *node = (pm_until_node_t) {
6910 .base = PM_NODE_INIT_NODES(parser, PM_UNTIL_NODE, flags, statements, predicate),
6911 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
6912 .do_keyword_loc = { 0 },
6913 .closing_loc = { 0 },
6914 .predicate = predicate,
6915 .statements = statements
6916 };
6917
6918 return node;
6919}
6920
6924static pm_when_node_t *
6925pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
6926 pm_when_node_t *node = PM_NODE_ALLOC(parser, pm_when_node_t);
6927
6928 *node = (pm_when_node_t) {
6929 .base = PM_NODE_INIT_TOKEN(parser, PM_WHEN_NODE, 0, keyword),
6930 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
6931 .statements = NULL,
6932 .then_keyword_loc = { 0 },
6933 .conditions = { 0 }
6934 };
6935
6936 return node;
6937}
6938
6942static void
6943pm_when_node_conditions_append(pm_when_node_t *node, pm_node_t *condition) {
6944 node->base.location.end = condition->location.end;
6945 pm_node_list_append(&node->conditions, condition);
6946}
6947
6951static inline void
6952pm_when_node_then_keyword_loc_set(pm_when_node_t *node, const pm_token_t *then_keyword) {
6953 node->base.location.end = then_keyword->end;
6954 node->then_keyword_loc = PM_LOCATION_TOKEN_VALUE(then_keyword);
6955}
6956
6960static void
6961pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statements) {
6962 if (statements->base.location.end > node->base.location.end) {
6963 node->base.location.end = statements->base.location.end;
6964 }
6965
6966 node->statements = statements;
6967}
6968
6972static pm_while_node_t *
6973pm_while_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_token_t *do_keyword, const pm_token_t *closing, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
6974 pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t);
6975 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6976
6977 *node = (pm_while_node_t) {
6978 .base = PM_NODE_INIT_TOKENS(parser, PM_WHILE_NODE, flags, keyword, closing),
6979 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
6980 .do_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword),
6981 .closing_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
6982 .predicate = predicate,
6983 .statements = statements
6984 };
6985
6986 return node;
6987}
6988
6992static pm_while_node_t *
6993pm_while_node_modifier_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *predicate, pm_statements_node_t *statements, pm_node_flags_t flags) {
6994 pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t);
6995 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6996 pm_loop_modifier_block_exits(parser, statements);
6997
6998 *node = (pm_while_node_t) {
6999 .base = PM_NODE_INIT_NODES(parser, PM_WHILE_NODE, flags, statements, predicate),
7000 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
7001 .do_keyword_loc = { 0 },
7002 .closing_loc = { 0 },
7003 .predicate = predicate,
7004 .statements = statements
7005 };
7006
7007 return node;
7008}
7009
7013static pm_while_node_t *
7014pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_statements_node_t *statements) {
7015 pm_while_node_t *node = PM_NODE_ALLOC(parser, pm_while_node_t);
7016
7017 *node = (pm_while_node_t) {
7018 .base = PM_NODE_INIT_BASE(parser, PM_WHILE_NODE, 0),
7019 .keyword_loc = PM_LOCATION_NULL_VALUE(parser),
7020 .do_keyword_loc = PM_LOCATION_NULL_VALUE(parser),
7021 .closing_loc = PM_LOCATION_NULL_VALUE(parser),
7022 .predicate = predicate,
7023 .statements = statements
7024 };
7025
7026 return node;
7027}
7028
7033static pm_x_string_node_t *
7034pm_xstring_node_create_unescaped(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing, const pm_string_t *unescaped) {
7035 pm_x_string_node_t *node = PM_NODE_ALLOC(parser, pm_x_string_node_t);
7036
7037 *node = (pm_x_string_node_t) {
7038 .base = PM_NODE_INIT_TOKENS(parser, PM_X_STRING_NODE, PM_STRING_FLAGS_FROZEN, opening, closing),
7039 .opening_loc = PM_LOCATION_TOKEN_VALUE(opening),
7040 .content_loc = PM_LOCATION_TOKEN_VALUE(content),
7041 .closing_loc = PM_LOCATION_TOKEN_VALUE(closing),
7042 .unescaped = *unescaped
7043 };
7044
7045 return node;
7046}
7047
7051static inline pm_x_string_node_t *
7052pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
7053 return pm_xstring_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
7054}
7055
7059static pm_yield_node_t *
7060pm_yield_node_create(pm_parser_t *parser, const pm_token_t *keyword, const pm_location_t *lparen_loc, pm_arguments_node_t *arguments, const pm_location_t *rparen_loc) {
7061 pm_yield_node_t *node = PM_NODE_ALLOC(parser, pm_yield_node_t);
7062
7063 const uint8_t *end;
7064 if (rparen_loc->start != NULL) {
7065 end = rparen_loc->end;
7066 } else if (arguments != NULL) {
7067 end = arguments->base.location.end;
7068 } else if (lparen_loc->start != NULL) {
7069 end = lparen_loc->end;
7070 } else {
7071 end = keyword->end;
7072 }
7073
7074 *node = (pm_yield_node_t) {
7075 .base = PM_NODE_INIT(parser, PM_YIELD_NODE, 0, keyword->start, end),
7076 .keyword_loc = PM_LOCATION_TOKEN_VALUE(keyword),
7077 .lparen_loc = *lparen_loc,
7078 .arguments = arguments,
7079 .rparen_loc = *rparen_loc
7080 };
7081
7082 return node;
7083}
7084
7089static int
7090pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant_id) {
7091 pm_scope_t *scope = parser->current_scope;
7092 int depth = 0;
7093
7094 while (scope != NULL) {
7095 if (pm_locals_find(&scope->locals, constant_id) != UINT32_MAX) return depth;
7096 if (scope->closed) break;
7097
7098 scope = scope->previous;
7099 depth++;
7100 }
7101
7102 return -1;
7103}
7104
7110static inline int
7111pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
7112 return pm_parser_local_depth_constant_id(parser, pm_parser_constant_id_token(parser, token));
7113}
7114
7118static inline void
7119pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7120 pm_locals_write(&parser->current_scope->locals, constant_id, start, end, reads);
7121}
7122
7126static pm_constant_id_t
7127pm_parser_local_add_location(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7128 pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, start, end);
7129 if (constant_id != 0) pm_parser_local_add(parser, constant_id, start, end, reads);
7130 return constant_id;
7131}
7132
7136static inline pm_constant_id_t
7137pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token, uint32_t reads) {
7138 return pm_parser_local_add_location(parser, token->start, token->end, reads);
7139}
7140
7144static pm_constant_id_t
7145pm_parser_local_add_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
7146 pm_constant_id_t constant_id = pm_parser_constant_id_owned(parser, start, length);
7147 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7148 return constant_id;
7149}
7150
7154static pm_constant_id_t
7155pm_parser_local_add_constant(pm_parser_t *parser, const char *start, size_t length) {
7156 pm_constant_id_t constant_id = pm_parser_constant_id_constant(parser, start, length);
7157 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7158 return constant_id;
7159}
7160
7168static bool
7169pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) {
7170 // We want to check whether the parameter name is a numbered parameter or
7171 // not.
7172 pm_refute_numbered_parameter(parser, name->start, name->end);
7173
7174 // Otherwise we'll fetch the constant id for the parameter name and check
7175 // whether it's already in the current scope.
7176 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name);
7177
7178 if (pm_locals_find(&parser->current_scope->locals, constant_id) != UINT32_MAX) {
7179 // Add an error if the parameter doesn't start with _ and has been seen before
7180 if ((name->start < name->end) && (*name->start != '_')) {
7181 pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_DUPLICATED);
7182 }
7183 return true;
7184 }
7185 return false;
7186}
7187
7191static void
7192pm_parser_scope_pop(pm_parser_t *parser) {
7193 pm_scope_t *scope = parser->current_scope;
7194 parser->current_scope = scope->previous;
7195 pm_locals_free(&scope->locals);
7196 pm_node_list_free(&scope->implicit_parameters);
7197 xfree(scope);
7198}
7199
7200/******************************************************************************/
7201/* Stack helpers */
7202/******************************************************************************/
7203
7207static inline void
7208pm_state_stack_push(pm_state_stack_t *stack, bool value) {
7209 *stack = (*stack << 1) | (value & 1);
7210}
7211
7215static inline void
7216pm_state_stack_pop(pm_state_stack_t *stack) {
7217 *stack >>= 1;
7218}
7219
7223static inline bool
7224pm_state_stack_p(const pm_state_stack_t *stack) {
7225 return *stack & 1;
7226}
7227
7228static inline void
7229pm_accepts_block_stack_push(pm_parser_t *parser, bool value) {
7230 // Use the negation of the value to prevent stack overflow.
7231 pm_state_stack_push(&parser->accepts_block_stack, !value);
7232}
7233
7234static inline void
7235pm_accepts_block_stack_pop(pm_parser_t *parser) {
7236 pm_state_stack_pop(&parser->accepts_block_stack);
7237}
7238
7239static inline bool
7240pm_accepts_block_stack_p(pm_parser_t *parser) {
7241 return !pm_state_stack_p(&parser->accepts_block_stack);
7242}
7243
7244static inline void
7245pm_do_loop_stack_push(pm_parser_t *parser, bool value) {
7246 pm_state_stack_push(&parser->do_loop_stack, value);
7247}
7248
7249static inline void
7250pm_do_loop_stack_pop(pm_parser_t *parser) {
7251 pm_state_stack_pop(&parser->do_loop_stack);
7252}
7253
7254static inline bool
7255pm_do_loop_stack_p(pm_parser_t *parser) {
7256 return pm_state_stack_p(&parser->do_loop_stack);
7257}
7258
7259/******************************************************************************/
7260/* Lexer check helpers */
7261/******************************************************************************/
7262
7267static inline uint8_t
7268peek_at(const pm_parser_t *parser, const uint8_t *cursor) {
7269 if (cursor < parser->end) {
7270 return *cursor;
7271 } else {
7272 return '\0';
7273 }
7274}
7275
7281static inline uint8_t
7282peek_offset(pm_parser_t *parser, ptrdiff_t offset) {
7283 return peek_at(parser, parser->current.end + offset);
7284}
7285
7290static inline uint8_t
7291peek(const pm_parser_t *parser) {
7292 return peek_at(parser, parser->current.end);
7293}
7294
7299static inline bool
7300match(pm_parser_t *parser, uint8_t value) {
7301 if (peek(parser) == value) {
7302 parser->current.end++;
7303 return true;
7304 }
7305 return false;
7306}
7307
7312static inline size_t
7313match_eol_at(pm_parser_t *parser, const uint8_t *cursor) {
7314 if (peek_at(parser, cursor) == '\n') {
7315 return 1;
7316 }
7317 if (peek_at(parser, cursor) == '\r' && peek_at(parser, cursor + 1) == '\n') {
7318 return 2;
7319 }
7320 return 0;
7321}
7322
7328static inline size_t
7329match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) {
7330 return match_eol_at(parser, parser->current.end + offset);
7331}
7332
7338static inline size_t
7339match_eol(pm_parser_t *parser) {
7340 return match_eol_at(parser, parser->current.end);
7341}
7342
7346static inline const uint8_t *
7347next_newline(const uint8_t *cursor, ptrdiff_t length) {
7348 assert(length >= 0);
7349
7350 // Note that it's okay for us to use memchr here to look for \n because none
7351 // of the encodings that we support have \n as a component of a multi-byte
7352 // character.
7353 return memchr(cursor, '\n', (size_t) length);
7354}
7355
7359static inline bool
7360ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) {
7361 return !lex_state_p(parser, PM_LEX_STATE_CLASS | PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME | PM_LEX_STATE_ENDFN) && space_seen && !pm_char_is_whitespace(peek(parser));
7362}
7363
7368static bool
7369parser_lex_magic_comment_encoding_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
7370 const pm_encoding_t *encoding = pm_encoding_find(start, end);
7371
7372 if (encoding != NULL) {
7373 if (parser->encoding != encoding) {
7374 parser->encoding = encoding;
7375 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
7376 }
7377
7378 parser->encoding_changed = (encoding != PM_ENCODING_UTF_8_ENTRY);
7379 return true;
7380 }
7381
7382 return false;
7383}
7384
7389static void
7390parser_lex_magic_comment_encoding(pm_parser_t *parser) {
7391 const uint8_t *cursor = parser->current.start + 1;
7392 const uint8_t *end = parser->current.end;
7393
7394 bool separator = false;
7395 while (true) {
7396 if (end - cursor <= 6) return;
7397 switch (cursor[6]) {
7398 case 'C': case 'c': cursor += 6; continue;
7399 case 'O': case 'o': cursor += 5; continue;
7400 case 'D': case 'd': cursor += 4; continue;
7401 case 'I': case 'i': cursor += 3; continue;
7402 case 'N': case 'n': cursor += 2; continue;
7403 case 'G': case 'g': cursor += 1; continue;
7404 case '=': case ':':
7405 separator = true;
7406 cursor += 6;
7407 break;
7408 default:
7409 cursor += 6;
7410 if (pm_char_is_whitespace(*cursor)) break;
7411 continue;
7412 }
7413 if (pm_strncasecmp(cursor - 6, (const uint8_t *) "coding", 6) == 0) break;
7414 separator = false;
7415 }
7416
7417 while (true) {
7418 do {
7419 if (++cursor >= end) return;
7420 } while (pm_char_is_whitespace(*cursor));
7421
7422 if (separator) break;
7423 if (*cursor != '=' && *cursor != ':') return;
7424
7425 separator = true;
7426 cursor++;
7427 }
7428
7429 const uint8_t *value_start = cursor;
7430 while ((*cursor == '-' || *cursor == '_' || parser->encoding->alnum_char(cursor, 1)) && ++cursor < end);
7431
7432 if (!parser_lex_magic_comment_encoding_value(parser, value_start, cursor)) {
7433 // If we were unable to parse the encoding value, then we've got an
7434 // issue because we didn't understand the encoding that the user was
7435 // trying to use. In this case we'll keep using the default encoding but
7436 // add an error to the parser to indicate an unsuccessful parse.
7437 pm_parser_err(parser, value_start, cursor, PM_ERR_INVALID_ENCODING_MAGIC_COMMENT);
7438 }
7439}
7440
7441typedef enum {
7442 PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE,
7443 PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE,
7444 PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID
7445} pm_magic_comment_boolean_value_t;
7446
7451static pm_magic_comment_boolean_value_t
7452parser_lex_magic_comment_boolean_value(const uint8_t *value_start, uint32_t value_length) {
7453 if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "true", 4) == 0) {
7454 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE;
7455 } else if (value_length == 5 && pm_strncasecmp(value_start, (const uint8_t *) "false", 5) == 0) {
7456 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE;
7457 } else {
7458 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID;
7459 }
7460}
7461
7462static inline bool
7463pm_char_is_magic_comment_key_delimiter(const uint8_t b) {
7464 return b == '\'' || b == '"' || b == ':' || b == ';';
7465}
7466
7472static inline const uint8_t *
7473parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor, const uint8_t *end) {
7474 while ((cursor + 3 <= end) && (cursor = pm_memchr(cursor, '-', (size_t) (end - cursor), parser->encoding_changed, parser->encoding)) != NULL) {
7475 if (cursor + 3 <= end && cursor[1] == '*' && cursor[2] == '-') {
7476 return cursor;
7477 }
7478 cursor++;
7479 }
7480 return NULL;
7481}
7482
7493static inline bool
7494parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
7495 bool result = true;
7496
7497 const uint8_t *start = parser->current.start + 1;
7498 const uint8_t *end = parser->current.end;
7499 if (end - start <= 7) return false;
7500
7501 const uint8_t *cursor;
7502 bool indicator = false;
7503
7504 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7505 start = cursor + 3;
7506
7507 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7508 end = cursor;
7509 indicator = true;
7510 } else {
7511 // If we have a start marker but not an end marker, then we cannot
7512 // have a magic comment.
7513 return false;
7514 }
7515 }
7516
7517 cursor = start;
7518 while (cursor < end) {
7519 while (cursor < end && (pm_char_is_magic_comment_key_delimiter(*cursor) || pm_char_is_whitespace(*cursor))) cursor++;
7520
7521 const uint8_t *key_start = cursor;
7522 while (cursor < end && (!pm_char_is_magic_comment_key_delimiter(*cursor) && !pm_char_is_whitespace(*cursor))) cursor++;
7523
7524 const uint8_t *key_end = cursor;
7525 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7526 if (cursor == end) break;
7527
7528 if (*cursor == ':') {
7529 cursor++;
7530 } else {
7531 if (!indicator) return false;
7532 continue;
7533 }
7534
7535 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7536 if (cursor == end) break;
7537
7538 const uint8_t *value_start;
7539 const uint8_t *value_end;
7540
7541 if (*cursor == '"') {
7542 value_start = ++cursor;
7543 for (; cursor < end && *cursor != '"'; cursor++) {
7544 if (*cursor == '\\' && (cursor + 1 < end)) cursor++;
7545 }
7546 value_end = cursor;
7547 if (cursor < end && *cursor == '"') cursor++;
7548 } else {
7549 value_start = cursor;
7550 while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++;
7551 value_end = cursor;
7552 }
7553
7554 if (indicator) {
7555 while (cursor < end && (*cursor == ';' || pm_char_is_whitespace(*cursor))) cursor++;
7556 } else {
7557 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7558 if (cursor != end) return false;
7559 }
7560
7561 // Here, we need to do some processing on the key to swap out dashes for
7562 // underscores. We only need to do this if there _is_ a dash in the key.
7563 pm_string_t key;
7564 const size_t key_length = (size_t) (key_end - key_start);
7565 const uint8_t *dash = pm_memchr(key_start, '-', key_length, parser->encoding_changed, parser->encoding);
7566
7567 if (dash == NULL) {
7568 pm_string_shared_init(&key, key_start, key_end);
7569 } else {
7570 uint8_t *buffer = xmalloc(key_length);
7571 if (buffer == NULL) break;
7572
7573 memcpy(buffer, key_start, key_length);
7574 buffer[dash - key_start] = '_';
7575
7576 while ((dash = pm_memchr(dash + 1, '-', (size_t) (key_end - dash - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7577 buffer[dash - key_start] = '_';
7578 }
7579
7580 pm_string_owned_init(&key, buffer, key_length);
7581 }
7582
7583 // Finally, we can start checking the key against the list of known
7584 // magic comment keys, and potentially change state based on that.
7585 const uint8_t *key_source = pm_string_source(&key);
7586 uint32_t value_length = (uint32_t) (value_end - value_start);
7587
7588 // We only want to attempt to compare against encoding comments if it's
7589 // the first line in the file (or the second in the case of a shebang).
7590 if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7591 if (
7592 (key_length == 8 && pm_strncasecmp(key_source, (const uint8_t *) "encoding", 8) == 0) ||
7593 (key_length == 6 && pm_strncasecmp(key_source, (const uint8_t *) "coding", 6) == 0)
7594 ) {
7595 result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7596 }
7597 }
7598
7599 if (key_length == 11) {
7600 if (pm_strncasecmp(key_source, (const uint8_t *) "warn_indent", 11) == 0) {
7601 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7602 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7603 PM_PARSER_WARN_TOKEN_FORMAT(
7604 parser,
7605 parser->current,
7606 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7607 (int) key_length,
7608 (const char *) key_source,
7609 (int) value_length,
7610 (const char *) value_start
7611 );
7612 break;
7613 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7614 parser->warn_mismatched_indentation = false;
7615 break;
7616 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7617 parser->warn_mismatched_indentation = true;
7618 break;
7619 }
7620 }
7621 } else if (key_length == 21) {
7622 if (pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) {
7623 // We only want to handle frozen string literal comments if it's
7624 // before any semantic tokens have been seen.
7625 if (semantic_token_seen) {
7626 pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
7627 } else {
7628 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7629 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7630 PM_PARSER_WARN_TOKEN_FORMAT(
7631 parser,
7632 parser->current,
7633 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7634 (int) key_length,
7635 (const char *) key_source,
7636 (int) value_length,
7637 (const char *) value_start
7638 );
7639 break;
7640 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7642 break;
7643 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7645 break;
7646 }
7647 }
7648 }
7649 } else if (key_length == 24) {
7650 if (pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) {
7651 const uint8_t *cursor = parser->current.start;
7652 while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
7653
7654 if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
7655 pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
7656 } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
7657 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
7658 } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
7659 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
7660 } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
7661 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
7662 } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
7663 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
7664 } else {
7665 PM_PARSER_WARN_TOKEN_FORMAT(
7666 parser,
7667 parser->current,
7668 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7669 (int) key_length,
7670 (const char *) key_source,
7671 (int) value_length,
7672 (const char *) value_start
7673 );
7674 }
7675 }
7676 }
7677
7678 // When we're done, we want to free the string in case we had to
7679 // allocate memory for it.
7680 pm_string_free(&key);
7681
7682 // Allocate a new magic comment node to append to the parser's list.
7684 if ((magic_comment = (pm_magic_comment_t *) xcalloc(1, sizeof(pm_magic_comment_t))) != NULL) {
7685 magic_comment->key_start = key_start;
7686 magic_comment->value_start = value_start;
7687 magic_comment->key_length = (uint32_t) key_length;
7688 magic_comment->value_length = value_length;
7689 pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment);
7690 }
7691 }
7692
7693 return result;
7694}
7695
7696/******************************************************************************/
7697/* Context manipulations */
7698/******************************************************************************/
7699
7700static const uint32_t context_terminators[] = {
7701 [PM_CONTEXT_NONE] = 0,
7702 [PM_CONTEXT_BEGIN] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7703 [PM_CONTEXT_BEGIN_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7704 [PM_CONTEXT_BEGIN_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7705 [PM_CONTEXT_BEGIN_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7706 [PM_CONTEXT_BLOCK_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7707 [PM_CONTEXT_BLOCK_KEYWORDS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7708 [PM_CONTEXT_BLOCK_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7709 [PM_CONTEXT_BLOCK_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7710 [PM_CONTEXT_BLOCK_PARAMETERS] = (1U << PM_TOKEN_PIPE),
7711 [PM_CONTEXT_BLOCK_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7712 [PM_CONTEXT_CASE_WHEN] = (1U << PM_TOKEN_KEYWORD_WHEN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7713 [PM_CONTEXT_CASE_IN] = (1U << PM_TOKEN_KEYWORD_IN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7714 [PM_CONTEXT_CLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7715 [PM_CONTEXT_CLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7716 [PM_CONTEXT_CLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7717 [PM_CONTEXT_CLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7718 [PM_CONTEXT_DEF] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7719 [PM_CONTEXT_DEF_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7720 [PM_CONTEXT_DEF_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7721 [PM_CONTEXT_DEF_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7722 [PM_CONTEXT_DEF_PARAMS] = (1U << PM_TOKEN_EOF),
7723 [PM_CONTEXT_DEFINED] = (1U << PM_TOKEN_EOF),
7724 [PM_CONTEXT_DEFAULT_PARAMS] = (1U << PM_TOKEN_COMMA) | (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7725 [PM_CONTEXT_ELSE] = (1U << PM_TOKEN_KEYWORD_END),
7726 [PM_CONTEXT_ELSIF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7727 [PM_CONTEXT_EMBEXPR] = (1U << PM_TOKEN_EMBEXPR_END),
7728 [PM_CONTEXT_FOR] = (1U << PM_TOKEN_KEYWORD_END),
7729 [PM_CONTEXT_FOR_INDEX] = (1U << PM_TOKEN_KEYWORD_IN),
7730 [PM_CONTEXT_IF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7731 [PM_CONTEXT_LAMBDA_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7732 [PM_CONTEXT_LAMBDA_DO_END] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7733 [PM_CONTEXT_LAMBDA_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7734 [PM_CONTEXT_LAMBDA_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7735 [PM_CONTEXT_LAMBDA_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7736 [PM_CONTEXT_LOOP_PREDICATE] = (1U << PM_TOKEN_KEYWORD_DO) | (1U << PM_TOKEN_KEYWORD_THEN),
7737 [PM_CONTEXT_MAIN] = (1U << PM_TOKEN_EOF),
7738 [PM_CONTEXT_MODULE] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7739 [PM_CONTEXT_MODULE_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7740 [PM_CONTEXT_MODULE_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7741 [PM_CONTEXT_MODULE_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7742 [PM_CONTEXT_MULTI_TARGET] = (1U << PM_TOKEN_EOF),
7743 [PM_CONTEXT_PARENS] = (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7744 [PM_CONTEXT_POSTEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7745 [PM_CONTEXT_PREDICATE] = (1U << PM_TOKEN_KEYWORD_THEN) | (1U << PM_TOKEN_NEWLINE) | (1U << PM_TOKEN_SEMICOLON),
7746 [PM_CONTEXT_PREEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7747 [PM_CONTEXT_RESCUE_MODIFIER] = (1U << PM_TOKEN_EOF),
7748 [PM_CONTEXT_SCLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7749 [PM_CONTEXT_SCLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7750 [PM_CONTEXT_SCLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7751 [PM_CONTEXT_SCLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7752 [PM_CONTEXT_TERNARY] = (1U << PM_TOKEN_EOF),
7753 [PM_CONTEXT_UNLESS] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7754 [PM_CONTEXT_UNTIL] = (1U << PM_TOKEN_KEYWORD_END),
7755 [PM_CONTEXT_WHILE] = (1U << PM_TOKEN_KEYWORD_END),
7756};
7757
7758static inline bool
7759context_terminator(pm_context_t context, pm_token_t *token) {
7760 return token->type < 32 && (context_terminators[context] & (1U << token->type));
7761}
7762
7767static pm_context_t
7768context_recoverable(const pm_parser_t *parser, pm_token_t *token) {
7769 pm_context_node_t *context_node = parser->current_context;
7770
7771 while (context_node != NULL) {
7772 if (context_terminator(context_node->context, token)) return context_node->context;
7773 context_node = context_node->prev;
7774 }
7775
7776 return PM_CONTEXT_NONE;
7777}
7778
7779static bool
7780context_push(pm_parser_t *parser, pm_context_t context) {
7781 pm_context_node_t *context_node = (pm_context_node_t *) xmalloc(sizeof(pm_context_node_t));
7782 if (context_node == NULL) return false;
7783
7784 *context_node = (pm_context_node_t) { .context = context, .prev = NULL };
7785
7786 if (parser->current_context == NULL) {
7787 parser->current_context = context_node;
7788 } else {
7789 context_node->prev = parser->current_context;
7790 parser->current_context = context_node;
7791 }
7792
7793 return true;
7794}
7795
7796static void
7797context_pop(pm_parser_t *parser) {
7798 pm_context_node_t *prev = parser->current_context->prev;
7799 xfree(parser->current_context);
7800 parser->current_context = prev;
7801}
7802
7803static bool
7804context_p(const pm_parser_t *parser, pm_context_t context) {
7805 pm_context_node_t *context_node = parser->current_context;
7806
7807 while (context_node != NULL) {
7808 if (context_node->context == context) return true;
7809 context_node = context_node->prev;
7810 }
7811
7812 return false;
7813}
7814
7815static bool
7816context_def_p(const pm_parser_t *parser) {
7817 pm_context_node_t *context_node = parser->current_context;
7818
7819 while (context_node != NULL) {
7820 switch (context_node->context) {
7821 case PM_CONTEXT_DEF:
7826 return true;
7827 case PM_CONTEXT_CLASS:
7831 case PM_CONTEXT_MODULE:
7835 case PM_CONTEXT_SCLASS:
7839 return false;
7840 default:
7841 context_node = context_node->prev;
7842 }
7843 }
7844
7845 return false;
7846}
7847
7852static const char *
7853context_human(pm_context_t context) {
7854 switch (context) {
7855 case PM_CONTEXT_NONE:
7856 assert(false && "unreachable");
7857 return "";
7858 case PM_CONTEXT_BEGIN: return "begin statement";
7859 case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block";
7860 case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block";
7861 case PM_CONTEXT_BLOCK_PARAMETERS: return "'|'..'|' block parameter";
7862 case PM_CONTEXT_CASE_WHEN: return "'when' clause";
7863 case PM_CONTEXT_CASE_IN: return "'in' clause";
7864 case PM_CONTEXT_CLASS: return "class definition";
7865 case PM_CONTEXT_DEF: return "method definition";
7866 case PM_CONTEXT_DEF_PARAMS: return "method parameters";
7867 case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value";
7868 case PM_CONTEXT_DEFINED: return "'defined?' expression";
7869 case PM_CONTEXT_ELSE:
7876 case PM_CONTEXT_SCLASS_ELSE: return "'else' clause";
7877 case PM_CONTEXT_ELSIF: return "'elsif' clause";
7878 case PM_CONTEXT_EMBEXPR: return "embedded expression";
7885 case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause";
7886 case PM_CONTEXT_FOR: return "for loop";
7887 case PM_CONTEXT_FOR_INDEX: return "for loop index";
7888 case PM_CONTEXT_IF: return "if statement";
7889 case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block";
7890 case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block";
7891 case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate";
7892 case PM_CONTEXT_MAIN: return "top level context";
7893 case PM_CONTEXT_MODULE: return "module definition";
7894 case PM_CONTEXT_MULTI_TARGET: return "multiple targets";
7895 case PM_CONTEXT_PARENS: return "parentheses";
7896 case PM_CONTEXT_POSTEXE: return "'END' block";
7897 case PM_CONTEXT_PREDICATE: return "predicate";
7898 case PM_CONTEXT_PREEXE: return "'BEGIN' block";
7906 case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause";
7907 case PM_CONTEXT_SCLASS: return "singleton class definition";
7908 case PM_CONTEXT_TERNARY: return "ternary expression";
7909 case PM_CONTEXT_UNLESS: return "unless statement";
7910 case PM_CONTEXT_UNTIL: return "until statement";
7911 case PM_CONTEXT_WHILE: return "while statement";
7912 }
7913
7914 assert(false && "unreachable");
7915 return "";
7916}
7917
7918/******************************************************************************/
7919/* Specific token lexers */
7920/******************************************************************************/
7921
7922static inline void
7923pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *string, size_t length, const uint8_t *invalid) {
7924 if (invalid != NULL) {
7925 pm_diagnostic_id_t diag_id = (invalid == (string + length - 1)) ? PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING : PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER;
7926 pm_parser_err(parser, invalid, invalid + 1, diag_id);
7927 }
7928}
7929
7930static size_t
7931pm_strspn_binary_number_validate(pm_parser_t *parser, const uint8_t *string) {
7932 const uint8_t *invalid = NULL;
7933 size_t length = pm_strspn_binary_number(string, parser->end - string, &invalid);
7934 pm_strspn_number_validate(parser, string, length, invalid);
7935 return length;
7936}
7937
7938static size_t
7939pm_strspn_octal_number_validate(pm_parser_t *parser, const uint8_t *string) {
7940 const uint8_t *invalid = NULL;
7941 size_t length = pm_strspn_octal_number(string, parser->end - string, &invalid);
7942 pm_strspn_number_validate(parser, string, length, invalid);
7943 return length;
7944}
7945
7946static size_t
7947pm_strspn_decimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
7948 const uint8_t *invalid = NULL;
7949 size_t length = pm_strspn_decimal_number(string, parser->end - string, &invalid);
7950 pm_strspn_number_validate(parser, string, length, invalid);
7951 return length;
7952}
7953
7954static size_t
7955pm_strspn_hexadecimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
7956 const uint8_t *invalid = NULL;
7957 size_t length = pm_strspn_hexadecimal_number(string, parser->end - string, &invalid);
7958 pm_strspn_number_validate(parser, string, length, invalid);
7959 return length;
7960}
7961
7962static pm_token_type_t
7963lex_optional_float_suffix(pm_parser_t *parser, bool* seen_e) {
7964 pm_token_type_t type = PM_TOKEN_INTEGER;
7965
7966 // Here we're going to attempt to parse the optional decimal portion of a
7967 // float. If it's not there, then it's okay and we'll just continue on.
7968 if (peek(parser) == '.') {
7969 if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
7970 parser->current.end += 2;
7971 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
7972 type = PM_TOKEN_FLOAT;
7973 } else {
7974 // If we had a . and then something else, then it's not a float
7975 // suffix on a number it's a method call or something else.
7976 return type;
7977 }
7978 }
7979
7980 // Here we're going to attempt to parse the optional exponent portion of a
7981 // float. If it's not there, it's okay and we'll just continue on.
7982 if ((peek(parser) == 'e') || (peek(parser) == 'E')) {
7983 if ((peek_offset(parser, 1) == '+') || (peek_offset(parser, 1) == '-')) {
7984 parser->current.end += 2;
7985
7986 if (pm_char_is_decimal_digit(peek(parser))) {
7987 parser->current.end++;
7988 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
7989 } else {
7990 pm_parser_err_current(parser, PM_ERR_INVALID_FLOAT_EXPONENT);
7991 }
7992 } else if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
7993 parser->current.end++;
7994 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
7995 } else {
7996 return type;
7997 }
7998
7999 *seen_e = true;
8000 type = PM_TOKEN_FLOAT;
8001 }
8002
8003 return type;
8004}
8005
8006static pm_token_type_t
8007lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) {
8008 pm_token_type_t type = PM_TOKEN_INTEGER;
8009 *seen_e = false;
8010
8011 if (peek_offset(parser, -1) == '0') {
8012 switch (*parser->current.end) {
8013 // 0d1111 is a decimal number
8014 case 'd':
8015 case 'D':
8016 parser->current.end++;
8017 if (pm_char_is_decimal_digit(peek(parser))) {
8018 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8019 } else {
8020 match(parser, '_');
8021 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_DECIMAL);
8022 }
8023
8024 break;
8025
8026 // 0b1111 is a binary number
8027 case 'b':
8028 case 'B':
8029 parser->current.end++;
8030 if (pm_char_is_binary_digit(peek(parser))) {
8031 parser->current.end += pm_strspn_binary_number_validate(parser, parser->current.end);
8032 } else {
8033 match(parser, '_');
8034 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY);
8035 }
8036
8037 parser->integer_base = PM_INTEGER_BASE_FLAGS_BINARY;
8038 break;
8039
8040 // 0o1111 is an octal number
8041 case 'o':
8042 case 'O':
8043 parser->current.end++;
8044 if (pm_char_is_octal_digit(peek(parser))) {
8045 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8046 } else {
8047 match(parser, '_');
8048 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL);
8049 }
8050
8051 parser->integer_base = PM_INTEGER_BASE_FLAGS_OCTAL;
8052 break;
8053
8054 // 01111 is an octal number
8055 case '_':
8056 case '0':
8057 case '1':
8058 case '2':
8059 case '3':
8060 case '4':
8061 case '5':
8062 case '6':
8063 case '7':
8064 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8065 parser->integer_base = PM_INTEGER_BASE_FLAGS_OCTAL;
8066 break;
8067
8068 // 0x1111 is a hexadecimal number
8069 case 'x':
8070 case 'X':
8071 parser->current.end++;
8072 if (pm_char_is_hexadecimal_digit(peek(parser))) {
8073 parser->current.end += pm_strspn_hexadecimal_number_validate(parser, parser->current.end);
8074 } else {
8075 match(parser, '_');
8076 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL);
8077 }
8078
8079 parser->integer_base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL;
8080 break;
8081
8082 // 0.xxx is a float
8083 case '.': {
8084 type = lex_optional_float_suffix(parser, seen_e);
8085 break;
8086 }
8087
8088 // 0exxx is a float
8089 case 'e':
8090 case 'E': {
8091 type = lex_optional_float_suffix(parser, seen_e);
8092 break;
8093 }
8094 }
8095 } else {
8096 // If it didn't start with a 0, then we'll lex as far as we can into a
8097 // decimal number.
8098 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8099
8100 // Afterward, we'll lex as far as we can into an optional float suffix.
8101 type = lex_optional_float_suffix(parser, seen_e);
8102 }
8103
8104 // At this point we have a completed number, but we want to provide the user
8105 // with a good experience if they put an additional .xxx fractional
8106 // component on the end, so we'll check for that here.
8107 if (peek_offset(parser, 0) == '.' && pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8108 const uint8_t *fraction_start = parser->current.end;
8109 const uint8_t *fraction_end = parser->current.end + 2;
8110 fraction_end += pm_strspn_decimal_digit(fraction_end, parser->end - fraction_end);
8111 pm_parser_err(parser, fraction_start, fraction_end, PM_ERR_INVALID_NUMBER_FRACTION);
8112 }
8113
8114 return type;
8115}
8116
8117static pm_token_type_t
8118lex_numeric(pm_parser_t *parser) {
8119 pm_token_type_t type = PM_TOKEN_INTEGER;
8120 parser->integer_base = PM_INTEGER_BASE_FLAGS_DECIMAL;
8121
8122 if (parser->current.end < parser->end) {
8123 bool seen_e = false;
8124 type = lex_numeric_prefix(parser, &seen_e);
8125
8126 const uint8_t *end = parser->current.end;
8127 pm_token_type_t suffix_type = type;
8128
8129 if (type == PM_TOKEN_INTEGER) {
8130 if (match(parser, 'r')) {
8131 suffix_type = PM_TOKEN_INTEGER_RATIONAL;
8132
8133 if (match(parser, 'i')) {
8134 suffix_type = PM_TOKEN_INTEGER_RATIONAL_IMAGINARY;
8135 }
8136 } else if (match(parser, 'i')) {
8137 suffix_type = PM_TOKEN_INTEGER_IMAGINARY;
8138 }
8139 } else {
8140 if (!seen_e && match(parser, 'r')) {
8141 suffix_type = PM_TOKEN_FLOAT_RATIONAL;
8142
8143 if (match(parser, 'i')) {
8144 suffix_type = PM_TOKEN_FLOAT_RATIONAL_IMAGINARY;
8145 }
8146 } else if (match(parser, 'i')) {
8147 suffix_type = PM_TOKEN_FLOAT_IMAGINARY;
8148 }
8149 }
8150
8151 const uint8_t b = peek(parser);
8152 if (b != '\0' && (b >= 0x80 || ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) || b == '_')) {
8153 parser->current.end = end;
8154 } else {
8155 type = suffix_type;
8156 }
8157 }
8158
8159 return type;
8160}
8161
8162static pm_token_type_t
8163lex_global_variable(pm_parser_t *parser) {
8164 if (parser->current.end >= parser->end) {
8165 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8166 return PM_TOKEN_GLOBAL_VARIABLE;
8167 }
8168
8169 // True if multiple characters are allowed after the declaration of the
8170 // global variable. Not true when it starts with "$-".
8171 bool allow_multiple = true;
8172
8173 switch (*parser->current.end) {
8174 case '~': // $~: match-data
8175 case '*': // $*: argv
8176 case '$': // $$: pid
8177 case '?': // $?: last status
8178 case '!': // $!: error string
8179 case '@': // $@: error position
8180 case '/': // $/: input record separator
8181 case '\\': // $\: output record separator
8182 case ';': // $;: field separator
8183 case ',': // $,: output field separator
8184 case '.': // $.: last read line number
8185 case '=': // $=: ignorecase
8186 case ':': // $:: load path
8187 case '<': // $<: reading filename
8188 case '>': // $>: default output handle
8189 case '\"': // $": already loaded files
8190 parser->current.end++;
8191 return PM_TOKEN_GLOBAL_VARIABLE;
8192
8193 case '&': // $&: last match
8194 case '`': // $`: string before last match
8195 case '\'': // $': string after last match
8196 case '+': // $+: string matches last paren.
8197 parser->current.end++;
8198 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_BACK_REFERENCE;
8199
8200 case '0': {
8201 parser->current.end++;
8202 size_t width;
8203
8204 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8205 do {
8206 parser->current.end += width;
8207 } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8208
8209 // $0 isn't allowed to be followed by anything.
8210 pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL;
8211 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id);
8212 }
8213
8214 return PM_TOKEN_GLOBAL_VARIABLE;
8215 }
8216
8217 case '1':
8218 case '2':
8219 case '3':
8220 case '4':
8221 case '5':
8222 case '6':
8223 case '7':
8224 case '8':
8225 case '9':
8226 parser->current.end += pm_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end);
8227 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_NUMBERED_REFERENCE;
8228
8229 case '-':
8230 parser->current.end++;
8231 allow_multiple = false;
8233 default: {
8234 size_t width;
8235
8236 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8237 do {
8238 parser->current.end += width;
8239 } while (allow_multiple && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8240 } else if (pm_char_is_whitespace(peek(parser))) {
8241 // If we get here, then we have a $ followed by whitespace,
8242 // which is not allowed.
8243 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8244 } else {
8245 // If we get here, then we have a $ followed by something that
8246 // isn't recognized as a global variable.
8247 pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL;
8248 const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
8249 PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - parser->current.start), (const char *) parser->current.start);
8250 }
8251
8252 return PM_TOKEN_GLOBAL_VARIABLE;
8253 }
8254 }
8255}
8256
8269static inline pm_token_type_t
8270lex_keyword(pm_parser_t *parser, const uint8_t *current_start, const char *value, size_t vlen, pm_lex_state_t state, pm_token_type_t type, pm_token_type_t modifier_type) {
8271 if (memcmp(current_start, value, vlen) == 0) {
8272 pm_lex_state_t last_state = parser->lex_state;
8273
8274 if (parser->lex_state & PM_LEX_STATE_FNAME) {
8275 lex_state_set(parser, PM_LEX_STATE_ENDFN);
8276 } else {
8277 lex_state_set(parser, state);
8278 if (state == PM_LEX_STATE_BEG) {
8279 parser->command_start = true;
8280 }
8281
8282 if ((modifier_type != PM_TOKEN_EOF) && !(last_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
8283 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
8284 return modifier_type;
8285 }
8286 }
8287
8288 return type;
8289 }
8290
8291 return PM_TOKEN_EOF;
8292}
8293
8294static pm_token_type_t
8295lex_identifier(pm_parser_t *parser, bool previous_command_start) {
8296 // Lex as far as we can into the current identifier.
8297 size_t width;
8298 const uint8_t *end = parser->end;
8299 const uint8_t *current_start = parser->current.start;
8300 const uint8_t *current_end = parser->current.end;
8301 bool encoding_changed = parser->encoding_changed;
8302
8303 if (encoding_changed) {
8304 while ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) {
8305 current_end += width;
8306 }
8307 } else {
8308 while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) {
8309 current_end += width;
8310 }
8311 }
8312 parser->current.end = current_end;
8313
8314 // Now cache the length of the identifier so that we can quickly compare it
8315 // against known keywords.
8316 width = (size_t) (current_end - current_start);
8317
8318 if (current_end < end) {
8319 if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
8320 // First we'll attempt to extend the identifier by a ! or ?. Then we'll
8321 // check if we're returning the defined? keyword or just an identifier.
8322 width++;
8323
8324 if (
8325 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8326 (peek(parser) == ':') && (peek_offset(parser, 1) != ':')
8327 ) {
8328 // If we're in a position where we can accept a : at the end of an
8329 // identifier, then we'll optionally accept it.
8330 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8331 (void) match(parser, ':');
8332 return PM_TOKEN_LABEL;
8333 }
8334
8335 if (parser->lex_state != PM_LEX_STATE_DOT) {
8336 if (width == 8 && (lex_keyword(parser, current_start, "defined?", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF) != PM_TOKEN_EOF)) {
8337 return PM_TOKEN_KEYWORD_DEFINED;
8338 }
8339 }
8340
8341 return PM_TOKEN_METHOD_NAME;
8342 }
8343
8344 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, '=')) {
8345 // If we're in a position where we can accept a = at the end of an
8346 // identifier, then we'll optionally accept it.
8347 return PM_TOKEN_IDENTIFIER;
8348 }
8349
8350 if (
8351 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8352 peek(parser) == ':' && peek_offset(parser, 1) != ':'
8353 ) {
8354 // If we're in a position where we can accept a : at the end of an
8355 // identifier, then we'll optionally accept it.
8356 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8357 (void) match(parser, ':');
8358 return PM_TOKEN_LABEL;
8359 }
8360 }
8361
8362 if (parser->lex_state != PM_LEX_STATE_DOT) {
8363 pm_token_type_t type;
8364 switch (width) {
8365 case 2:
8366 if (lex_keyword(parser, current_start, "do", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF) != PM_TOKEN_EOF) {
8367 if (pm_do_loop_stack_p(parser)) {
8368 return PM_TOKEN_KEYWORD_DO_LOOP;
8369 }
8370 return PM_TOKEN_KEYWORD_DO;
8371 }
8372
8373 if ((type = lex_keyword(parser, current_start, "if", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER)) != PM_TOKEN_EOF) return type;
8374 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;
8375 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;
8376 break;
8377 case 3:
8378 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;
8379 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;
8380 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;
8381 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;
8382 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;
8383 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;
8384 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;
8385 break;
8386 case 4:
8387 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;
8388 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;
8389 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;
8390 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;
8391 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;
8392 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;
8393 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;
8394 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;
8395 break;
8396 case 5:
8397 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;
8398 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;
8399 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;
8400 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;
8401 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;
8402 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;
8403 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;
8404 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;
8405 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;
8406 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;
8407 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;
8408 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;
8409 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;
8410 break;
8411 case 6:
8412 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;
8413 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;
8414 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;
8415 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;
8416 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;
8417 break;
8418 case 8:
8419 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;
8420 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;
8421 break;
8422 case 12:
8423 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;
8424 break;
8425 }
8426 }
8427
8428 if (encoding_changed) {
8429 return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8430 }
8431 return pm_encoding_utf_8_isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8432}
8433
8438static bool
8439current_token_starts_line(pm_parser_t *parser) {
8440 return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n');
8441}
8442
8457static pm_token_type_t
8458lex_interpolation(pm_parser_t *parser, const uint8_t *pound) {
8459 // If there is no content following this #, then we're at the end of
8460 // the string and we can safely return string content.
8461 if (pound + 1 >= parser->end) {
8462 parser->current.end = pound + 1;
8463 return PM_TOKEN_STRING_CONTENT;
8464 }
8465
8466 // Now we'll check against the character that follows the #. If it constitutes
8467 // valid interplation, we'll handle that, otherwise we'll return
8468 // PM_TOKEN_NOT_PROVIDED.
8469 switch (pound[1]) {
8470 case '@': {
8471 // In this case we may have hit an embedded instance or class variable.
8472 if (pound + 2 >= parser->end) {
8473 parser->current.end = pound + 1;
8474 return PM_TOKEN_STRING_CONTENT;
8475 }
8476
8477 // If we're looking at a @ and there's another @, then we'll skip past the
8478 // second @.
8479 const uint8_t *variable = pound + 2;
8480 if (*variable == '@' && pound + 3 < parser->end) variable++;
8481
8482 if (char_is_identifier_start(parser, variable, parser->end - variable)) {
8483 // At this point we're sure that we've either hit an embedded instance
8484 // or class variable. In this case we'll first need to check if we've
8485 // already consumed content.
8486 if (pound > parser->current.start) {
8487 parser->current.end = pound;
8488 return PM_TOKEN_STRING_CONTENT;
8489 }
8490
8491 // Otherwise we need to return the embedded variable token
8492 // and then switch to the embedded variable lex mode.
8493 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8494 parser->current.end = pound + 1;
8495 return PM_TOKEN_EMBVAR;
8496 }
8497
8498 // If we didn't get a valid interpolation, then this is just regular
8499 // string content. This is like if we get "#@-". In this case the caller
8500 // should keep lexing.
8501 parser->current.end = pound + 1;
8502 return PM_TOKEN_NOT_PROVIDED;
8503 }
8504 case '$':
8505 // In this case we may have hit an embedded global variable. If there's
8506 // not enough room, then we'll just return string content.
8507 if (pound + 2 >= parser->end) {
8508 parser->current.end = pound + 1;
8509 return PM_TOKEN_STRING_CONTENT;
8510 }
8511
8512 // This is the character that we're going to check to see if it is the
8513 // start of an identifier that would indicate that this is a global
8514 // variable.
8515 const uint8_t *check = pound + 2;
8516
8517 if (pound[2] == '-') {
8518 if (pound + 3 >= parser->end) {
8519 parser->current.end = pound + 2;
8520 return PM_TOKEN_STRING_CONTENT;
8521 }
8522
8523 check++;
8524 }
8525
8526 // If the character that we're going to check is the start of an
8527 // identifier, or we don't have a - and the character is a decimal number
8528 // or a global name punctuation character, then we've hit an embedded
8529 // global variable.
8530 if (
8531 char_is_identifier_start(parser, check, parser->end - check) ||
8532 (pound[2] != '-' && (pm_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2])))
8533 ) {
8534 // In this case we've hit an embedded global variable. First check to
8535 // see if we've already consumed content. If we have, then we need to
8536 // return that content as string content first.
8537 if (pound > parser->current.start) {
8538 parser->current.end = pound;
8539 return PM_TOKEN_STRING_CONTENT;
8540 }
8541
8542 // Otherwise, we need to return the embedded variable token and switch
8543 // to the embedded variable lex mode.
8544 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8545 parser->current.end = pound + 1;
8546 return PM_TOKEN_EMBVAR;
8547 }
8548
8549 // In this case we've hit a #$ that does not indicate a global variable.
8550 // In this case we'll continue lexing past it.
8551 parser->current.end = pound + 1;
8552 return PM_TOKEN_NOT_PROVIDED;
8553 case '{':
8554 // In this case it's the start of an embedded expression. If we have
8555 // already consumed content, then we need to return that content as string
8556 // content first.
8557 if (pound > parser->current.start) {
8558 parser->current.end = pound;
8559 return PM_TOKEN_STRING_CONTENT;
8560 }
8561
8562 parser->enclosure_nesting++;
8563
8564 // Otherwise we'll skip past the #{ and begin lexing the embedded
8565 // expression.
8566 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBEXPR });
8567 parser->current.end = pound + 2;
8568 parser->command_start = true;
8569 pm_do_loop_stack_push(parser, false);
8570 return PM_TOKEN_EMBEXPR_BEGIN;
8571 default:
8572 // In this case we've hit a # that doesn't constitute interpolation. We'll
8573 // mark that by returning the not provided token type. This tells the
8574 // consumer to keep lexing forward.
8575 parser->current.end = pound + 1;
8576 return PM_TOKEN_NOT_PROVIDED;
8577 }
8578}
8579
8580static const uint8_t PM_ESCAPE_FLAG_NONE = 0x0;
8581static const uint8_t PM_ESCAPE_FLAG_CONTROL = 0x1;
8582static const uint8_t PM_ESCAPE_FLAG_META = 0x2;
8583static const uint8_t PM_ESCAPE_FLAG_SINGLE = 0x4;
8584static const uint8_t PM_ESCAPE_FLAG_REGEXP = 0x8;
8585
8589static const bool ascii_printable_chars[] = {
8590 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
8591 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8592 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8593 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8594 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8595 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
8596 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8597 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
8598};
8599
8600static inline bool
8601char_is_ascii_printable(const uint8_t b) {
8602 return (b < 0x80) && ascii_printable_chars[b];
8603}
8604
8609static inline uint8_t
8610escape_hexadecimal_digit(const uint8_t value) {
8611 return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9);
8612}
8613
8619static inline uint32_t
8620escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location) {
8621 uint32_t value = 0;
8622 for (size_t index = 0; index < length; index++) {
8623 if (index != 0) value <<= 4;
8624 value |= escape_hexadecimal_digit(string[index]);
8625 }
8626
8627 // Here we're going to verify that the value is actually a valid Unicode
8628 // codepoint and not a surrogate pair.
8629 if (value >= 0xD800 && value <= 0xDFFF) {
8630 if (error_location != NULL) {
8631 pm_parser_err(parser, error_location->start, error_location->end, PM_ERR_ESCAPE_INVALID_UNICODE);
8632 } else {
8633 pm_parser_err(parser, string, string + length, PM_ERR_ESCAPE_INVALID_UNICODE);
8634 }
8635 return 0xFFFD;
8636 }
8637
8638 return value;
8639}
8640
8644static inline uint8_t
8645escape_byte(uint8_t value, const uint8_t flags) {
8646 if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x9f;
8647 if (flags & PM_ESCAPE_FLAG_META) value |= 0x80;
8648 return value;
8649}
8650
8654static inline void
8655escape_write_unicode(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t flags, const uint8_t *start, const uint8_t *end, uint32_t value) {
8656 // \u escape sequences in string-like structures implicitly change the
8657 // encoding to UTF-8 if they are >= 0x80 or if they are used in a character
8658 // literal.
8659 if (value >= 0x80 || flags & PM_ESCAPE_FLAG_SINGLE) {
8660 if (parser->explicit_encoding != NULL && parser->explicit_encoding != PM_ENCODING_UTF_8_ENTRY) {
8661 PM_PARSER_ERR_FORMAT(parser, start, end, PM_ERR_MIXED_ENCODING, parser->explicit_encoding->name);
8662 }
8663
8665 }
8666
8667 if (!pm_buffer_append_unicode_codepoint(buffer, value)) {
8668 pm_parser_err(parser, start, end, PM_ERR_ESCAPE_INVALID_UNICODE);
8669 pm_buffer_append_byte(buffer, 0xEF);
8670 pm_buffer_append_byte(buffer, 0xBF);
8671 pm_buffer_append_byte(buffer, 0xBD);
8672 }
8673}
8674
8679static inline void
8680escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, uint8_t byte) {
8681 if (byte >= 0x80) {
8682 if (parser->explicit_encoding != NULL && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
8683 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_MIXED_ENCODING, parser->encoding->name);
8684 }
8685
8686 parser->explicit_encoding = parser->encoding;
8687 }
8688
8689 pm_buffer_append_byte(buffer, byte);
8690}
8691
8707static inline void
8708escape_write_byte(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags, uint8_t byte) {
8709 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8710 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", byte);
8711 }
8712
8713 escape_write_byte_encoded(parser, buffer, byte);
8714}
8715
8719static inline void
8720escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
8721 size_t width;
8722 if (parser->encoding_changed) {
8723 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
8724 } else {
8725 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
8726 }
8727
8728 if (width == 1) {
8729 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(*parser->current.end++, flags));
8730 } else if (width > 1) {
8731 // Valid multibyte character. Just ignore escape.
8732 pm_buffer_t *b = (flags & PM_ESCAPE_FLAG_REGEXP) ? regular_expression_buffer : buffer;
8733 pm_buffer_append_bytes(b, parser->current.end, width);
8734 parser->current.end += width;
8735 } else {
8736 // Assume the next character wasn't meant to be part of this escape
8737 // sequence since it is invalid. Add an error and move on.
8738 parser->current.end++;
8739 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
8740 }
8741}
8742
8748static void
8749escape_read_warn(pm_parser_t *parser, uint8_t flags, uint8_t flag, const char *type) {
8750#define FLAG(value) ((value & PM_ESCAPE_FLAG_CONTROL) ? "\\C-" : (value & PM_ESCAPE_FLAG_META) ? "\\M-" : "")
8751
8752 PM_PARSER_WARN_TOKEN_FORMAT(
8753 parser,
8754 parser->current,
8755 PM_WARN_INVALID_CHARACTER,
8756 FLAG(flags),
8757 FLAG(flag),
8758 type
8759 );
8760
8761#undef FLAG
8762}
8763
8767static void
8768escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
8769 uint8_t peeked = peek(parser);
8770 switch (peeked) {
8771 case '\\': {
8772 parser->current.end++;
8773 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\\', flags));
8774 return;
8775 }
8776 case '\'': {
8777 parser->current.end++;
8778 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\'', flags));
8779 return;
8780 }
8781 case 'a': {
8782 parser->current.end++;
8783 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\a', flags));
8784 return;
8785 }
8786 case 'b': {
8787 parser->current.end++;
8788 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\b', flags));
8789 return;
8790 }
8791 case 'e': {
8792 parser->current.end++;
8793 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\033', flags));
8794 return;
8795 }
8796 case 'f': {
8797 parser->current.end++;
8798 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\f', flags));
8799 return;
8800 }
8801 case 'n': {
8802 parser->current.end++;
8803 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\n', flags));
8804 return;
8805 }
8806 case 'r': {
8807 parser->current.end++;
8808 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\r', flags));
8809 return;
8810 }
8811 case 's': {
8812 parser->current.end++;
8813 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(' ', flags));
8814 return;
8815 }
8816 case 't': {
8817 parser->current.end++;
8818 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\t', flags));
8819 return;
8820 }
8821 case 'v': {
8822 parser->current.end++;
8823 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\v', flags));
8824 return;
8825 }
8826 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': {
8827 uint8_t value = (uint8_t) (*parser->current.end - '0');
8828 parser->current.end++;
8829
8830 if (pm_char_is_octal_digit(peek(parser))) {
8831 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
8832 parser->current.end++;
8833
8834 if (pm_char_is_octal_digit(peek(parser))) {
8835 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
8836 parser->current.end++;
8837 }
8838 }
8839
8840 value = escape_byte(value, flags);
8841 escape_write_byte(parser, buffer, regular_expression_buffer, flags, value);
8842 return;
8843 }
8844 case 'x': {
8845 const uint8_t *start = parser->current.end - 1;
8846
8847 parser->current.end++;
8848 uint8_t byte = peek(parser);
8849
8850 if (pm_char_is_hexadecimal_digit(byte)) {
8851 uint8_t value = escape_hexadecimal_digit(byte);
8852 parser->current.end++;
8853
8854 byte = peek(parser);
8855 if (pm_char_is_hexadecimal_digit(byte)) {
8856 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(byte));
8857 parser->current.end++;
8858 }
8859
8860 value = escape_byte(value, flags);
8861 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8862 if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) {
8863 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", value);
8864 } else {
8865 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
8866 }
8867 }
8868
8869 escape_write_byte_encoded(parser, buffer, value);
8870 } else {
8871 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL);
8872 }
8873
8874 return;
8875 }
8876 case 'u': {
8877 const uint8_t *start = parser->current.end - 1;
8878 parser->current.end++;
8879
8880 if (parser->current.end == parser->end) {
8881 const uint8_t *start = parser->current.end - 2;
8882 PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
8883 } else if (peek(parser) == '{') {
8884 const uint8_t *unicode_codepoints_start = parser->current.end - 2;
8885 parser->current.end++;
8886
8887 size_t whitespace;
8888 while (true) {
8889 if ((whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end)) > 0) {
8890 parser->current.end += whitespace;
8891 } else if (peek(parser) == '\\' && peek_offset(parser, 1) == 'n') {
8892 // This is super hacky, but it gets us nicer error
8893 // messages because we can still pass it off to the
8894 // regular expression engine even if we hit an
8895 // unterminated regular expression.
8896 parser->current.end += 2;
8897 } else {
8898 break;
8899 }
8900 }
8901
8902 const uint8_t *extra_codepoints_start = NULL;
8903 int codepoints_count = 0;
8904
8905 while ((parser->current.end < parser->end) && (*parser->current.end != '}')) {
8906 const uint8_t *unicode_start = parser->current.end;
8907 size_t hexadecimal_length = pm_strspn_hexadecimal_digit(parser->current.end, parser->end - parser->current.end);
8908
8909 if (hexadecimal_length > 6) {
8910 // \u{nnnn} character literal allows only 1-6 hexadecimal digits
8911 pm_parser_err(parser, unicode_start, unicode_start + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE_LONG);
8912 } else if (hexadecimal_length == 0) {
8913 // there are not hexadecimal characters
8914
8915 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8916 // If this is a regular expression, we are going to
8917 // let the regular expression engine handle this
8918 // error instead of us.
8919 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
8920 } else {
8921 pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE);
8922 pm_parser_err(parser, parser->current.end, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
8923 }
8924
8925 return;
8926 }
8927
8928 parser->current.end += hexadecimal_length;
8929 codepoints_count++;
8930 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count == 2) {
8931 extra_codepoints_start = unicode_start;
8932 }
8933
8934 uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL);
8935 escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value);
8936
8937 parser->current.end += pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
8938 }
8939
8940 // ?\u{nnnn} character literal should contain only one codepoint
8941 // and cannot be like ?\u{nnnn mmmm}.
8942 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count > 1) {
8943 pm_parser_err(parser, extra_codepoints_start, parser->current.end - 1, PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL);
8944 }
8945
8946 if (parser->current.end == parser->end) {
8947 PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_LIST, (int) (parser->current.end - start), start);
8948 } else if (peek(parser) == '}') {
8949 parser->current.end++;
8950 } else {
8951 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8952 // If this is a regular expression, we are going to let
8953 // the regular expression engine handle this error
8954 // instead of us.
8955 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
8956 } else {
8957 pm_parser_err(parser, unicode_codepoints_start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
8958 }
8959 }
8960
8961 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8962 pm_buffer_append_bytes(regular_expression_buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start));
8963 }
8964 } else {
8965 size_t length = pm_strspn_hexadecimal_digit(parser->current.end, MIN(parser->end - parser->current.end, 4));
8966
8967 if (length == 0) {
8968 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8969 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
8970 } else {
8971 const uint8_t *start = parser->current.end - 2;
8972 PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
8973 }
8974 } else if (length == 4) {
8975 uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL);
8976
8977 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8978 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start));
8979 }
8980
8981 escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value);
8982 parser->current.end += 4;
8983 } else {
8984 parser->current.end += length;
8985
8986 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8987 // If this is a regular expression, we are going to let
8988 // the regular expression engine handle this error
8989 // instead of us.
8990 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
8991 } else {
8992 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE);
8993 }
8994 }
8995 }
8996
8997 return;
8998 }
8999 case 'c': {
9000 parser->current.end++;
9001 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9002 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9003 }
9004
9005 if (parser->current.end == parser->end) {
9006 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9007 return;
9008 }
9009
9010 uint8_t peeked = peek(parser);
9011 switch (peeked) {
9012 case '?': {
9013 parser->current.end++;
9014 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9015 return;
9016 }
9017 case '\\':
9018 parser->current.end++;
9019
9020 if (match(parser, 'u') || match(parser, 'U')) {
9021 pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER);
9022 return;
9023 }
9024
9025 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9026 return;
9027 case ' ':
9028 parser->current.end++;
9029 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9030 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9031 return;
9032 case '\t':
9033 parser->current.end++;
9034 escape_read_warn(parser, flags, 0, "\\t");
9035 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9036 return;
9037 default: {
9038 if (!char_is_ascii_printable(peeked)) {
9039 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9040 return;
9041 }
9042
9043 parser->current.end++;
9044 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9045 return;
9046 }
9047 }
9048 }
9049 case 'C': {
9050 parser->current.end++;
9051 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9052 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9053 }
9054
9055 if (peek(parser) != '-') {
9056 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9057 pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_CONTROL);
9058 return;
9059 }
9060
9061 parser->current.end++;
9062 if (parser->current.end == parser->end) {
9063 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9064 return;
9065 }
9066
9067 uint8_t peeked = peek(parser);
9068 switch (peeked) {
9069 case '?': {
9070 parser->current.end++;
9071 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9072 return;
9073 }
9074 case '\\':
9075 parser->current.end++;
9076
9077 if (match(parser, 'u') || match(parser, 'U')) {
9078 pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER);
9079 return;
9080 }
9081
9082 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9083 return;
9084 case ' ':
9085 parser->current.end++;
9086 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9087 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9088 return;
9089 case '\t':
9090 parser->current.end++;
9091 escape_read_warn(parser, flags, 0, "\\t");
9092 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9093 return;
9094 default: {
9095 if (!char_is_ascii_printable(peeked)) {
9096 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9097 pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_CONTROL);
9098 return;
9099 }
9100
9101 parser->current.end++;
9102 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9103 return;
9104 }
9105 }
9106 }
9107 case 'M': {
9108 parser->current.end++;
9109 if (flags & PM_ESCAPE_FLAG_META) {
9110 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
9111 }
9112
9113 if (peek(parser) != '-') {
9114 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9115 pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META);
9116 return;
9117 }
9118
9119 parser->current.end++;
9120 if (parser->current.end == parser->end) {
9121 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META);
9122 return;
9123 }
9124
9125 uint8_t peeked = peek(parser);
9126 switch (peeked) {
9127 case '\\':
9128 parser->current.end++;
9129
9130 if (match(parser, 'u') || match(parser, 'U')) {
9131 pm_parser_err(parser, parser->current.start, parser->current.end, PM_ERR_INVALID_ESCAPE_CHARACTER);
9132 return;
9133 }
9134
9135 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_META);
9136 return;
9137 case ' ':
9138 parser->current.end++;
9139 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_META, "\\s");
9140 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9141 return;
9142 case '\t':
9143 parser->current.end++;
9144 escape_read_warn(parser, flags & ((uint8_t) ~PM_ESCAPE_FLAG_CONTROL), PM_ESCAPE_FLAG_META, "\\t");
9145 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9146 return;
9147 default:
9148 if (!char_is_ascii_printable(peeked)) {
9149 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9150 pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META);
9151 return;
9152 }
9153
9154 parser->current.end++;
9155 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9156 return;
9157 }
9158 }
9159 case '\r': {
9160 if (peek_offset(parser, 1) == '\n') {
9161 parser->current.end += 2;
9162 escape_write_byte_encoded(parser, buffer, escape_byte('\n', flags));
9163 return;
9164 }
9166 }
9167 default: {
9168 if ((flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) && !char_is_ascii_printable(peeked)) {
9169 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9170 pm_parser_err(parser, parser->current.start, parser->current.end + width, PM_ERR_ESCAPE_INVALID_META);
9171 return;
9172 }
9173 if (parser->current.end < parser->end) {
9174 escape_write_escape_encoded(parser, buffer, regular_expression_buffer, flags);
9175 } else {
9176 pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER);
9177 }
9178 return;
9179 }
9180 }
9181}
9182
9208static pm_token_type_t
9209lex_question_mark(pm_parser_t *parser) {
9210 if (lex_state_end_p(parser)) {
9211 lex_state_set(parser, PM_LEX_STATE_BEG);
9212 return PM_TOKEN_QUESTION_MARK;
9213 }
9214
9215 if (parser->current.end >= parser->end) {
9216 pm_parser_err_current(parser, PM_ERR_INCOMPLETE_QUESTION_MARK);
9217 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9218 return PM_TOKEN_CHARACTER_LITERAL;
9219 }
9220
9221 if (pm_char_is_whitespace(*parser->current.end)) {
9222 lex_state_set(parser, PM_LEX_STATE_BEG);
9223 return PM_TOKEN_QUESTION_MARK;
9224 }
9225
9226 lex_state_set(parser, PM_LEX_STATE_BEG);
9227
9228 if (match(parser, '\\')) {
9229 lex_state_set(parser, PM_LEX_STATE_END);
9230
9231 pm_buffer_t buffer;
9232 pm_buffer_init_capacity(&buffer, 3);
9233
9234 escape_read(parser, &buffer, NULL, PM_ESCAPE_FLAG_SINGLE);
9235 pm_string_owned_init(&parser->current_string, (uint8_t *) buffer.value, buffer.length);
9236
9237 return PM_TOKEN_CHARACTER_LITERAL;
9238 } else {
9239 size_t encoding_width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9240
9241 // Ternary operators can have a ? immediately followed by an identifier
9242 // which starts with an underscore. We check for this case here.
9243 if (
9244 !(parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end) || peek(parser) == '_') ||
9245 (
9246 (parser->current.end + encoding_width >= parser->end) ||
9247 !char_is_identifier(parser, parser->current.end + encoding_width, parser->end - (parser->current.end + encoding_width))
9248 )
9249 ) {
9250 lex_state_set(parser, PM_LEX_STATE_END);
9251 parser->current.end += encoding_width;
9252 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9253 return PM_TOKEN_CHARACTER_LITERAL;
9254 }
9255 }
9256
9257 return PM_TOKEN_QUESTION_MARK;
9258}
9259
9264static pm_token_type_t
9265lex_at_variable(pm_parser_t *parser) {
9266 pm_token_type_t type = match(parser, '@') ? PM_TOKEN_CLASS_VARIABLE : PM_TOKEN_INSTANCE_VARIABLE;
9267 const uint8_t *end = parser->end;
9268
9269 size_t width;
9270 if ((width = char_is_identifier_start(parser, parser->current.end, end - parser->current.end)) > 0) {
9271 parser->current.end += width;
9272
9273 while ((width = char_is_identifier(parser, parser->current.end, end - parser->current.end)) > 0) {
9274 parser->current.end += width;
9275 }
9276 } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) {
9277 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
9278 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) {
9279 diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3;
9280 }
9281
9282 size_t width = parser->encoding->char_width(parser->current.end, end - parser->current.end);
9283 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start);
9284 } else {
9285 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_CLASS_VARIABLE_BARE : PM_ERR_INSTANCE_VARIABLE_BARE;
9286 pm_parser_err_token(parser, &parser->current, diag_id);
9287 }
9288
9289 // If we're lexing an embedded variable, then we need to pop back into the
9290 // parent lex context.
9291 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
9292 lex_mode_pop(parser);
9293 }
9294
9295 return type;
9296}
9297
9301static inline void
9302parser_lex_callback(pm_parser_t *parser) {
9303 if (parser->lex_callback) {
9304 parser->lex_callback->callback(parser->lex_callback->data, parser, &parser->current);
9305 }
9306}
9307
9311static inline pm_comment_t *
9312parser_comment(pm_parser_t *parser, pm_comment_type_t type) {
9313 pm_comment_t *comment = (pm_comment_t *) xcalloc(1, sizeof(pm_comment_t));
9314 if (comment == NULL) return NULL;
9315
9316 *comment = (pm_comment_t) {
9317 .type = type,
9318 .location = { parser->current.start, parser->current.end }
9319 };
9320
9321 return comment;
9322}
9323
9329static pm_token_type_t
9330lex_embdoc(pm_parser_t *parser) {
9331 // First, lex out the EMBDOC_BEGIN token.
9332 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9333
9334 if (newline == NULL) {
9335 parser->current.end = parser->end;
9336 } else {
9337 pm_newline_list_append(&parser->newline_list, newline);
9338 parser->current.end = newline + 1;
9339 }
9340
9341 parser->current.type = PM_TOKEN_EMBDOC_BEGIN;
9342 parser_lex_callback(parser);
9343
9344 // Now, create a comment that is going to be attached to the parser.
9345 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC);
9346 if (comment == NULL) return PM_TOKEN_EOF;
9347
9348 // Now, loop until we find the end of the embedded documentation or the end
9349 // of the file.
9350 while (parser->current.end + 4 <= parser->end) {
9351 parser->current.start = parser->current.end;
9352
9353 // If we've hit the end of the embedded documentation then we'll return
9354 // that token here.
9355 if (
9356 (memcmp(parser->current.end, "=end", 4) == 0) &&
9357 (
9358 (parser->current.end + 4 == parser->end) || // end of file
9359 pm_char_is_whitespace(parser->current.end[4]) || // whitespace
9360 (parser->current.end[4] == '\0') || // NUL or end of script
9361 (parser->current.end[4] == '\004') || // ^D
9362 (parser->current.end[4] == '\032') // ^Z
9363 )
9364 ) {
9365 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9366
9367 if (newline == NULL) {
9368 parser->current.end = parser->end;
9369 } else {
9370 pm_newline_list_append(&parser->newline_list, newline);
9371 parser->current.end = newline + 1;
9372 }
9373
9374 parser->current.type = PM_TOKEN_EMBDOC_END;
9375 parser_lex_callback(parser);
9376
9377 comment->location.end = parser->current.end;
9378 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9379
9380 return PM_TOKEN_EMBDOC_END;
9381 }
9382
9383 // Otherwise, we'll parse until the end of the line and return a line of
9384 // embedded documentation.
9385 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9386
9387 if (newline == NULL) {
9388 parser->current.end = parser->end;
9389 } else {
9390 pm_newline_list_append(&parser->newline_list, newline);
9391 parser->current.end = newline + 1;
9392 }
9393
9394 parser->current.type = PM_TOKEN_EMBDOC_LINE;
9395 parser_lex_callback(parser);
9396 }
9397
9398 pm_parser_err_current(parser, PM_ERR_EMBDOC_TERM);
9399
9400 comment->location.end = parser->current.end;
9401 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9402
9403 return PM_TOKEN_EOF;
9404}
9405
9411static inline void
9412parser_lex_ignored_newline(pm_parser_t *parser) {
9413 parser->current.type = PM_TOKEN_IGNORED_NEWLINE;
9414 parser_lex_callback(parser);
9415}
9416
9426static inline void
9427parser_flush_heredoc_end(pm_parser_t *parser) {
9428 assert(parser->heredoc_end <= parser->end);
9429 parser->next_start = parser->heredoc_end;
9430 parser->heredoc_end = NULL;
9431}
9432
9436static bool
9437parser_end_of_line_p(const pm_parser_t *parser) {
9438 const uint8_t *cursor = parser->current.end;
9439
9440 while (cursor < parser->end && *cursor != '\n' && *cursor != '#') {
9441 if (!pm_char_is_inline_whitespace(*cursor++)) return false;
9442 }
9443
9444 return true;
9445}
9446
9465typedef struct {
9471
9476 const uint8_t *cursor;
9478
9498
9502static inline void
9503pm_token_buffer_push_byte(pm_token_buffer_t *token_buffer, uint8_t byte) {
9504 pm_buffer_append_byte(&token_buffer->buffer, byte);
9505}
9506
9507static inline void
9508pm_regexp_token_buffer_push_byte(pm_regexp_token_buffer_t *token_buffer, uint8_t byte) {
9509 pm_buffer_append_byte(&token_buffer->regexp_buffer, byte);
9510}
9511
9515static inline size_t
9516parser_char_width(const pm_parser_t *parser) {
9517 size_t width;
9518 if (parser->encoding_changed) {
9519 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9520 } else {
9521 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9522 }
9523
9524 // TODO: If the character is invalid in the given encoding, then we'll just
9525 // push one byte into the buffer. This should actually be an error.
9526 return (width == 0 ? 1 : width);
9527}
9528
9532static void
9533pm_token_buffer_push_escaped(pm_token_buffer_t *token_buffer, pm_parser_t *parser) {
9534 size_t width = parser_char_width(parser);
9535 pm_buffer_append_bytes(&token_buffer->buffer, parser->current.end, width);
9536 parser->current.end += width;
9537}
9538
9539static void
9540pm_regexp_token_buffer_push_escaped(pm_regexp_token_buffer_t *token_buffer, pm_parser_t *parser) {
9541 size_t width = parser_char_width(parser);
9542 pm_buffer_append_bytes(&token_buffer->base.buffer, parser->current.end, width);
9543 pm_buffer_append_bytes(&token_buffer->regexp_buffer, parser->current.end, width);
9544 parser->current.end += width;
9545}
9546
9547static bool
9548pm_slice_ascii_only_p(const uint8_t *value, size_t length) {
9549 for (size_t index = 0; index < length; index++) {
9550 if (value[index] & 0x80) return false;
9551 }
9552
9553 return true;
9554}
9555
9562static inline void
9563pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9564 pm_string_owned_init(&parser->current_string, (uint8_t *) pm_buffer_value(&token_buffer->buffer), pm_buffer_length(&token_buffer->buffer));
9565}
9566
9567static inline void
9568pm_regexp_token_buffer_copy(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9569 pm_string_owned_init(&parser->current_string, (uint8_t *) pm_buffer_value(&token_buffer->base.buffer), pm_buffer_length(&token_buffer->base.buffer));
9570 parser->current_regular_expression_ascii_only = pm_slice_ascii_only_p((const uint8_t *) pm_buffer_value(&token_buffer->regexp_buffer), pm_buffer_length(&token_buffer->regexp_buffer));
9571 pm_buffer_free(&token_buffer->regexp_buffer);
9572}
9573
9583static void
9584pm_token_buffer_flush(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9585 if (token_buffer->cursor == NULL) {
9586 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9587 } else {
9588 pm_buffer_append_bytes(&token_buffer->buffer, token_buffer->cursor, (size_t) (parser->current.end - token_buffer->cursor));
9589 pm_token_buffer_copy(parser, token_buffer);
9590 }
9591}
9592
9593static void
9594pm_regexp_token_buffer_flush(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9595 if (token_buffer->base.cursor == NULL) {
9596 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9597 parser->current_regular_expression_ascii_only = pm_slice_ascii_only_p(parser->current.start, (size_t) (parser->current.end - parser->current.start));
9598 } else {
9599 pm_buffer_append_bytes(&token_buffer->base.buffer, token_buffer->base.cursor, (size_t) (parser->current.end - token_buffer->base.cursor));
9600 pm_buffer_append_bytes(&token_buffer->regexp_buffer, token_buffer->base.cursor, (size_t) (parser->current.end - token_buffer->base.cursor));
9601 pm_regexp_token_buffer_copy(parser, token_buffer);
9602 }
9603}
9604
9605#define PM_TOKEN_BUFFER_DEFAULT_SIZE 16
9606
9615static void
9616pm_token_buffer_escape(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9617 const uint8_t *start;
9618 if (token_buffer->cursor == NULL) {
9619 pm_buffer_init_capacity(&token_buffer->buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9620 start = parser->current.start;
9621 } else {
9622 start = token_buffer->cursor;
9623 }
9624
9625 const uint8_t *end = parser->current.end - 1;
9626 assert(end >= start);
9627 pm_buffer_append_bytes(&token_buffer->buffer, start, (size_t) (end - start));
9628
9629 token_buffer->cursor = end;
9630}
9631
9632static void
9633pm_regexp_token_buffer_escape(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9634 const uint8_t *start;
9635 if (token_buffer->base.cursor == NULL) {
9636 pm_buffer_init_capacity(&token_buffer->base.buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9637 pm_buffer_init_capacity(&token_buffer->regexp_buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9638 start = parser->current.start;
9639 } else {
9640 start = token_buffer->base.cursor;
9641 }
9642
9643 const uint8_t *end = parser->current.end - 1;
9644 pm_buffer_append_bytes(&token_buffer->base.buffer, start, (size_t) (end - start));
9645 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, (size_t) (end - start));
9646
9647 token_buffer->base.cursor = end;
9648}
9649
9650#undef PM_TOKEN_BUFFER_DEFAULT_SIZE
9651
9656static inline size_t
9657pm_heredoc_strspn_inline_whitespace(pm_parser_t *parser, const uint8_t **cursor, pm_heredoc_indent_t indent) {
9658 size_t whitespace = 0;
9659
9660 switch (indent) {
9661 case PM_HEREDOC_INDENT_NONE:
9662 // Do nothing, we can't match a terminator with
9663 // indentation and there's no need to calculate common
9664 // whitespace.
9665 break;
9666 case PM_HEREDOC_INDENT_DASH:
9667 // Skip past inline whitespace.
9668 *cursor += pm_strspn_inline_whitespace(*cursor, parser->end - *cursor);
9669 break;
9670 case PM_HEREDOC_INDENT_TILDE:
9671 // Skip past inline whitespace and calculate common
9672 // whitespace.
9673 while (*cursor < parser->end && pm_char_is_inline_whitespace(**cursor)) {
9674 if (**cursor == '\t') {
9675 whitespace = (whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
9676 } else {
9677 whitespace++;
9678 }
9679 (*cursor)++;
9680 }
9681
9682 break;
9683 }
9684
9685 return whitespace;
9686}
9687
9692static uint8_t
9693pm_lex_percent_delimiter(pm_parser_t *parser) {
9694 size_t eol_length = match_eol(parser);
9695
9696 if (eol_length) {
9697 if (parser->heredoc_end) {
9698 // If we have already lexed a heredoc, then the newline has already
9699 // been added to the list. In this case we want to just flush the
9700 // heredoc end.
9701 parser_flush_heredoc_end(parser);
9702 } else {
9703 // Otherwise, we'll add the newline to the list of newlines.
9704 pm_newline_list_append(&parser->newline_list, parser->current.end + eol_length - 1);
9705 }
9706
9707 uint8_t delimiter = *parser->current.end;
9708
9709 // If our delimiter is \r\n, we want to treat it as if it's \n.
9710 // For example, %\r\nfoo\r\n should be "foo"
9711 if (eol_length == 2) {
9712 delimiter = *(parser->current.end + 1);
9713 }
9714
9715 parser->current.end += eol_length;
9716 return delimiter;
9717 }
9718
9719 return *parser->current.end++;
9720}
9721
9726#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return
9727
9734static void
9735parser_lex(pm_parser_t *parser) {
9736 assert(parser->current.end <= parser->end);
9737 parser->previous = parser->current;
9738
9739 // This value mirrors cmd_state from CRuby.
9740 bool previous_command_start = parser->command_start;
9741 parser->command_start = false;
9742
9743 // This is used to communicate to the newline lexing function that we've
9744 // already seen a comment.
9745 bool lexed_comment = false;
9746
9747 // Here we cache the current value of the semantic token seen flag. This is
9748 // used to reset it in case we find a token that shouldn't flip this flag.
9749 unsigned int semantic_token_seen = parser->semantic_token_seen;
9750 parser->semantic_token_seen = true;
9751
9752 switch (parser->lex_modes.current->mode) {
9753 case PM_LEX_DEFAULT:
9754 case PM_LEX_EMBEXPR:
9755 case PM_LEX_EMBVAR:
9756
9757 // We have a specific named label here because we are going to jump back to
9758 // this location in the event that we have lexed a token that should not be
9759 // returned to the parser. This includes comments, ignored newlines, and
9760 // invalid tokens of some form.
9761 lex_next_token: {
9762 // If we have the special next_start pointer set, then we're going to jump
9763 // to that location and start lexing from there.
9764 if (parser->next_start != NULL) {
9765 parser->current.end = parser->next_start;
9766 parser->next_start = NULL;
9767 }
9768
9769 // This value mirrors space_seen from CRuby. It tracks whether or not
9770 // space has been eaten before the start of the next token.
9771 bool space_seen = false;
9772
9773 // First, we're going to skip past any whitespace at the front of the next
9774 // token.
9775 bool chomping = true;
9776 while (parser->current.end < parser->end && chomping) {
9777 switch (*parser->current.end) {
9778 case ' ':
9779 case '\t':
9780 case '\f':
9781 case '\v':
9782 parser->current.end++;
9783 space_seen = true;
9784 break;
9785 case '\r':
9786 if (match_eol_offset(parser, 1)) {
9787 chomping = false;
9788 } else {
9789 pm_parser_warn(parser, parser->current.end, parser->current.end + 1, PM_WARN_UNEXPECTED_CARRIAGE_RETURN);
9790 parser->current.end++;
9791 space_seen = true;
9792 }
9793 break;
9794 case '\\': {
9795 size_t eol_length = match_eol_offset(parser, 1);
9796 if (eol_length) {
9797 if (parser->heredoc_end) {
9798 parser->current.end = parser->heredoc_end;
9799 parser->heredoc_end = NULL;
9800 } else {
9801 parser->current.end += eol_length + 1;
9802 pm_newline_list_append(&parser->newline_list, parser->current.end - 1);
9803 space_seen = true;
9804 }
9805 } else if (pm_char_is_inline_whitespace(*parser->current.end)) {
9806 parser->current.end += 2;
9807 } else {
9808 chomping = false;
9809 }
9810
9811 break;
9812 }
9813 default:
9814 chomping = false;
9815 break;
9816 }
9817 }
9818
9819 // Next, we'll set to start of this token to be the current end.
9820 parser->current.start = parser->current.end;
9821
9822 // We'll check if we're at the end of the file. If we are, then we
9823 // need to return the EOF token.
9824 if (parser->current.end >= parser->end) {
9825 // If we hit EOF, but the EOF came immediately after a newline,
9826 // set the start of the token to the newline. This way any EOF
9827 // errors will be reported as happening on that line rather than
9828 // a line after. For example "foo(\n" should report an error
9829 // on line 1 even though EOF technically occurs on line 2.
9830 if (parser->current.start > parser->start && (*(parser->current.start - 1) == '\n')) {
9831 parser->current.start -= 1;
9832 }
9833 LEX(PM_TOKEN_EOF);
9834 }
9835
9836 // Finally, we'll check the current character to determine the next
9837 // token.
9838 switch (*parser->current.end++) {
9839 case '\0': // NUL or end of script
9840 case '\004': // ^D
9841 case '\032': // ^Z
9842 parser->current.end--;
9843 LEX(PM_TOKEN_EOF);
9844
9845 case '#': { // comments
9846 const uint8_t *ending = next_newline(parser->current.end, parser->end - parser->current.end);
9847 parser->current.end = ending == NULL ? parser->end : ending;
9848
9849 // If we found a comment while lexing, then we're going to
9850 // add it to the list of comments in the file and keep
9851 // lexing.
9852 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE);
9853 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9854
9855 if (ending) parser->current.end++;
9856 parser->current.type = PM_TOKEN_COMMENT;
9857 parser_lex_callback(parser);
9858
9859 // Here, parse the comment to see if it's a magic comment
9860 // and potentially change state on the parser.
9861 if (!parser_lex_magic_comment(parser, semantic_token_seen) && (parser->current.start == parser->encoding_comment_start)) {
9862 ptrdiff_t length = parser->current.end - parser->current.start;
9863
9864 // If we didn't find a magic comment within the first
9865 // pass and we're at the start of the file, then we need
9866 // to do another pass to potentially find other patterns
9867 // for encoding comments.
9868 if (length >= 10 && !parser->encoding_locked) {
9869 parser_lex_magic_comment_encoding(parser);
9870 }
9871 }
9872
9873 lexed_comment = true;
9874 }
9876 case '\r':
9877 case '\n': {
9878 parser->semantic_token_seen = semantic_token_seen & 0x1;
9879 size_t eol_length = match_eol_at(parser, parser->current.end - 1);
9880
9881 if (eol_length) {
9882 // The only way you can have carriage returns in this
9883 // particular loop is if you have a carriage return
9884 // followed by a newline. In that case we'll just skip
9885 // over the carriage return and continue lexing, in
9886 // order to make it so that the newline token
9887 // encapsulates both the carriage return and the
9888 // newline. Note that we need to check that we haven't
9889 // already lexed a comment here because that falls
9890 // through into here as well.
9891 if (!lexed_comment) {
9892 parser->current.end += eol_length - 1; // skip CR
9893 }
9894
9895 if (parser->heredoc_end == NULL) {
9896 pm_newline_list_append(&parser->newline_list, parser->current.end - 1);
9897 }
9898 }
9899
9900 if (parser->heredoc_end) {
9901 parser_flush_heredoc_end(parser);
9902 }
9903
9904 // If this is an ignored newline, then we can continue lexing after
9905 // calling the callback with the ignored newline token.
9906 switch (lex_state_ignored_p(parser)) {
9907 case PM_IGNORED_NEWLINE_NONE:
9908 break;
9909 case PM_IGNORED_NEWLINE_PATTERN:
9910 if (parser->pattern_matching_newlines || parser->in_keyword_arg) {
9911 if (!lexed_comment) parser_lex_ignored_newline(parser);
9912 lex_state_set(parser, PM_LEX_STATE_BEG);
9913 parser->command_start = true;
9914 parser->current.type = PM_TOKEN_NEWLINE;
9915 return;
9916 }
9918 case PM_IGNORED_NEWLINE_ALL:
9919 if (!lexed_comment) parser_lex_ignored_newline(parser);
9920 lexed_comment = false;
9921 goto lex_next_token;
9922 }
9923
9924 // Here we need to look ahead and see if there is a call operator
9925 // (either . or &.) that starts the next line. If there is, then this
9926 // is going to become an ignored newline and we're going to instead
9927 // return the call operator.
9928 const uint8_t *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start;
9929 next_content += pm_strspn_inline_whitespace(next_content, parser->end - next_content);
9930
9931 if (next_content < parser->end) {
9932 // If we hit a comment after a newline, then we're going to check
9933 // if it's ignored or if it's followed by a method call ('.').
9934 // If it is, then we're going to call the
9935 // callback with an ignored newline and then continue lexing.
9936 // Otherwise we'll return a regular newline.
9937 if (next_content[0] == '#') {
9938 // Here we look for a "." or "&." following a "\n".
9939 const uint8_t *following = next_newline(next_content, parser->end - next_content);
9940
9941 while (following && (following + 1 < parser->end)) {
9942 following++;
9943 following += pm_strspn_inline_whitespace(following, parser->end - following);
9944
9945 // If this is not followed by a comment, then we can break out
9946 // of this loop.
9947 if (peek_at(parser, following) != '#') break;
9948
9949 // If there is a comment, then we need to find the end of the
9950 // comment and continue searching from there.
9951 following = next_newline(following, parser->end - following);
9952 }
9953
9954 // If the lex state was ignored, we will lex the
9955 // ignored newline.
9956 if (lex_state_ignored_p(parser)) {
9957 if (!lexed_comment) parser_lex_ignored_newline(parser);
9958 lexed_comment = false;
9959 goto lex_next_token;
9960 }
9961
9962 // If we hit a '.' or a '&.' we will lex the ignored
9963 // newline.
9964 if (following && (
9965 (peek_at(parser, following) == '.') ||
9966 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
9967 )) {
9968 if (!lexed_comment) parser_lex_ignored_newline(parser);
9969 lexed_comment = false;
9970 goto lex_next_token;
9971 }
9972
9973
9974 // If we are parsing as CRuby 4.0 or later and we
9975 // hit a '&&' or a '||' then we will lex the ignored
9976 // newline.
9977 if (
9979 following && (
9980 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
9981 (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
9982 (
9983 peek_at(parser, following) == 'a' &&
9984 peek_at(parser, following + 1) == 'n' &&
9985 peek_at(parser, following + 2) == 'd' &&
9986 peek_at(parser, next_content + 3) != '!' &&
9987 peek_at(parser, next_content + 3) != '?' &&
9988 !char_is_identifier(parser, following + 3, parser->end - (following + 3))
9989 ) ||
9990 (
9991 peek_at(parser, following) == 'o' &&
9992 peek_at(parser, following + 1) == 'r' &&
9993 peek_at(parser, next_content + 2) != '!' &&
9994 peek_at(parser, next_content + 2) != '?' &&
9995 !char_is_identifier(parser, following + 2, parser->end - (following + 2))
9996 )
9997 )
9998 ) {
9999 if (!lexed_comment) parser_lex_ignored_newline(parser);
10000 lexed_comment = false;
10001 goto lex_next_token;
10002 }
10003 }
10004
10005 // If we hit a . after a newline, then we're in a call chain and
10006 // we need to return the call operator.
10007 if (next_content[0] == '.') {
10008 // To match ripper, we need to emit an ignored newline even though
10009 // it's a real newline in the case that we have a beginless range
10010 // on a subsequent line.
10011 if (peek_at(parser, next_content + 1) == '.') {
10012 if (!lexed_comment) parser_lex_ignored_newline(parser);
10013 lex_state_set(parser, PM_LEX_STATE_BEG);
10014 parser->command_start = true;
10015 parser->current.type = PM_TOKEN_NEWLINE;
10016 return;
10017 }
10018
10019 if (!lexed_comment) parser_lex_ignored_newline(parser);
10020 lex_state_set(parser, PM_LEX_STATE_DOT);
10021 parser->current.start = next_content;
10022 parser->current.end = next_content + 1;
10023 parser->next_start = NULL;
10024 LEX(PM_TOKEN_DOT);
10025 }
10026
10027 // If we hit a &. after a newline, then we're in a call chain and
10028 // we need to return the call operator.
10029 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '.') {
10030 if (!lexed_comment) parser_lex_ignored_newline(parser);
10031 lex_state_set(parser, PM_LEX_STATE_DOT);
10032 parser->current.start = next_content;
10033 parser->current.end = next_content + 2;
10034 parser->next_start = NULL;
10035 LEX(PM_TOKEN_AMPERSAND_DOT);
10036 }
10037
10038 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
10039 // If we hit an && then we are in a logical chain
10040 // and we need to return the logical operator.
10041 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
10042 if (!lexed_comment) parser_lex_ignored_newline(parser);
10043 lex_state_set(parser, PM_LEX_STATE_BEG);
10044 parser->current.start = next_content;
10045 parser->current.end = next_content + 2;
10046 parser->next_start = NULL;
10047 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10048 }
10049
10050 // If we hit a || then we are in a logical chain and
10051 // we need to return the logical operator.
10052 if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
10053 if (!lexed_comment) parser_lex_ignored_newline(parser);
10054 lex_state_set(parser, PM_LEX_STATE_BEG);
10055 parser->current.start = next_content;
10056 parser->current.end = next_content + 2;
10057 parser->next_start = NULL;
10058 LEX(PM_TOKEN_PIPE_PIPE);
10059 }
10060
10061 // If we hit an 'and' then we are in a logical chain
10062 // and we need to return the logical operator.
10063 if (
10064 peek_at(parser, next_content) == 'a' &&
10065 peek_at(parser, next_content + 1) == 'n' &&
10066 peek_at(parser, next_content + 2) == 'd' &&
10067 peek_at(parser, next_content + 3) != '!' &&
10068 peek_at(parser, next_content + 3) != '?' &&
10069 !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
10070 ) {
10071 if (!lexed_comment) parser_lex_ignored_newline(parser);
10072 lex_state_set(parser, PM_LEX_STATE_BEG);
10073 parser->current.start = next_content;
10074 parser->current.end = next_content + 3;
10075 parser->next_start = NULL;
10076 parser->command_start = true;
10077 LEX(PM_TOKEN_KEYWORD_AND);
10078 }
10079
10080 // If we hit a 'or' then we are in a logical chain
10081 // and we need to return the logical operator.
10082 if (
10083 peek_at(parser, next_content) == 'o' &&
10084 peek_at(parser, next_content + 1) == 'r' &&
10085 peek_at(parser, next_content + 2) != '!' &&
10086 peek_at(parser, next_content + 2) != '?' &&
10087 !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
10088 ) {
10089 if (!lexed_comment) parser_lex_ignored_newline(parser);
10090 lex_state_set(parser, PM_LEX_STATE_BEG);
10091 parser->current.start = next_content;
10092 parser->current.end = next_content + 2;
10093 parser->next_start = NULL;
10094 parser->command_start = true;
10095 LEX(PM_TOKEN_KEYWORD_OR);
10096 }
10097 }
10098 }
10099
10100 // At this point we know this is a regular newline, and we can set the
10101 // necessary state and return the token.
10102 lex_state_set(parser, PM_LEX_STATE_BEG);
10103 parser->command_start = true;
10104 parser->current.type = PM_TOKEN_NEWLINE;
10105 if (!lexed_comment) parser_lex_callback(parser);
10106 return;
10107 }
10108
10109 // ,
10110 case ',':
10111 if ((parser->previous.type == PM_TOKEN_COMMA) && (parser->enclosure_nesting > 0)) {
10112 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARRAY_TERM, pm_token_type_human(parser->current.type));
10113 }
10114
10115 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10116 LEX(PM_TOKEN_COMMA);
10117
10118 // (
10119 case '(': {
10120 pm_token_type_t type = PM_TOKEN_PARENTHESIS_LEFT;
10121
10122 if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (PM_LEX_STATE_END | PM_LEX_STATE_LABEL))) {
10123 type = PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES;
10124 }
10125
10126 parser->enclosure_nesting++;
10127 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10128 pm_do_loop_stack_push(parser, false);
10129 LEX(type);
10130 }
10131
10132 // )
10133 case ')':
10134 parser->enclosure_nesting--;
10135 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10136 pm_do_loop_stack_pop(parser);
10137 LEX(PM_TOKEN_PARENTHESIS_RIGHT);
10138
10139 // ;
10140 case ';':
10141 lex_state_set(parser, PM_LEX_STATE_BEG);
10142 parser->command_start = true;
10143 LEX(PM_TOKEN_SEMICOLON);
10144
10145 // [ [] []=
10146 case '[':
10147 parser->enclosure_nesting++;
10148 pm_token_type_t type = PM_TOKEN_BRACKET_LEFT;
10149
10150 if (lex_state_operator_p(parser)) {
10151 if (match(parser, ']')) {
10152 parser->enclosure_nesting--;
10153 lex_state_set(parser, PM_LEX_STATE_ARG);
10154 LEX(match(parser, '=') ? PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : PM_TOKEN_BRACKET_LEFT_RIGHT);
10155 }
10156
10157 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABEL);
10158 LEX(type);
10159 }
10160
10161 if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, PM_LEX_STATE_LABELED)))) {
10162 type = PM_TOKEN_BRACKET_LEFT_ARRAY;
10163 }
10164
10165 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10166 pm_do_loop_stack_push(parser, false);
10167 LEX(type);
10168
10169 // ]
10170 case ']':
10171 parser->enclosure_nesting--;
10172 lex_state_set(parser, PM_LEX_STATE_END);
10173 pm_do_loop_stack_pop(parser);
10174 LEX(PM_TOKEN_BRACKET_RIGHT);
10175
10176 // {
10177 case '{': {
10178 pm_token_type_t type = PM_TOKEN_BRACE_LEFT;
10179
10180 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
10181 // This { begins a lambda
10182 parser->command_start = true;
10183 lex_state_set(parser, PM_LEX_STATE_BEG);
10184 type = PM_TOKEN_LAMBDA_BEGIN;
10185 } else if (lex_state_p(parser, PM_LEX_STATE_LABELED)) {
10186 // This { begins a hash literal
10187 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10188 } else if (lex_state_p(parser, PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_END | PM_LEX_STATE_ENDFN)) {
10189 // This { begins a block
10190 parser->command_start = true;
10191 lex_state_set(parser, PM_LEX_STATE_BEG);
10192 } else if (lex_state_p(parser, PM_LEX_STATE_ENDARG)) {
10193 // This { begins a block on a command
10194 parser->command_start = true;
10195 lex_state_set(parser, PM_LEX_STATE_BEG);
10196 } else {
10197 // This { begins a hash literal
10198 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10199 }
10200
10201 parser->enclosure_nesting++;
10202 parser->brace_nesting++;
10203 pm_do_loop_stack_push(parser, false);
10204
10205 LEX(type);
10206 }
10207
10208 // }
10209 case '}':
10210 parser->enclosure_nesting--;
10211 pm_do_loop_stack_pop(parser);
10212
10213 if ((parser->lex_modes.current->mode == PM_LEX_EMBEXPR) && (parser->brace_nesting == 0)) {
10214 lex_mode_pop(parser);
10215 LEX(PM_TOKEN_EMBEXPR_END);
10216 }
10217
10218 parser->brace_nesting--;
10219 lex_state_set(parser, PM_LEX_STATE_END);
10220 LEX(PM_TOKEN_BRACE_RIGHT);
10221
10222 // * ** **= *=
10223 case '*': {
10224 if (match(parser, '*')) {
10225 if (match(parser, '=')) {
10226 lex_state_set(parser, PM_LEX_STATE_BEG);
10227 LEX(PM_TOKEN_STAR_STAR_EQUAL);
10228 }
10229
10230 pm_token_type_t type = PM_TOKEN_STAR_STAR;
10231
10232 if (lex_state_spcarg_p(parser, space_seen)) {
10233 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR);
10234 type = PM_TOKEN_USTAR_STAR;
10235 } else if (lex_state_beg_p(parser)) {
10236 type = PM_TOKEN_USTAR_STAR;
10237 } else if (ambiguous_operator_p(parser, space_seen)) {
10238 PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "**", "argument prefix");
10239 }
10240
10241 if (lex_state_operator_p(parser)) {
10242 lex_state_set(parser, PM_LEX_STATE_ARG);
10243 } else {
10244 lex_state_set(parser, PM_LEX_STATE_BEG);
10245 }
10246
10247 LEX(type);
10248 }
10249
10250 if (match(parser, '=')) {
10251 lex_state_set(parser, PM_LEX_STATE_BEG);
10252 LEX(PM_TOKEN_STAR_EQUAL);
10253 }
10254
10255 pm_token_type_t type = PM_TOKEN_STAR;
10256
10257 if (lex_state_spcarg_p(parser, space_seen)) {
10258 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR);
10259 type = PM_TOKEN_USTAR;
10260 } else if (lex_state_beg_p(parser)) {
10261 type = PM_TOKEN_USTAR;
10262 } else if (ambiguous_operator_p(parser, space_seen)) {
10263 PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "*", "argument prefix");
10264 }
10265
10266 if (lex_state_operator_p(parser)) {
10267 lex_state_set(parser, PM_LEX_STATE_ARG);
10268 } else {
10269 lex_state_set(parser, PM_LEX_STATE_BEG);
10270 }
10271
10272 LEX(type);
10273 }
10274
10275 // ! != !~ !@
10276 case '!':
10277 if (lex_state_operator_p(parser)) {
10278 lex_state_set(parser, PM_LEX_STATE_ARG);
10279 if (match(parser, '@')) {
10280 LEX(PM_TOKEN_BANG);
10281 }
10282 } else {
10283 lex_state_set(parser, PM_LEX_STATE_BEG);
10284 }
10285
10286 if (match(parser, '=')) {
10287 LEX(PM_TOKEN_BANG_EQUAL);
10288 }
10289
10290 if (match(parser, '~')) {
10291 LEX(PM_TOKEN_BANG_TILDE);
10292 }
10293
10294 LEX(PM_TOKEN_BANG);
10295
10296 // = => =~ == === =begin
10297 case '=':
10298 if (
10299 current_token_starts_line(parser) &&
10300 (parser->current.end + 5 <= parser->end) &&
10301 memcmp(parser->current.end, "begin", 5) == 0 &&
10302 (pm_char_is_whitespace(peek_offset(parser, 5)) || (peek_offset(parser, 5) == '\0'))
10303 ) {
10304 pm_token_type_t type = lex_embdoc(parser);
10305 if (type == PM_TOKEN_EOF) {
10306 LEX(type);
10307 }
10308
10309 goto lex_next_token;
10310 }
10311
10312 if (lex_state_operator_p(parser)) {
10313 lex_state_set(parser, PM_LEX_STATE_ARG);
10314 } else {
10315 lex_state_set(parser, PM_LEX_STATE_BEG);
10316 }
10317
10318 if (match(parser, '>')) {
10319 LEX(PM_TOKEN_EQUAL_GREATER);
10320 }
10321
10322 if (match(parser, '~')) {
10323 LEX(PM_TOKEN_EQUAL_TILDE);
10324 }
10325
10326 if (match(parser, '=')) {
10327 LEX(match(parser, '=') ? PM_TOKEN_EQUAL_EQUAL_EQUAL : PM_TOKEN_EQUAL_EQUAL);
10328 }
10329
10330 LEX(PM_TOKEN_EQUAL);
10331
10332 // < << <<= <= <=>
10333 case '<':
10334 if (match(parser, '<')) {
10335 if (
10336 !lex_state_p(parser, PM_LEX_STATE_DOT | PM_LEX_STATE_CLASS) &&
10337 !lex_state_end_p(parser) &&
10338 (!lex_state_p(parser, PM_LEX_STATE_ARG_ANY) || lex_state_p(parser, PM_LEX_STATE_LABELED) || space_seen)
10339 ) {
10340 const uint8_t *end = parser->current.end;
10341
10342 pm_heredoc_quote_t quote = PM_HEREDOC_QUOTE_NONE;
10343 pm_heredoc_indent_t indent = PM_HEREDOC_INDENT_NONE;
10344
10345 if (match(parser, '-')) {
10346 indent = PM_HEREDOC_INDENT_DASH;
10347 }
10348 else if (match(parser, '~')) {
10349 indent = PM_HEREDOC_INDENT_TILDE;
10350 }
10351
10352 if (match(parser, '`')) {
10353 quote = PM_HEREDOC_QUOTE_BACKTICK;
10354 }
10355 else if (match(parser, '"')) {
10356 quote = PM_HEREDOC_QUOTE_DOUBLE;
10357 }
10358 else if (match(parser, '\'')) {
10359 quote = PM_HEREDOC_QUOTE_SINGLE;
10360 }
10361
10362 const uint8_t *ident_start = parser->current.end;
10363 size_t width = 0;
10364
10365 if (parser->current.end >= parser->end) {
10366 parser->current.end = end;
10367 } else if (quote == PM_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) == 0) {
10368 parser->current.end = end;
10369 } else {
10370 if (quote == PM_HEREDOC_QUOTE_NONE) {
10371 parser->current.end += width;
10372
10373 while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end))) {
10374 parser->current.end += width;
10375 }
10376 } else {
10377 // If we have quotes, then we're going to go until we find the
10378 // end quote.
10379 while ((parser->current.end < parser->end) && quote != (pm_heredoc_quote_t) (*parser->current.end)) {
10380 if (*parser->current.end == '\r' || *parser->current.end == '\n') break;
10381 parser->current.end++;
10382 }
10383 }
10384
10385 size_t ident_length = (size_t) (parser->current.end - ident_start);
10386 bool ident_error = false;
10387
10388 if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) {
10389 pm_parser_err(parser, ident_start, ident_start + ident_length, PM_ERR_HEREDOC_IDENTIFIER);
10390 ident_error = true;
10391 }
10392
10393 parser->explicit_encoding = NULL;
10394 lex_mode_push(parser, (pm_lex_mode_t) {
10395 .mode = PM_LEX_HEREDOC,
10396 .as.heredoc = {
10397 .base = {
10398 .ident_start = ident_start,
10399 .ident_length = ident_length,
10400 .quote = quote,
10401 .indent = indent
10402 },
10403 .next_start = parser->current.end,
10404 .common_whitespace = NULL,
10405 .line_continuation = false
10406 }
10407 });
10408
10409 if (parser->heredoc_end == NULL) {
10410 const uint8_t *body_start = next_newline(parser->current.end, parser->end - parser->current.end);
10411
10412 if (body_start == NULL) {
10413 // If there is no newline after the heredoc identifier, then
10414 // this is not a valid heredoc declaration. In this case we
10415 // will add an error, but we will still return a heredoc
10416 // start.
10417 if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length);
10418 body_start = parser->end;
10419 } else {
10420 // Otherwise, we want to indicate that the body of the
10421 // heredoc starts on the character after the next newline.
10422 pm_newline_list_append(&parser->newline_list, body_start);
10423 body_start++;
10424 }
10425
10426 parser->next_start = body_start;
10427 } else {
10428 parser->next_start = parser->heredoc_end;
10429 }
10430
10431 LEX(PM_TOKEN_HEREDOC_START);
10432 }
10433 }
10434
10435 if (match(parser, '=')) {
10436 lex_state_set(parser, PM_LEX_STATE_BEG);
10437 LEX(PM_TOKEN_LESS_LESS_EQUAL);
10438 }
10439
10440 if (ambiguous_operator_p(parser, space_seen)) {
10441 PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "<<", "here document");
10442 }
10443
10444 if (lex_state_operator_p(parser)) {
10445 lex_state_set(parser, PM_LEX_STATE_ARG);
10446 } else {
10447 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10448 lex_state_set(parser, PM_LEX_STATE_BEG);
10449 }
10450
10451 LEX(PM_TOKEN_LESS_LESS);
10452 }
10453
10454 if (lex_state_operator_p(parser)) {
10455 lex_state_set(parser, PM_LEX_STATE_ARG);
10456 } else {
10457 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10458 lex_state_set(parser, PM_LEX_STATE_BEG);
10459 }
10460
10461 if (match(parser, '=')) {
10462 if (match(parser, '>')) {
10463 LEX(PM_TOKEN_LESS_EQUAL_GREATER);
10464 }
10465
10466 LEX(PM_TOKEN_LESS_EQUAL);
10467 }
10468
10469 LEX(PM_TOKEN_LESS);
10470
10471 // > >> >>= >=
10472 case '>':
10473 if (match(parser, '>')) {
10474 if (lex_state_operator_p(parser)) {
10475 lex_state_set(parser, PM_LEX_STATE_ARG);
10476 } else {
10477 lex_state_set(parser, PM_LEX_STATE_BEG);
10478 }
10479 LEX(match(parser, '=') ? PM_TOKEN_GREATER_GREATER_EQUAL : PM_TOKEN_GREATER_GREATER);
10480 }
10481
10482 if (lex_state_operator_p(parser)) {
10483 lex_state_set(parser, PM_LEX_STATE_ARG);
10484 } else {
10485 lex_state_set(parser, PM_LEX_STATE_BEG);
10486 }
10487
10488 LEX(match(parser, '=') ? PM_TOKEN_GREATER_EQUAL : PM_TOKEN_GREATER);
10489
10490 // double-quoted string literal
10491 case '"': {
10492 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10493 lex_mode_push_string(parser, true, label_allowed, '\0', '"');
10494 LEX(PM_TOKEN_STRING_BEGIN);
10495 }
10496
10497 // xstring literal
10498 case '`': {
10499 if (lex_state_p(parser, PM_LEX_STATE_FNAME)) {
10500 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10501 LEX(PM_TOKEN_BACKTICK);
10502 }
10503
10504 if (lex_state_p(parser, PM_LEX_STATE_DOT)) {
10505 if (previous_command_start) {
10506 lex_state_set(parser, PM_LEX_STATE_CMDARG);
10507 } else {
10508 lex_state_set(parser, PM_LEX_STATE_ARG);
10509 }
10510
10511 LEX(PM_TOKEN_BACKTICK);
10512 }
10513
10514 lex_mode_push_string(parser, true, false, '\0', '`');
10515 LEX(PM_TOKEN_BACKTICK);
10516 }
10517
10518 // single-quoted string literal
10519 case '\'': {
10520 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10521 lex_mode_push_string(parser, false, label_allowed, '\0', '\'');
10522 LEX(PM_TOKEN_STRING_BEGIN);
10523 }
10524
10525 // ? character literal
10526 case '?':
10527 LEX(lex_question_mark(parser));
10528
10529 // & && &&= &=
10530 case '&': {
10531 if (match(parser, '&')) {
10532 lex_state_set(parser, PM_LEX_STATE_BEG);
10533
10534 if (match(parser, '=')) {
10535 LEX(PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
10536 }
10537
10538 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10539 }
10540
10541 if (match(parser, '=')) {
10542 lex_state_set(parser, PM_LEX_STATE_BEG);
10543 LEX(PM_TOKEN_AMPERSAND_EQUAL);
10544 }
10545
10546 if (match(parser, '.')) {
10547 lex_state_set(parser, PM_LEX_STATE_DOT);
10548 LEX(PM_TOKEN_AMPERSAND_DOT);
10549 }
10550
10551 pm_token_type_t type = PM_TOKEN_AMPERSAND;
10552 if (lex_state_spcarg_p(parser, space_seen)) {
10553 if ((peek(parser) != ':') || (peek_offset(parser, 1) == '\0')) {
10554 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
10555 } else {
10556 const uint8_t delim = peek_offset(parser, 1);
10557
10558 if ((delim != '\'') && (delim != '"') && !char_is_identifier(parser, parser->current.end + 1, parser->end - (parser->current.end + 1))) {
10559 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
10560 }
10561 }
10562
10563 type = PM_TOKEN_UAMPERSAND;
10564 } else if (lex_state_beg_p(parser)) {
10565 type = PM_TOKEN_UAMPERSAND;
10566 } else if (ambiguous_operator_p(parser, space_seen)) {
10567 PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "&", "argument prefix");
10568 }
10569
10570 if (lex_state_operator_p(parser)) {
10571 lex_state_set(parser, PM_LEX_STATE_ARG);
10572 } else {
10573 lex_state_set(parser, PM_LEX_STATE_BEG);
10574 }
10575
10576 LEX(type);
10577 }
10578
10579 // | || ||= |=
10580 case '|':
10581 if (match(parser, '|')) {
10582 if (match(parser, '=')) {
10583 lex_state_set(parser, PM_LEX_STATE_BEG);
10584 LEX(PM_TOKEN_PIPE_PIPE_EQUAL);
10585 }
10586
10587 if (lex_state_p(parser, PM_LEX_STATE_BEG)) {
10588 parser->current.end--;
10589 LEX(PM_TOKEN_PIPE);
10590 }
10591
10592 lex_state_set(parser, PM_LEX_STATE_BEG);
10593 LEX(PM_TOKEN_PIPE_PIPE);
10594 }
10595
10596 if (match(parser, '=')) {
10597 lex_state_set(parser, PM_LEX_STATE_BEG);
10598 LEX(PM_TOKEN_PIPE_EQUAL);
10599 }
10600
10601 if (lex_state_operator_p(parser)) {
10602 lex_state_set(parser, PM_LEX_STATE_ARG);
10603 } else {
10604 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10605 }
10606
10607 LEX(PM_TOKEN_PIPE);
10608
10609 // + += +@
10610 case '+': {
10611 if (lex_state_operator_p(parser)) {
10612 lex_state_set(parser, PM_LEX_STATE_ARG);
10613
10614 if (match(parser, '@')) {
10615 LEX(PM_TOKEN_UPLUS);
10616 }
10617
10618 LEX(PM_TOKEN_PLUS);
10619 }
10620
10621 if (match(parser, '=')) {
10622 lex_state_set(parser, PM_LEX_STATE_BEG);
10623 LEX(PM_TOKEN_PLUS_EQUAL);
10624 }
10625
10626 if (
10627 lex_state_beg_p(parser) ||
10628 (lex_state_spcarg_p(parser, space_seen) ? (pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS), true) : false)
10629 ) {
10630 lex_state_set(parser, PM_LEX_STATE_BEG);
10631
10632 if (pm_char_is_decimal_digit(peek(parser))) {
10633 parser->current.end++;
10634 pm_token_type_t type = lex_numeric(parser);
10635 lex_state_set(parser, PM_LEX_STATE_END);
10636 LEX(type);
10637 }
10638
10639 LEX(PM_TOKEN_UPLUS);
10640 }
10641
10642 if (ambiguous_operator_p(parser, space_seen)) {
10643 PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "+", "unary operator");
10644 }
10645
10646 lex_state_set(parser, PM_LEX_STATE_BEG);
10647 LEX(PM_TOKEN_PLUS);
10648 }
10649
10650 // - -= -@
10651 case '-': {
10652 if (lex_state_operator_p(parser)) {
10653 lex_state_set(parser, PM_LEX_STATE_ARG);
10654
10655 if (match(parser, '@')) {
10656 LEX(PM_TOKEN_UMINUS);
10657 }
10658
10659 LEX(PM_TOKEN_MINUS);
10660 }
10661
10662 if (match(parser, '=')) {
10663 lex_state_set(parser, PM_LEX_STATE_BEG);
10664 LEX(PM_TOKEN_MINUS_EQUAL);
10665 }
10666
10667 if (match(parser, '>')) {
10668 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10669 LEX(PM_TOKEN_MINUS_GREATER);
10670 }
10671
10672 bool spcarg = lex_state_spcarg_p(parser, space_seen);
10673 bool is_beg = lex_state_beg_p(parser);
10674 if (!is_beg && spcarg) {
10675 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS);
10676 }
10677
10678 if (is_beg || spcarg) {
10679 lex_state_set(parser, PM_LEX_STATE_BEG);
10680 LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS);
10681 }
10682
10683 if (ambiguous_operator_p(parser, space_seen)) {
10684 PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "-", "unary operator");
10685 }
10686
10687 lex_state_set(parser, PM_LEX_STATE_BEG);
10688 LEX(PM_TOKEN_MINUS);
10689 }
10690
10691 // . .. ...
10692 case '.': {
10693 bool beg_p = lex_state_beg_p(parser);
10694
10695 if (match(parser, '.')) {
10696 if (match(parser, '.')) {
10697 // If we're _not_ inside a range within default parameters
10698 if (!context_p(parser, PM_CONTEXT_DEFAULT_PARAMS) && context_p(parser, PM_CONTEXT_DEF_PARAMS)) {
10699 if (lex_state_p(parser, PM_LEX_STATE_END)) {
10700 lex_state_set(parser, PM_LEX_STATE_BEG);
10701 } else {
10702 lex_state_set(parser, PM_LEX_STATE_ENDARG);
10703 }
10704 LEX(PM_TOKEN_UDOT_DOT_DOT);
10705 }
10706
10707 if (parser->enclosure_nesting == 0 && parser_end_of_line_p(parser)) {
10708 pm_parser_warn_token(parser, &parser->current, PM_WARN_DOT_DOT_DOT_EOL);
10709 }
10710
10711 lex_state_set(parser, PM_LEX_STATE_BEG);
10712 LEX(beg_p ? PM_TOKEN_UDOT_DOT_DOT : PM_TOKEN_DOT_DOT_DOT);
10713 }
10714
10715 lex_state_set(parser, PM_LEX_STATE_BEG);
10716 LEX(beg_p ? PM_TOKEN_UDOT_DOT : PM_TOKEN_DOT_DOT);
10717 }
10718
10719 lex_state_set(parser, PM_LEX_STATE_DOT);
10720 LEX(PM_TOKEN_DOT);
10721 }
10722
10723 // integer
10724 case '0':
10725 case '1':
10726 case '2':
10727 case '3':
10728 case '4':
10729 case '5':
10730 case '6':
10731 case '7':
10732 case '8':
10733 case '9': {
10734 pm_token_type_t type = lex_numeric(parser);
10735 lex_state_set(parser, PM_LEX_STATE_END);
10736 LEX(type);
10737 }
10738
10739 // :: symbol
10740 case ':':
10741 if (match(parser, ':')) {
10742 if (lex_state_beg_p(parser) || lex_state_p(parser, PM_LEX_STATE_CLASS) || (lex_state_p(parser, PM_LEX_STATE_ARG_ANY) && space_seen)) {
10743 lex_state_set(parser, PM_LEX_STATE_BEG);
10744 LEX(PM_TOKEN_UCOLON_COLON);
10745 }
10746
10747 lex_state_set(parser, PM_LEX_STATE_DOT);
10748 LEX(PM_TOKEN_COLON_COLON);
10749 }
10750
10751 if (lex_state_end_p(parser) || pm_char_is_whitespace(peek(parser)) || peek(parser) == '#') {
10752 lex_state_set(parser, PM_LEX_STATE_BEG);
10753 LEX(PM_TOKEN_COLON);
10754 }
10755
10756 if (peek(parser) == '"' || peek(parser) == '\'') {
10757 lex_mode_push_string(parser, peek(parser) == '"', false, '\0', *parser->current.end);
10758 parser->current.end++;
10759 }
10760
10761 lex_state_set(parser, PM_LEX_STATE_FNAME);
10762 LEX(PM_TOKEN_SYMBOL_BEGIN);
10763
10764 // / /=
10765 case '/':
10766 if (lex_state_beg_p(parser)) {
10767 lex_mode_push_regexp(parser, '\0', '/');
10768 LEX(PM_TOKEN_REGEXP_BEGIN);
10769 }
10770
10771 if (match(parser, '=')) {
10772 lex_state_set(parser, PM_LEX_STATE_BEG);
10773 LEX(PM_TOKEN_SLASH_EQUAL);
10774 }
10775
10776 if (lex_state_spcarg_p(parser, space_seen)) {
10777 // https://bugs.ruby-lang.org/issues/21994
10778 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_4_0) {
10779 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_SLASH);
10780 }
10781 lex_mode_push_regexp(parser, '\0', '/');
10782 LEX(PM_TOKEN_REGEXP_BEGIN);
10783 }
10784
10785 if (ambiguous_operator_p(parser, space_seen)) {
10786 PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "/", "regexp literal");
10787 }
10788
10789 if (lex_state_operator_p(parser)) {
10790 lex_state_set(parser, PM_LEX_STATE_ARG);
10791 } else {
10792 lex_state_set(parser, PM_LEX_STATE_BEG);
10793 }
10794
10795 LEX(PM_TOKEN_SLASH);
10796
10797 // ^ ^=
10798 case '^':
10799 if (lex_state_operator_p(parser)) {
10800 lex_state_set(parser, PM_LEX_STATE_ARG);
10801 } else {
10802 lex_state_set(parser, PM_LEX_STATE_BEG);
10803 }
10804 LEX(match(parser, '=') ? PM_TOKEN_CARET_EQUAL : PM_TOKEN_CARET);
10805
10806 // ~ ~@
10807 case '~':
10808 if (lex_state_operator_p(parser)) {
10809 (void) match(parser, '@');
10810 lex_state_set(parser, PM_LEX_STATE_ARG);
10811 } else {
10812 lex_state_set(parser, PM_LEX_STATE_BEG);
10813 }
10814
10815 LEX(PM_TOKEN_TILDE);
10816
10817 // % %= %i %I %q %Q %w %W
10818 case '%': {
10819 // If there is no subsequent character then we have an
10820 // invalid token. We're going to say it's the percent
10821 // operator because we don't want to move into the string
10822 // lex mode unnecessarily.
10823 if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) {
10824 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT_EOF);
10825 LEX(PM_TOKEN_PERCENT);
10826 }
10827
10828 if (!lex_state_beg_p(parser) && match(parser, '=')) {
10829 lex_state_set(parser, PM_LEX_STATE_BEG);
10830 LEX(PM_TOKEN_PERCENT_EQUAL);
10831 } else if (
10832 lex_state_beg_p(parser) ||
10833 (lex_state_p(parser, PM_LEX_STATE_FITEM) && (peek(parser) == 's')) ||
10834 lex_state_spcarg_p(parser, space_seen)
10835 ) {
10836 if (!parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end)) {
10837 if (*parser->current.end >= 0x80) {
10838 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
10839 }
10840
10841 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
10842 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
10843 LEX(PM_TOKEN_STRING_BEGIN);
10844 }
10845
10846 // Delimiters for %-literals cannot be alphanumeric. We
10847 // validate that here.
10848 uint8_t delimiter = peek_offset(parser, 1);
10849 if (delimiter >= 0x80 || parser->encoding->alnum_char(&delimiter, 1)) {
10850 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
10851 goto lex_next_token;
10852 }
10853
10854 switch (peek(parser)) {
10855 case 'i': {
10856 parser->current.end++;
10857
10858 if (parser->current.end < parser->end) {
10859 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
10860 } else {
10861 lex_mode_push_list_eof(parser);
10862 }
10863
10864 LEX(PM_TOKEN_PERCENT_LOWER_I);
10865 }
10866 case 'I': {
10867 parser->current.end++;
10868
10869 if (parser->current.end < parser->end) {
10870 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
10871 } else {
10872 lex_mode_push_list_eof(parser);
10873 }
10874
10875 LEX(PM_TOKEN_PERCENT_UPPER_I);
10876 }
10877 case 'r': {
10878 parser->current.end++;
10879
10880 if (parser->current.end < parser->end) {
10881 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
10882 lex_mode_push_regexp(parser, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
10883 } else {
10884 lex_mode_push_regexp(parser, '\0', '\0');
10885 }
10886
10887 LEX(PM_TOKEN_REGEXP_BEGIN);
10888 }
10889 case 'q': {
10890 parser->current.end++;
10891
10892 if (parser->current.end < parser->end) {
10893 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
10894 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
10895 } else {
10896 lex_mode_push_string_eof(parser);
10897 }
10898
10899 LEX(PM_TOKEN_STRING_BEGIN);
10900 }
10901 case 'Q': {
10902 parser->current.end++;
10903
10904 if (parser->current.end < parser->end) {
10905 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
10906 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
10907 } else {
10908 lex_mode_push_string_eof(parser);
10909 }
10910
10911 LEX(PM_TOKEN_STRING_BEGIN);
10912 }
10913 case 's': {
10914 parser->current.end++;
10915
10916 if (parser->current.end < parser->end) {
10917 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
10918 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
10919 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
10920 } else {
10921 lex_mode_push_string_eof(parser);
10922 }
10923
10924 LEX(PM_TOKEN_SYMBOL_BEGIN);
10925 }
10926 case 'w': {
10927 parser->current.end++;
10928
10929 if (parser->current.end < parser->end) {
10930 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
10931 } else {
10932 lex_mode_push_list_eof(parser);
10933 }
10934
10935 LEX(PM_TOKEN_PERCENT_LOWER_W);
10936 }
10937 case 'W': {
10938 parser->current.end++;
10939
10940 if (parser->current.end < parser->end) {
10941 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
10942 } else {
10943 lex_mode_push_list_eof(parser);
10944 }
10945
10946 LEX(PM_TOKEN_PERCENT_UPPER_W);
10947 }
10948 case 'x': {
10949 parser->current.end++;
10950
10951 if (parser->current.end < parser->end) {
10952 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
10953 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
10954 } else {
10955 lex_mode_push_string_eof(parser);
10956 }
10957
10958 LEX(PM_TOKEN_PERCENT_LOWER_X);
10959 }
10960 default:
10961 // If we get to this point, then we have a % that is completely
10962 // unparsable. In this case we'll just drop it from the parser
10963 // and skip past it and hope that the next token is something
10964 // that we can parse.
10965 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
10966 goto lex_next_token;
10967 }
10968 }
10969
10970 if (ambiguous_operator_p(parser, space_seen)) {
10971 PM_PARSER_WARN_TOKEN_FORMAT(parser, parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "%", "string literal");
10972 }
10973
10974 lex_state_set(parser, lex_state_operator_p(parser) ? PM_LEX_STATE_ARG : PM_LEX_STATE_BEG);
10975 LEX(PM_TOKEN_PERCENT);
10976 }
10977
10978 // global variable
10979 case '$': {
10980 pm_token_type_t type = lex_global_variable(parser);
10981
10982 // If we're lexing an embedded variable, then we need to pop back into
10983 // the parent lex context.
10984 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
10985 lex_mode_pop(parser);
10986 }
10987
10988 lex_state_set(parser, PM_LEX_STATE_END);
10989 LEX(type);
10990 }
10991
10992 // instance variable, class variable
10993 case '@':
10994 lex_state_set(parser, parser->lex_state & PM_LEX_STATE_FNAME ? PM_LEX_STATE_ENDFN : PM_LEX_STATE_END);
10995 LEX(lex_at_variable(parser));
10996
10997 default: {
10998 if (*parser->current.start != '_') {
10999 size_t width = char_is_identifier_start(parser, parser->current.start, parser->end - parser->current.start);
11000
11001 // If this isn't the beginning of an identifier, then
11002 // it's an invalid token as we've exhausted all of the
11003 // other options. We'll skip past it and return the next
11004 // token after adding an appropriate error message.
11005 if (!width) {
11006 if (*parser->current.start >= 0x80) {
11007 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *parser->current.start);
11008 } else if (*parser->current.start == '\\') {
11009 switch (peek_at(parser, parser->current.start + 1)) {
11010 case ' ':
11011 parser->current.end++;
11012 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped space");
11013 break;
11014 case '\f':
11015 parser->current.end++;
11016 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped form feed");
11017 break;
11018 case '\t':
11019 parser->current.end++;
11020 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped horizontal tab");
11021 break;
11022 case '\v':
11023 parser->current.end++;
11024 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped vertical tab");
11025 break;
11026 case '\r':
11027 if (peek_at(parser, parser->current.start + 2) != '\n') {
11028 parser->current.end++;
11029 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped carriage return");
11030 break;
11031 }
11033 default:
11034 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "backslash");
11035 break;
11036 }
11037 } else if (char_is_ascii_printable(*parser->current.start)) {
11038 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_PRINTABLE_CHARACTER, *parser->current.start);
11039 } else {
11040 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_INVALID_CHARACTER, *parser->current.start);
11041 }
11042
11043 goto lex_next_token;
11044 }
11045
11046 parser->current.end = parser->current.start + width;
11047 }
11048
11049 pm_token_type_t type = lex_identifier(parser, previous_command_start);
11050
11051 // If we've hit a __END__ and it was at the start of the
11052 // line or the start of the file and it is followed by
11053 // either a \n or a \r\n, then this is the last token of the
11054 // file.
11055 if (
11056 ((parser->current.end - parser->current.start) == 7) &&
11057 current_token_starts_line(parser) &&
11058 (memcmp(parser->current.start, "__END__", 7) == 0) &&
11059 (parser->current.end == parser->end || match_eol(parser))
11060 ) {
11061 // Since we know we're about to add an __END__ comment,
11062 // we know we need to add all of the newlines to get the
11063 // correct column information for it.
11064 const uint8_t *cursor = parser->current.end;
11065 while ((cursor = next_newline(cursor, parser->end - cursor)) != NULL) {
11066 pm_newline_list_append(&parser->newline_list, cursor++);
11067 }
11068
11069 parser->current.end = parser->end;
11070 parser->current.type = PM_TOKEN___END__;
11071 parser_lex_callback(parser);
11072
11073 parser->data_loc.start = parser->current.start;
11074 parser->data_loc.end = parser->current.end;
11075
11076 LEX(PM_TOKEN_EOF);
11077 }
11078
11079 pm_lex_state_t last_state = parser->lex_state;
11080
11081 if (type == PM_TOKEN_IDENTIFIER || type == PM_TOKEN_CONSTANT || type == PM_TOKEN_METHOD_NAME) {
11082 if (lex_state_p(parser, PM_LEX_STATE_BEG_ANY | PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_DOT)) {
11083 if (previous_command_start) {
11084 lex_state_set(parser, PM_LEX_STATE_CMDARG);
11085 } else {
11086 lex_state_set(parser, PM_LEX_STATE_ARG);
11087 }
11088 } else if (parser->lex_state == PM_LEX_STATE_FNAME) {
11089 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11090 } else {
11091 lex_state_set(parser, PM_LEX_STATE_END);
11092 }
11093 }
11094
11095 if (
11096 !(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) &&
11097 (type == PM_TOKEN_IDENTIFIER) &&
11098 ((pm_parser_local_depth(parser, &parser->current) != -1) ||
11099 pm_token_is_numbered_parameter(parser->current.start, parser->current.end))
11100 ) {
11101 lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL);
11102 }
11103
11104 LEX(type);
11105 }
11106 }
11107 }
11108 case PM_LEX_LIST: {
11109 if (parser->next_start != NULL) {
11110 parser->current.end = parser->next_start;
11111 parser->next_start = NULL;
11112 }
11113
11114 // First we'll set the beginning of the token.
11115 parser->current.start = parser->current.end;
11116
11117 // If there's any whitespace at the start of the list, then we're
11118 // going to trim it off the beginning and create a new token.
11119 size_t whitespace;
11120
11121 if (parser->heredoc_end) {
11122 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11123 if (peek_offset(parser, (ptrdiff_t)whitespace) == '\n') {
11124 whitespace += 1;
11125 }
11126 } else {
11127 whitespace = pm_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->newline_list);
11128 }
11129
11130 if (whitespace > 0) {
11131 parser->current.end += whitespace;
11132 if (peek_offset(parser, -1) == '\n') {
11133 // mutates next_start
11134 parser_flush_heredoc_end(parser);
11135 }
11136 LEX(PM_TOKEN_WORDS_SEP);
11137 }
11138
11139 // We'll check if we're at the end of the file. If we are, then we
11140 // need to return the EOF token.
11141 if (parser->current.end >= parser->end) {
11142 LEX(PM_TOKEN_EOF);
11143 }
11144
11145 // Here we'll get a list of the places where strpbrk should break,
11146 // and then find the first one.
11147 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11148 const uint8_t *breakpoints = lex_mode->as.list.breakpoints;
11149 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11150
11151 // If we haven't found an escape yet, then this buffer will be
11152 // unallocated since we can refer directly to the source string.
11153 pm_token_buffer_t token_buffer = { 0 };
11154
11155 while (breakpoint != NULL) {
11156 // If we hit whitespace, then we must have received content by
11157 // now, so we can return an element of the list.
11158 if (pm_char_is_whitespace(*breakpoint)) {
11159 parser->current.end = breakpoint;
11160 pm_token_buffer_flush(parser, &token_buffer);
11161 LEX(PM_TOKEN_STRING_CONTENT);
11162 }
11163
11164 // If we hit the terminator, we need to check which token to
11165 // return.
11166 if (*breakpoint == lex_mode->as.list.terminator) {
11167 // If this terminator doesn't actually close the list, then
11168 // we need to continue on past it.
11169 if (lex_mode->as.list.nesting > 0) {
11170 parser->current.end = breakpoint + 1;
11171 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11172 lex_mode->as.list.nesting--;
11173 continue;
11174 }
11175
11176 // If we've hit the terminator and we've already skipped
11177 // past content, then we can return a list node.
11178 if (breakpoint > parser->current.start) {
11179 parser->current.end = breakpoint;
11180 pm_token_buffer_flush(parser, &token_buffer);
11181 LEX(PM_TOKEN_STRING_CONTENT);
11182 }
11183
11184 // Otherwise, switch back to the default state and return
11185 // the end of the list.
11186 parser->current.end = breakpoint + 1;
11187 lex_mode_pop(parser);
11188 lex_state_set(parser, PM_LEX_STATE_END);
11189 LEX(PM_TOKEN_STRING_END);
11190 }
11191
11192 // If we hit a null byte, skip directly past it.
11193 if (*breakpoint == '\0') {
11194 breakpoint = pm_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1), true);
11195 continue;
11196 }
11197
11198 // If we hit escapes, then we need to treat the next token
11199 // literally. In this case we'll skip past the next character
11200 // and find the next breakpoint.
11201 if (*breakpoint == '\\') {
11202 parser->current.end = breakpoint + 1;
11203
11204 // If we've hit the end of the file, then break out of the
11205 // loop by setting the breakpoint to NULL.
11206 if (parser->current.end == parser->end) {
11207 breakpoint = NULL;
11208 continue;
11209 }
11210
11211 pm_token_buffer_escape(parser, &token_buffer);
11212 uint8_t peeked = peek(parser);
11213
11214 switch (peeked) {
11215 case ' ':
11216 case '\f':
11217 case '\t':
11218 case '\v':
11219 case '\\':
11220 pm_token_buffer_push_byte(&token_buffer, peeked);
11221 parser->current.end++;
11222 break;
11223 case '\r':
11224 parser->current.end++;
11225 if (peek(parser) != '\n') {
11226 pm_token_buffer_push_byte(&token_buffer, '\r');
11227 break;
11228 }
11230 case '\n':
11231 pm_token_buffer_push_byte(&token_buffer, '\n');
11232
11233 if (parser->heredoc_end) {
11234 // ... if we are on the same line as a heredoc,
11235 // flush the heredoc and continue parsing after
11236 // heredoc_end.
11237 parser_flush_heredoc_end(parser);
11238 pm_token_buffer_copy(parser, &token_buffer);
11239 LEX(PM_TOKEN_STRING_CONTENT);
11240 } else {
11241 // ... else track the newline.
11242 pm_newline_list_append(&parser->newline_list, parser->current.end);
11243 }
11244
11245 parser->current.end++;
11246 break;
11247 default:
11248 if (peeked == lex_mode->as.list.incrementor || peeked == lex_mode->as.list.terminator) {
11249 pm_token_buffer_push_byte(&token_buffer, peeked);
11250 parser->current.end++;
11251 } else if (lex_mode->as.list.interpolation) {
11252 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
11253 } else {
11254 pm_token_buffer_push_byte(&token_buffer, '\\');
11255 pm_token_buffer_push_escaped(&token_buffer, parser);
11256 }
11257
11258 break;
11259 }
11260
11261 token_buffer.cursor = parser->current.end;
11262 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11263 continue;
11264 }
11265
11266 // If we hit a #, then we will attempt to lex interpolation.
11267 if (*breakpoint == '#') {
11268 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11269
11270 if (type == PM_TOKEN_NOT_PROVIDED) {
11271 // If we haven't returned at this point then we had something
11272 // that looked like an interpolated class or instance variable
11273 // like "#@" but wasn't actually. In this case we'll just skip
11274 // to the next breakpoint.
11275 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11276 continue;
11277 }
11278
11279 if (type == PM_TOKEN_STRING_CONTENT) {
11280 pm_token_buffer_flush(parser, &token_buffer);
11281 }
11282
11283 LEX(type);
11284 }
11285
11286 // If we've hit the incrementor, then we need to skip past it
11287 // and find the next breakpoint.
11288 assert(*breakpoint == lex_mode->as.list.incrementor);
11289 parser->current.end = breakpoint + 1;
11290 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11291 lex_mode->as.list.nesting++;
11292 continue;
11293 }
11294
11295 if (parser->current.end > parser->current.start) {
11296 pm_token_buffer_flush(parser, &token_buffer);
11297 LEX(PM_TOKEN_STRING_CONTENT);
11298 }
11299
11300 // If we were unable to find a breakpoint, then this token hits the
11301 // end of the file.
11302 parser->current.end = parser->end;
11303 pm_token_buffer_flush(parser, &token_buffer);
11304 LEX(PM_TOKEN_STRING_CONTENT);
11305 }
11306 case PM_LEX_REGEXP: {
11307 // First, we'll set to start of this token to be the current end.
11308 if (parser->next_start == NULL) {
11309 parser->current.start = parser->current.end;
11310 } else {
11311 parser->current.start = parser->next_start;
11312 parser->current.end = parser->next_start;
11313 parser->next_start = NULL;
11314 }
11315
11316 // We'll check if we're at the end of the file. If we are, then we
11317 // need to return the EOF token.
11318 if (parser->current.end >= parser->end) {
11319 LEX(PM_TOKEN_EOF);
11320 }
11321
11322 // Get a reference to the current mode.
11323 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11324
11325 // These are the places where we need to split up the content of the
11326 // regular expression. We'll use strpbrk to find the first of these
11327 // characters.
11328 const uint8_t *breakpoints = lex_mode->as.regexp.breakpoints;
11329 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11330 pm_regexp_token_buffer_t token_buffer = { 0 };
11331
11332 while (breakpoint != NULL) {
11333 uint8_t term = lex_mode->as.regexp.terminator;
11334 bool is_terminator = (*breakpoint == term);
11335
11336 // If the terminator is newline, we need to consider \r\n _also_ a newline
11337 // For example: `%\nfoo\r\n`
11338 // The string should be "foo", not "foo\r"
11339 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
11340 if (term == '\n') {
11341 is_terminator = true;
11342 }
11343
11344 // If the terminator is a CR, but we see a CRLF, we need to
11345 // treat the CRLF as a newline, meaning this is _not_ the
11346 // terminator
11347 if (term == '\r') {
11348 is_terminator = false;
11349 }
11350 }
11351
11352 // If we hit the terminator, we need to determine what kind of
11353 // token to return.
11354 if (is_terminator) {
11355 if (lex_mode->as.regexp.nesting > 0) {
11356 parser->current.end = breakpoint + 1;
11357 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11358 lex_mode->as.regexp.nesting--;
11359 continue;
11360 }
11361
11362 // Here we've hit the terminator. If we have already consumed
11363 // content then we need to return that content as string content
11364 // first.
11365 if (breakpoint > parser->current.start) {
11366 parser->current.end = breakpoint;
11367 pm_regexp_token_buffer_flush(parser, &token_buffer);
11368 LEX(PM_TOKEN_STRING_CONTENT);
11369 }
11370
11371 // Check here if we need to track the newline.
11372 size_t eol_length = match_eol_at(parser, breakpoint);
11373 if (eol_length) {
11374 parser->current.end = breakpoint + eol_length;
11375
11376 // Track the newline if we're not in a heredoc that
11377 // would have already have added the newline to the
11378 // list.
11379 if (parser->heredoc_end == NULL) {
11380 pm_newline_list_append(&parser->newline_list, parser->current.end - 1);
11381 }
11382 } else {
11383 parser->current.end = breakpoint + 1;
11384 }
11385
11386 // Since we've hit the terminator of the regular expression,
11387 // we now need to parse the options.
11388 parser->current.end += pm_strspn_regexp_option(parser->current.end, parser->end - parser->current.end);
11389
11390 lex_mode_pop(parser);
11391 lex_state_set(parser, PM_LEX_STATE_END);
11392 LEX(PM_TOKEN_REGEXP_END);
11393 }
11394
11395 // If we've hit the incrementor, then we need to skip past it
11396 // and find the next breakpoint.
11397 if (*breakpoint && *breakpoint == lex_mode->as.regexp.incrementor) {
11398 parser->current.end = breakpoint + 1;
11399 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11400 lex_mode->as.regexp.nesting++;
11401 continue;
11402 }
11403
11404 switch (*breakpoint) {
11405 case '\0':
11406 // If we hit a null byte, skip directly past it.
11407 parser->current.end = breakpoint + 1;
11408 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11409 break;
11410 case '\r':
11411 if (peek_at(parser, breakpoint + 1) != '\n') {
11412 parser->current.end = breakpoint + 1;
11413 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11414 break;
11415 }
11416
11417 breakpoint++;
11418 parser->current.end = breakpoint;
11419 pm_regexp_token_buffer_escape(parser, &token_buffer);
11420 token_buffer.base.cursor = breakpoint;
11421
11423 case '\n':
11424 // If we've hit a newline, then we need to track that in
11425 // the list of newlines.
11426 if (parser->heredoc_end == NULL) {
11427 pm_newline_list_append(&parser->newline_list, breakpoint);
11428 parser->current.end = breakpoint + 1;
11429 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11430 break;
11431 }
11432
11433 parser->current.end = breakpoint + 1;
11434 parser_flush_heredoc_end(parser);
11435 pm_regexp_token_buffer_flush(parser, &token_buffer);
11436 LEX(PM_TOKEN_STRING_CONTENT);
11437 case '\\': {
11438 // If we hit escapes, then we need to treat the next
11439 // token literally. In this case we'll skip past the
11440 // next character and find the next breakpoint.
11441 parser->current.end = breakpoint + 1;
11442
11443 // If we've hit the end of the file, then break out of
11444 // the loop by setting the breakpoint to NULL.
11445 if (parser->current.end == parser->end) {
11446 breakpoint = NULL;
11447 break;
11448 }
11449
11450 pm_regexp_token_buffer_escape(parser, &token_buffer);
11451 uint8_t peeked = peek(parser);
11452
11453 switch (peeked) {
11454 case '\r':
11455 parser->current.end++;
11456 if (peek(parser) != '\n') {
11457 if (lex_mode->as.regexp.terminator != '\r') {
11458 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11459 }
11460 pm_regexp_token_buffer_push_byte(&token_buffer, '\r');
11461 pm_token_buffer_push_byte(&token_buffer.base, '\r');
11462 break;
11463 }
11465 case '\n':
11466 if (parser->heredoc_end) {
11467 // ... if we are on the same line as a heredoc,
11468 // flush the heredoc and continue parsing after
11469 // heredoc_end.
11470 parser_flush_heredoc_end(parser);
11471 pm_regexp_token_buffer_copy(parser, &token_buffer);
11472 LEX(PM_TOKEN_STRING_CONTENT);
11473 } else {
11474 // ... else track the newline.
11475 pm_newline_list_append(&parser->newline_list, parser->current.end);
11476 }
11477
11478 parser->current.end++;
11479 break;
11480 case 'c':
11481 case 'C':
11482 case 'M':
11483 case 'u':
11484 case 'x':
11485 escape_read(parser, &token_buffer.regexp_buffer, &token_buffer.base.buffer, PM_ESCAPE_FLAG_REGEXP);
11486 break;
11487 default:
11488 if (lex_mode->as.regexp.terminator == peeked) {
11489 // Some characters when they are used as the
11490 // terminator also receive an escape. They are
11491 // enumerated here.
11492 switch (peeked) {
11493 case '$': case ')': case '*': case '+':
11494 case '.': case '>': case '?': case ']':
11495 case '^': case '|': case '}':
11496 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11497 break;
11498 default:
11499 break;
11500 }
11501
11502 pm_regexp_token_buffer_push_byte(&token_buffer, peeked);
11503 pm_token_buffer_push_byte(&token_buffer.base, peeked);
11504 parser->current.end++;
11505 break;
11506 }
11507
11508 if (peeked < 0x80) pm_token_buffer_push_byte(&token_buffer.base, '\\');
11509 pm_regexp_token_buffer_push_escaped(&token_buffer, parser);
11510 break;
11511 }
11512
11513 token_buffer.base.cursor = parser->current.end;
11514 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11515 break;
11516 }
11517 case '#': {
11518 // If we hit a #, then we will attempt to lex
11519 // interpolation.
11520 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11521
11522 if (type == PM_TOKEN_NOT_PROVIDED) {
11523 // If we haven't returned at this point then we had
11524 // something that looked like an interpolated class or
11525 // instance variable like "#@" but wasn't actually. In
11526 // this case we'll just skip to the next breakpoint.
11527 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11528 break;
11529 }
11530
11531 if (type == PM_TOKEN_STRING_CONTENT) {
11532 pm_regexp_token_buffer_flush(parser, &token_buffer);
11533 }
11534
11535 LEX(type);
11536 }
11537 default:
11538 assert(false && "unreachable");
11539 break;
11540 }
11541 }
11542
11543 if (parser->current.end > parser->current.start) {
11544 pm_regexp_token_buffer_flush(parser, &token_buffer);
11545 LEX(PM_TOKEN_STRING_CONTENT);
11546 }
11547
11548 // If we were unable to find a breakpoint, then this token hits the
11549 // end of the file.
11550 parser->current.end = parser->end;
11551 pm_regexp_token_buffer_flush(parser, &token_buffer);
11552 LEX(PM_TOKEN_STRING_CONTENT);
11553 }
11554 case PM_LEX_STRING: {
11555 // First, we'll set to start of this token to be the current end.
11556 if (parser->next_start == NULL) {
11557 parser->current.start = parser->current.end;
11558 } else {
11559 parser->current.start = parser->next_start;
11560 parser->current.end = parser->next_start;
11561 parser->next_start = NULL;
11562 }
11563
11564 // We'll check if we're at the end of the file. If we are, then we need to
11565 // return the EOF token.
11566 if (parser->current.end >= parser->end) {
11567 LEX(PM_TOKEN_EOF);
11568 }
11569
11570 // These are the places where we need to split up the content of the
11571 // string. We'll use strpbrk to find the first of these characters.
11572 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11573 const uint8_t *breakpoints = lex_mode->as.string.breakpoints;
11574 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11575
11576 // If we haven't found an escape yet, then this buffer will be
11577 // unallocated since we can refer directly to the source string.
11578 pm_token_buffer_t token_buffer = { 0 };
11579
11580 while (breakpoint != NULL) {
11581 // If we hit the incrementor, then we'll increment then nesting and
11582 // continue lexing.
11583 if (lex_mode->as.string.incrementor != '\0' && *breakpoint == lex_mode->as.string.incrementor) {
11584 lex_mode->as.string.nesting++;
11585 parser->current.end = breakpoint + 1;
11586 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11587 continue;
11588 }
11589
11590 uint8_t term = lex_mode->as.string.terminator;
11591 bool is_terminator = (*breakpoint == term);
11592
11593 // If the terminator is newline, we need to consider \r\n _also_ a newline
11594 // For example: `%r\nfoo\r\n`
11595 // The string should be /foo/, not /foo\r/
11596 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
11597 if (term == '\n') {
11598 is_terminator = true;
11599 }
11600
11601 // If the terminator is a CR, but we see a CRLF, we need to
11602 // treat the CRLF as a newline, meaning this is _not_ the
11603 // terminator
11604 if (term == '\r') {
11605 is_terminator = false;
11606 }
11607 }
11608
11609 // Note that we have to check the terminator here first because we could
11610 // potentially be parsing a % string that has a # character as the
11611 // terminator.
11612 if (is_terminator) {
11613 // If this terminator doesn't actually close the string, then we need
11614 // to continue on past it.
11615 if (lex_mode->as.string.nesting > 0) {
11616 parser->current.end = breakpoint + 1;
11617 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11618 lex_mode->as.string.nesting--;
11619 continue;
11620 }
11621
11622 // Here we've hit the terminator. If we have already consumed content
11623 // then we need to return that content as string content first.
11624 if (breakpoint > parser->current.start) {
11625 parser->current.end = breakpoint;
11626 pm_token_buffer_flush(parser, &token_buffer);
11627 LEX(PM_TOKEN_STRING_CONTENT);
11628 }
11629
11630 // Otherwise we need to switch back to the parent lex mode and
11631 // return the end of the string.
11632 size_t eol_length = match_eol_at(parser, breakpoint);
11633 if (eol_length) {
11634 parser->current.end = breakpoint + eol_length;
11635
11636 // Track the newline if we're not in a heredoc that
11637 // would have already have added the newline to the
11638 // list.
11639 if (parser->heredoc_end == NULL) {
11640 pm_newline_list_append(&parser->newline_list, parser->current.end - 1);
11641 }
11642 } else {
11643 parser->current.end = breakpoint + 1;
11644 }
11645
11646 if (lex_mode->as.string.label_allowed && (peek(parser) == ':') && (peek_offset(parser, 1) != ':')) {
11647 parser->current.end++;
11648 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
11649 lex_mode_pop(parser);
11650 LEX(PM_TOKEN_LABEL_END);
11651 }
11652
11653 // When the delimiter itself is a newline, we won't
11654 // get a chance to flush heredocs in the usual places since
11655 // the newline is already consumed.
11656 if (term == '\n' && parser->heredoc_end) {
11657 parser_flush_heredoc_end(parser);
11658 }
11659
11660 lex_state_set(parser, PM_LEX_STATE_END);
11661 lex_mode_pop(parser);
11662 LEX(PM_TOKEN_STRING_END);
11663 }
11664
11665 switch (*breakpoint) {
11666 case '\0':
11667 // Skip directly past the null character.
11668 parser->current.end = breakpoint + 1;
11669 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11670 break;
11671 case '\r':
11672 if (peek_at(parser, breakpoint + 1) != '\n') {
11673 parser->current.end = breakpoint + 1;
11674 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11675 break;
11676 }
11677
11678 // If we hit a \r\n sequence, then we need to treat it
11679 // as a newline.
11680 breakpoint++;
11681 parser->current.end = breakpoint;
11682 pm_token_buffer_escape(parser, &token_buffer);
11683 token_buffer.cursor = breakpoint;
11684
11686 case '\n':
11687 // When we hit a newline, we need to flush any potential
11688 // heredocs. Note that this has to happen after we check
11689 // for the terminator in case the terminator is a
11690 // newline character.
11691 if (parser->heredoc_end == NULL) {
11692 pm_newline_list_append(&parser->newline_list, breakpoint);
11693 parser->current.end = breakpoint + 1;
11694 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11695 break;
11696 }
11697
11698 parser->current.end = breakpoint + 1;
11699 parser_flush_heredoc_end(parser);
11700 pm_token_buffer_flush(parser, &token_buffer);
11701 LEX(PM_TOKEN_STRING_CONTENT);
11702 case '\\': {
11703 // Here we hit escapes.
11704 parser->current.end = breakpoint + 1;
11705
11706 // If we've hit the end of the file, then break out of
11707 // the loop by setting the breakpoint to NULL.
11708 if (parser->current.end == parser->end) {
11709 breakpoint = NULL;
11710 continue;
11711 }
11712
11713 pm_token_buffer_escape(parser, &token_buffer);
11714 uint8_t peeked = peek(parser);
11715
11716 switch (peeked) {
11717 case '\\':
11718 pm_token_buffer_push_byte(&token_buffer, '\\');
11719 parser->current.end++;
11720 break;
11721 case '\r':
11722 parser->current.end++;
11723 if (peek(parser) != '\n') {
11724 if (!lex_mode->as.string.interpolation) {
11725 pm_token_buffer_push_byte(&token_buffer, '\\');
11726 }
11727 pm_token_buffer_push_byte(&token_buffer, '\r');
11728 break;
11729 }
11731 case '\n':
11732 if (!lex_mode->as.string.interpolation) {
11733 pm_token_buffer_push_byte(&token_buffer, '\\');
11734 pm_token_buffer_push_byte(&token_buffer, '\n');
11735 }
11736
11737 if (parser->heredoc_end) {
11738 // ... if we are on the same line as a heredoc,
11739 // flush the heredoc and continue parsing after
11740 // heredoc_end.
11741 parser_flush_heredoc_end(parser);
11742 pm_token_buffer_copy(parser, &token_buffer);
11743 LEX(PM_TOKEN_STRING_CONTENT);
11744 } else {
11745 // ... else track the newline.
11746 pm_newline_list_append(&parser->newline_list, parser->current.end);
11747 }
11748
11749 parser->current.end++;
11750 break;
11751 default:
11752 if (lex_mode->as.string.incrementor != '\0' && peeked == lex_mode->as.string.incrementor) {
11753 pm_token_buffer_push_byte(&token_buffer, peeked);
11754 parser->current.end++;
11755 } else if (lex_mode->as.string.terminator != '\0' && peeked == lex_mode->as.string.terminator) {
11756 pm_token_buffer_push_byte(&token_buffer, peeked);
11757 parser->current.end++;
11758 } else if (lex_mode->as.string.interpolation) {
11759 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
11760 } else {
11761 pm_token_buffer_push_byte(&token_buffer, '\\');
11762 pm_token_buffer_push_escaped(&token_buffer, parser);
11763 }
11764
11765 break;
11766 }
11767
11768 token_buffer.cursor = parser->current.end;
11769 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11770 break;
11771 }
11772 case '#': {
11773 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11774
11775 if (type == PM_TOKEN_NOT_PROVIDED) {
11776 // If we haven't returned at this point then we had something that
11777 // looked like an interpolated class or instance variable like "#@"
11778 // but wasn't actually. In this case we'll just skip to the next
11779 // breakpoint.
11780 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11781 break;
11782 }
11783
11784 if (type == PM_TOKEN_STRING_CONTENT) {
11785 pm_token_buffer_flush(parser, &token_buffer);
11786 }
11787
11788 LEX(type);
11789 }
11790 default:
11791 assert(false && "unreachable");
11792 }
11793 }
11794
11795 if (parser->current.end > parser->current.start) {
11796 pm_token_buffer_flush(parser, &token_buffer);
11797 LEX(PM_TOKEN_STRING_CONTENT);
11798 }
11799
11800 // If we've hit the end of the string, then this is an unterminated
11801 // string. In that case we'll return a string content token.
11802 parser->current.end = parser->end;
11803 pm_token_buffer_flush(parser, &token_buffer);
11804 LEX(PM_TOKEN_STRING_CONTENT);
11805 }
11806 case PM_LEX_HEREDOC: {
11807 // First, we'll set to start of this token.
11808 if (parser->next_start == NULL) {
11809 parser->current.start = parser->current.end;
11810 } else {
11811 parser->current.start = parser->next_start;
11812 parser->current.end = parser->next_start;
11813 parser->heredoc_end = NULL;
11814 parser->next_start = NULL;
11815 }
11816
11817 // Now let's grab the information about the identifier off of the
11818 // current lex mode.
11819 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11820 pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base;
11821
11822 bool line_continuation = lex_mode->as.heredoc.line_continuation;
11823 lex_mode->as.heredoc.line_continuation = false;
11824
11825 // We'll check if we're at the end of the file. If we are, then we
11826 // will add an error (because we weren't able to find the
11827 // terminator) but still continue parsing so that content after the
11828 // declaration of the heredoc can be parsed.
11829 if (parser->current.end >= parser->end) {
11830 pm_parser_err_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length);
11831 parser->next_start = lex_mode->as.heredoc.next_start;
11832 parser->heredoc_end = parser->current.end;
11833 lex_state_set(parser, PM_LEX_STATE_END);
11834 lex_mode_pop(parser);
11835 LEX(PM_TOKEN_HEREDOC_END);
11836 }
11837
11838 const uint8_t *ident_start = heredoc_lex_mode->ident_start;
11839 size_t ident_length = heredoc_lex_mode->ident_length;
11840
11841 // If we are immediately following a newline and we have hit the
11842 // terminator, then we need to return the ending of the heredoc.
11843 if (current_token_starts_line(parser)) {
11844 const uint8_t *start = parser->current.start;
11845
11846 if (!line_continuation && (start + ident_length <= parser->end)) {
11847 const uint8_t *newline = next_newline(start, parser->end - start);
11848 const uint8_t *ident_end = newline;
11849 const uint8_t *terminator_end = newline;
11850
11851 if (newline == NULL) {
11852 terminator_end = parser->end;
11853 ident_end = parser->end;
11854 } else {
11855 terminator_end++;
11856 if (newline[-1] == '\r') {
11857 ident_end--; // Remove \r
11858 }
11859 }
11860
11861 const uint8_t *terminator_start = ident_end - ident_length;
11862 const uint8_t *cursor = start;
11863
11864 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
11865 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
11866 cursor++;
11867 }
11868 }
11869
11870 if (
11871 (cursor == terminator_start) &&
11872 (memcmp(terminator_start, ident_start, ident_length) == 0)
11873 ) {
11874 if (newline != NULL) {
11875 pm_newline_list_append(&parser->newline_list, newline);
11876 }
11877
11878 parser->current.end = terminator_end;
11879 if (*lex_mode->as.heredoc.next_start == '\\') {
11880 parser->next_start = NULL;
11881 } else {
11882 parser->next_start = lex_mode->as.heredoc.next_start;
11883 parser->heredoc_end = parser->current.end;
11884 }
11885
11886 lex_state_set(parser, PM_LEX_STATE_END);
11887 lex_mode_pop(parser);
11888 LEX(PM_TOKEN_HEREDOC_END);
11889 }
11890 }
11891
11892 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent);
11893 if (
11894 heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE &&
11895 lex_mode->as.heredoc.common_whitespace != NULL &&
11896 (*lex_mode->as.heredoc.common_whitespace > whitespace) &&
11897 peek_at(parser, start) != '\n'
11898 ) {
11899 *lex_mode->as.heredoc.common_whitespace = whitespace;
11900 }
11901 }
11902
11903 // Otherwise we'll be parsing string content. These are the places
11904 // where we need to split up the content of the heredoc. We'll use
11905 // strpbrk to find the first of these characters.
11906 uint8_t breakpoints[] = "\r\n\\#";
11907
11908 pm_heredoc_quote_t quote = heredoc_lex_mode->quote;
11909 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
11910 breakpoints[3] = '\0';
11911 }
11912
11913 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11914 pm_token_buffer_t token_buffer = { 0 };
11915 bool was_line_continuation = false;
11916
11917 while (breakpoint != NULL) {
11918 switch (*breakpoint) {
11919 case '\0':
11920 // Skip directly past the null character.
11921 parser->current.end = breakpoint + 1;
11922 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11923 break;
11924 case '\r':
11925 parser->current.end = breakpoint + 1;
11926
11927 if (peek_at(parser, breakpoint + 1) != '\n') {
11928 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11929 break;
11930 }
11931
11932 // If we hit a \r\n sequence, then we want to replace it
11933 // with a single \n character in the final string.
11934 breakpoint++;
11935 pm_token_buffer_escape(parser, &token_buffer);
11936 token_buffer.cursor = breakpoint;
11937
11939 case '\n': {
11940 if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) {
11941 parser_flush_heredoc_end(parser);
11942 parser->current.end = breakpoint + 1;
11943 pm_token_buffer_flush(parser, &token_buffer);
11944 LEX(PM_TOKEN_STRING_CONTENT);
11945 }
11946
11947 pm_newline_list_append(&parser->newline_list, breakpoint);
11948
11949 // If we have a - or ~ heredoc, then we can match after
11950 // some leading whitespace.
11951 const uint8_t *start = breakpoint + 1;
11952
11953 if (!was_line_continuation && (start + ident_length <= parser->end)) {
11954 // We want to match the terminator starting from the end of the line in case
11955 // there is whitespace in the ident such as <<-' DOC' or <<~' DOC'.
11956 const uint8_t *newline = next_newline(start, parser->end - start);
11957
11958 if (newline == NULL) {
11959 newline = parser->end;
11960 } else if (newline[-1] == '\r') {
11961 newline--; // Remove \r
11962 }
11963
11964 // Start of a possible terminator.
11965 const uint8_t *terminator_start = newline - ident_length;
11966
11967 // Cursor to check for the leading whitespace. We skip the
11968 // leading whitespace if we have a - or ~ heredoc.
11969 const uint8_t *cursor = start;
11970
11971 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
11972 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
11973 cursor++;
11974 }
11975 }
11976
11977 if (
11978 cursor == terminator_start &&
11979 (memcmp(terminator_start, ident_start, ident_length) == 0)
11980 ) {
11981 parser->current.end = breakpoint + 1;
11982 pm_token_buffer_flush(parser, &token_buffer);
11983 LEX(PM_TOKEN_STRING_CONTENT);
11984 }
11985 }
11986
11987 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.base.indent);
11988
11989 // If we have hit a newline that is followed by a valid
11990 // terminator, then we need to return the content of the
11991 // heredoc here as string content. Then, the next time a
11992 // token is lexed, it will match again and return the
11993 // end of the heredoc.
11994 if (lex_mode->as.heredoc.base.indent == PM_HEREDOC_INDENT_TILDE) {
11995 if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
11996 *lex_mode->as.heredoc.common_whitespace = whitespace;
11997 }
11998
11999 parser->current.end = breakpoint + 1;
12000 pm_token_buffer_flush(parser, &token_buffer);
12001 LEX(PM_TOKEN_STRING_CONTENT);
12002 }
12003
12004 // Otherwise we hit a newline and it wasn't followed by
12005 // a terminator, so we can continue parsing.
12006 parser->current.end = breakpoint + 1;
12007 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12008 break;
12009 }
12010 case '\\': {
12011 // If we hit an escape, then we need to skip past
12012 // however many characters the escape takes up. However
12013 // it's important that if \n or \r\n are escaped, we
12014 // stop looping before the newline and not after the
12015 // newline so that we can still potentially find the
12016 // terminator of the heredoc.
12017 parser->current.end = breakpoint + 1;
12018
12019 // If we've hit the end of the file, then break out of
12020 // the loop by setting the breakpoint to NULL.
12021 if (parser->current.end == parser->end) {
12022 breakpoint = NULL;
12023 continue;
12024 }
12025
12026 pm_token_buffer_escape(parser, &token_buffer);
12027 uint8_t peeked = peek(parser);
12028
12029 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12030 switch (peeked) {
12031 case '\r':
12032 parser->current.end++;
12033 if (peek(parser) != '\n') {
12034 pm_token_buffer_push_byte(&token_buffer, '\\');
12035 pm_token_buffer_push_byte(&token_buffer, '\r');
12036 break;
12037 }
12039 case '\n':
12040 pm_token_buffer_push_byte(&token_buffer, '\\');
12041 pm_token_buffer_push_byte(&token_buffer, '\n');
12042 token_buffer.cursor = parser->current.end + 1;
12043 breakpoint = parser->current.end;
12044 continue;
12045 default:
12046 pm_token_buffer_push_byte(&token_buffer, '\\');
12047 pm_token_buffer_push_escaped(&token_buffer, parser);
12048 break;
12049 }
12050 } else {
12051 switch (peeked) {
12052 case '\r':
12053 parser->current.end++;
12054 if (peek(parser) != '\n') {
12055 pm_token_buffer_push_byte(&token_buffer, '\r');
12056 break;
12057 }
12059 case '\n':
12060 // If we are in a tilde here, we should
12061 // break out of the loop and return the
12062 // string content.
12063 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12064 const uint8_t *end = parser->current.end;
12065
12066 if (parser->heredoc_end == NULL) {
12067 pm_newline_list_append(&parser->newline_list, end);
12068 }
12069
12070 // Here we want the buffer to only
12071 // include up to the backslash.
12072 parser->current.end = breakpoint;
12073 pm_token_buffer_flush(parser, &token_buffer);
12074
12075 // Now we can advance the end of the
12076 // token past the newline.
12077 parser->current.end = end + 1;
12078 lex_mode->as.heredoc.line_continuation = true;
12079 LEX(PM_TOKEN_STRING_CONTENT);
12080 }
12081
12082 was_line_continuation = true;
12083 token_buffer.cursor = parser->current.end + 1;
12084 breakpoint = parser->current.end;
12085 continue;
12086 default:
12087 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12088 break;
12089 }
12090 }
12091
12092 token_buffer.cursor = parser->current.end;
12093 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12094 break;
12095 }
12096 case '#': {
12097 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12098
12099 if (type == PM_TOKEN_NOT_PROVIDED) {
12100 // If we haven't returned at this point then we had
12101 // something that looked like an interpolated class
12102 // or instance variable like "#@" but wasn't
12103 // actually. In this case we'll just skip to the
12104 // next breakpoint.
12105 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12106 break;
12107 }
12108
12109 if (type == PM_TOKEN_STRING_CONTENT) {
12110 pm_token_buffer_flush(parser, &token_buffer);
12111 }
12112
12113 LEX(type);
12114 }
12115 default:
12116 assert(false && "unreachable");
12117 }
12118
12119 was_line_continuation = false;
12120 }
12121
12122 if (parser->current.end > parser->current.start) {
12123 parser->current.end = parser->end;
12124 pm_token_buffer_flush(parser, &token_buffer);
12125 LEX(PM_TOKEN_STRING_CONTENT);
12126 }
12127
12128 // If we've hit the end of the string, then this is an unterminated
12129 // heredoc. In that case we'll return a string content token.
12130 parser->current.end = parser->end;
12131 pm_token_buffer_flush(parser, &token_buffer);
12132 LEX(PM_TOKEN_STRING_CONTENT);
12133 }
12134 }
12135
12136 assert(false && "unreachable");
12137}
12138
12139#undef LEX
12140
12141/******************************************************************************/
12142/* Parse functions */
12143/******************************************************************************/
12144
12153typedef enum {
12154 PM_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator
12155 PM_BINDING_POWER_STATEMENT = 2,
12156 PM_BINDING_POWER_MODIFIER_RESCUE = 4, // rescue
12157 PM_BINDING_POWER_MODIFIER = 6, // if unless until while
12158 PM_BINDING_POWER_COMPOSITION = 8, // and or
12159 PM_BINDING_POWER_NOT = 10, // not
12160 PM_BINDING_POWER_MATCH = 12, // => in
12161 PM_BINDING_POWER_DEFINED = 14, // defined?
12162 PM_BINDING_POWER_MULTI_ASSIGNMENT = 16, // =
12163 PM_BINDING_POWER_ASSIGNMENT = 18, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **=
12164 PM_BINDING_POWER_TERNARY = 20, // ?:
12165 PM_BINDING_POWER_RANGE = 22, // .. ...
12166 PM_BINDING_POWER_LOGICAL_OR = 24, // ||
12167 PM_BINDING_POWER_LOGICAL_AND = 26, // &&
12168 PM_BINDING_POWER_EQUALITY = 28, // <=> == === != =~ !~
12169 PM_BINDING_POWER_COMPARISON = 30, // > >= < <=
12170 PM_BINDING_POWER_BITWISE_OR = 32, // | ^
12171 PM_BINDING_POWER_BITWISE_AND = 34, // &
12172 PM_BINDING_POWER_SHIFT = 36, // << >>
12173 PM_BINDING_POWER_TERM = 38, // + -
12174 PM_BINDING_POWER_FACTOR = 40, // * / %
12175 PM_BINDING_POWER_UMINUS = 42, // -@
12176 PM_BINDING_POWER_EXPONENT = 44, // **
12177 PM_BINDING_POWER_UNARY = 46, // ! ~ +@
12178 PM_BINDING_POWER_INDEX = 48, // [] []=
12179 PM_BINDING_POWER_CALL = 50, // :: .
12180 PM_BINDING_POWER_MAX = 52
12181} pm_binding_power_t;
12182
12187typedef struct {
12189 pm_binding_power_t left;
12190
12192 pm_binding_power_t right;
12193
12196
12203
12204#define BINDING_POWER_ASSIGNMENT { PM_BINDING_POWER_UNARY, PM_BINDING_POWER_ASSIGNMENT, true, false }
12205#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, false }
12206#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true, false }
12207#define NON_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, true }
12208#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false }
12209
12210pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
12211 // rescue
12212 [PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = { PM_BINDING_POWER_MODIFIER_RESCUE, PM_BINDING_POWER_COMPOSITION, true, false },
12213
12214 // if unless until while
12215 [PM_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12216 [PM_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12217 [PM_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12218 [PM_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12219
12220 // and or
12221 [PM_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12222 [PM_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12223
12224 // => in
12225 [PM_TOKEN_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12226 [PM_TOKEN_KEYWORD_IN] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12227
12228 // &&= &= ^= = >>= <<= -= %= |= ||= += /= *= **=
12229 [PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12230 [PM_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12231 [PM_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT,
12232 [PM_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT,
12233 [PM_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT,
12234 [PM_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12235 [PM_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12236 [PM_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT,
12237 [PM_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12238 [PM_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12239 [PM_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12240 [PM_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT,
12241 [PM_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12242 [PM_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12243
12244 // ?:
12245 [PM_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_TERNARY),
12246
12247 // .. ...
12248 [PM_TOKEN_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12249 [PM_TOKEN_DOT_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12250 [PM_TOKEN_UDOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12251 [PM_TOKEN_UDOT_DOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12252
12253 // ||
12254 [PM_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_OR),
12255
12256 // &&
12257 [PM_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_AND),
12258
12259 // != !~ == === =~ <=>
12260 [PM_TOKEN_BANG_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12261 [PM_TOKEN_BANG_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12262 [PM_TOKEN_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12263 [PM_TOKEN_EQUAL_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12264 [PM_TOKEN_EQUAL_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12265 [PM_TOKEN_LESS_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12266
12267 // > >= < <=
12268 [PM_TOKEN_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12269 [PM_TOKEN_GREATER_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12270 [PM_TOKEN_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12271 [PM_TOKEN_LESS_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12272
12273 // ^ |
12274 [PM_TOKEN_CARET] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12275 [PM_TOKEN_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12276
12277 // &
12278 [PM_TOKEN_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_AND),
12279
12280 // >> <<
12281 [PM_TOKEN_GREATER_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12282 [PM_TOKEN_LESS_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12283
12284 // - +
12285 [PM_TOKEN_MINUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12286 [PM_TOKEN_PLUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12287
12288 // % / *
12289 [PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12290 [PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12291 [PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12292 [PM_TOKEN_USTAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_FACTOR),
12293
12294 // -@
12295 [PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS),
12296 [PM_TOKEN_UMINUS_NUM] = { PM_BINDING_POWER_UMINUS, PM_BINDING_POWER_MAX, false, false },
12297
12298 // **
12299 [PM_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_EXPONENT),
12300 [PM_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12301
12302 // ! ~ +@
12303 [PM_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12304 [PM_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12305 [PM_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12306
12307 // [
12308 [PM_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_INDEX),
12309
12310 // :: . &.
12311 [PM_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12312 [PM_TOKEN_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12313 [PM_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL)
12314};
12315
12316#undef BINDING_POWER_ASSIGNMENT
12317#undef LEFT_ASSOCIATIVE
12318#undef RIGHT_ASSOCIATIVE
12319#undef RIGHT_ASSOCIATIVE_UNARY
12320
12324static inline bool
12325match1(const pm_parser_t *parser, pm_token_type_t type) {
12326 return parser->current.type == type;
12327}
12328
12332static inline bool
12333match2(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12334 return match1(parser, type1) || match1(parser, type2);
12335}
12336
12340static inline bool
12341match3(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) {
12342 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3);
12343}
12344
12348static inline bool
12349match4(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4) {
12350 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4);
12351}
12352
12356static inline bool
12357match7(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6, pm_token_type_t type7) {
12358 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6) || match1(parser, type7);
12359}
12360
12364static inline bool
12365match8(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6, pm_token_type_t type7, pm_token_type_t type8) {
12366 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6) || match1(parser, type7) || match1(parser, type8);
12367}
12368
12375static bool
12376accept1(pm_parser_t *parser, pm_token_type_t type) {
12377 if (match1(parser, type)) {
12378 parser_lex(parser);
12379 return true;
12380 }
12381 return false;
12382}
12383
12388static inline bool
12389accept2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12390 if (match2(parser, type1, type2)) {
12391 parser_lex(parser);
12392 return true;
12393 }
12394 return false;
12395}
12396
12408static void
12409expect1(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id) {
12410 if (accept1(parser, type)) return;
12411
12412 const uint8_t *location = parser->previous.end;
12413 pm_parser_err(parser, location, location, diag_id);
12414
12415 parser->previous.start = location;
12416 parser->previous.type = PM_TOKEN_MISSING;
12417}
12418
12423static void
12424expect2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_diagnostic_id_t diag_id) {
12425 if (accept2(parser, type1, type2)) return;
12426
12427 const uint8_t *location = parser->previous.end;
12428 pm_parser_err(parser, location, location, diag_id);
12429
12430 parser->previous.start = location;
12431 parser->previous.type = PM_TOKEN_MISSING;
12432}
12433
12438static void
12439expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
12440 if (match1(parser, PM_TOKEN_HEREDOC_END)) {
12441 parser_lex(parser);
12442 } else {
12443 pm_parser_err_heredoc_term(parser, ident_start, ident_length);
12444 parser->previous.start = parser->previous.end;
12445 parser->previous.type = PM_TOKEN_MISSING;
12446 }
12447}
12448
12455static void
12456expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id, const pm_token_t *opening) {
12457 if (accept1(parser, type)) return;
12458
12459 pm_parser_err(parser, opening->start, opening->end, diag_id);
12460
12461 parser->previous.start = opening->end;
12462 parser->previous.type = PM_TOKEN_MISSING;
12463}
12464
12465static pm_node_t *
12466parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth);
12467
12472static pm_node_t *
12473parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) {
12474 pm_node_t *node = parse_expression(parser, binding_power, accepts_command_call, accepts_label, diag_id, depth);
12475 pm_assert_value_expression(parser, node);
12476 return node;
12477}
12478
12497static inline bool
12498token_begins_expression_p(pm_token_type_t type) {
12499 switch (type) {
12500 case PM_TOKEN_EQUAL_GREATER:
12501 case PM_TOKEN_KEYWORD_IN:
12502 // We need to special case this because it is a binary operator that
12503 // should not be marked as beginning an expression.
12504 return false;
12505 case PM_TOKEN_BRACE_RIGHT:
12506 case PM_TOKEN_BRACKET_RIGHT:
12507 case PM_TOKEN_COLON:
12508 case PM_TOKEN_COMMA:
12509 case PM_TOKEN_EMBEXPR_END:
12510 case PM_TOKEN_EOF:
12511 case PM_TOKEN_LAMBDA_BEGIN:
12512 case PM_TOKEN_KEYWORD_DO:
12513 case PM_TOKEN_KEYWORD_DO_LOOP:
12514 case PM_TOKEN_KEYWORD_END:
12515 case PM_TOKEN_KEYWORD_ELSE:
12516 case PM_TOKEN_KEYWORD_ELSIF:
12517 case PM_TOKEN_KEYWORD_ENSURE:
12518 case PM_TOKEN_KEYWORD_THEN:
12519 case PM_TOKEN_KEYWORD_RESCUE:
12520 case PM_TOKEN_KEYWORD_WHEN:
12521 case PM_TOKEN_NEWLINE:
12522 case PM_TOKEN_PARENTHESIS_RIGHT:
12523 case PM_TOKEN_SEMICOLON:
12524 // The reason we need this short-circuit is because we're using the
12525 // binding powers table to tell us if the subsequent token could
12526 // potentially be the start of an expression. If there _is_ a binding
12527 // power for one of these tokens, then we should remove it from this list
12528 // and let it be handled by the default case below.
12529 assert(pm_binding_powers[type].left == PM_BINDING_POWER_UNSET);
12530 return false;
12531 case PM_TOKEN_UAMPERSAND:
12532 // This is a special case because this unary operator cannot appear
12533 // as a general operator, it only appears in certain circumstances.
12534 return false;
12535 case PM_TOKEN_UCOLON_COLON:
12536 case PM_TOKEN_UMINUS:
12537 case PM_TOKEN_UMINUS_NUM:
12538 case PM_TOKEN_UPLUS:
12539 case PM_TOKEN_BANG:
12540 case PM_TOKEN_TILDE:
12541 case PM_TOKEN_UDOT_DOT:
12542 case PM_TOKEN_UDOT_DOT_DOT:
12543 // These unary tokens actually do have binding power associated with them
12544 // so that we can correctly place them into the precedence order. But we
12545 // want them to be marked as beginning an expression, so we need to
12546 // special case them here.
12547 return true;
12548 default:
12549 return pm_binding_powers[type].left == PM_BINDING_POWER_UNSET;
12550 }
12551}
12552
12557static pm_node_t *
12558parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id, uint16_t depth) {
12559 if (accept1(parser, PM_TOKEN_USTAR)) {
12560 pm_token_t operator = parser->previous;
12561 pm_node_t *expression = parse_value_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
12562 return UP(pm_splat_node_create(parser, &operator, expression));
12563 }
12564
12565 return parse_value_expression(parser, binding_power, accepts_command_call, false, diag_id, depth);
12566}
12567
12568static bool
12569pm_node_unreference_each(const pm_node_t *node, void *data) {
12570 switch (PM_NODE_TYPE(node)) {
12571 /* When we are about to destroy a set of nodes that could potentially
12572 * contain block exits for the current scope, we need to check if they
12573 * are contained in the list of block exits and remove them if they are.
12574 */
12575 case PM_BREAK_NODE:
12576 case PM_NEXT_NODE:
12577 case PM_REDO_NODE: {
12578 pm_parser_t *parser = (pm_parser_t *) data;
12579 size_t index = 0;
12580
12581 while (index < parser->current_block_exits->size) {
12582 pm_node_t *block_exit = parser->current_block_exits->nodes[index];
12583
12584 if (block_exit == node) {
12585 if (index + 1 < parser->current_block_exits->size) {
12586 memmove(
12587 &parser->current_block_exits->nodes[index],
12588 &parser->current_block_exits->nodes[index + 1],
12589 (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *)
12590 );
12591 }
12592 parser->current_block_exits->size--;
12593
12594 /* Note returning true here because these nodes could have
12595 * arguments that are themselves block exits. */
12596 return true;
12597 }
12598
12599 index++;
12600 }
12601
12602 return true;
12603 }
12604 /* When an implicit local variable is written to or targeted, it becomes
12605 * a regular, named local variable. This branch removes it from the list
12606 * of implicit parameters when that happens. */
12607 case PM_LOCAL_VARIABLE_READ_NODE:
12608 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
12609 pm_parser_t *parser = (pm_parser_t *) data;
12610 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
12611
12612 for (size_t index = 0; index < implicit_parameters->size; index++) {
12613 if (implicit_parameters->nodes[index] == node) {
12614 /* If the node is not the last one in the list, we need to
12615 * shift the remaining nodes down to fill the gap. This is
12616 * extremely unlikely to happen. */
12617 if (index != implicit_parameters->size - 1) {
12618 memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *));
12619 }
12620
12621 implicit_parameters->size--;
12622 break;
12623 }
12624 }
12625
12626 return false;
12627 }
12628 default:
12629 return true;
12630 }
12631}
12632
12638static void
12639pm_node_unreference(pm_parser_t *parser, const pm_node_t *node) {
12640 pm_visit_node(node, pm_node_unreference_each, parser);
12641}
12642
12647static void
12648parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) {
12649 // The method name needs to change. If we previously had
12650 // foo, we now need foo=. In this case we'll allocate a new
12651 // owned string, copy the previous method name in, and
12652 // append an =.
12653 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *name_field);
12654 size_t length = constant->length;
12655 uint8_t *name = xcalloc(length + 1, sizeof(uint8_t));
12656 if (name == NULL) return;
12657
12658 memcpy(name, constant->start, length);
12659 name[length] = '=';
12660
12661 // Now switch the name to the new string.
12662 // This silences clang analyzer warning about leak of memory pointed by `name`.
12663 // NOLINTNEXTLINE(clang-analyzer-*)
12664 *name_field = pm_constant_pool_insert_owned(&parser->constant_pool, name, length + 1);
12665}
12666
12673static pm_node_t *
12674parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) {
12675 switch (PM_NODE_TYPE(target)) {
12676 case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
12677 case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
12678 case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
12679 case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
12680 case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
12681 case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
12682 case PM_TRUE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
12683 default: break;
12684 }
12685
12686 pm_constant_id_t name = pm_parser_constant_id_location(parser, target->location.start, target->location.end);
12687 pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0);
12688
12689 pm_node_destroy(parser, target);
12690 return UP(result);
12691}
12692
12701static pm_node_t *
12702parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_parent) {
12703 switch (PM_NODE_TYPE(target)) {
12704 case PM_MISSING_NODE:
12705 return target;
12706 case PM_SOURCE_ENCODING_NODE:
12707 case PM_FALSE_NODE:
12708 case PM_SOURCE_FILE_NODE:
12709 case PM_SOURCE_LINE_NODE:
12710 case PM_NIL_NODE:
12711 case PM_SELF_NODE:
12712 case PM_TRUE_NODE: {
12713 // In these special cases, we have specific error messages and we
12714 // will replace them with local variable writes.
12715 return parse_unwriteable_target(parser, target);
12716 }
12717 case PM_CLASS_VARIABLE_READ_NODE:
12718 assert(sizeof(pm_class_variable_target_node_t) == sizeof(pm_class_variable_read_node_t));
12719 target->type = PM_CLASS_VARIABLE_TARGET_NODE;
12720 return target;
12721 case PM_CONSTANT_PATH_NODE:
12722 if (context_def_p(parser)) {
12723 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
12724 }
12725
12726 assert(sizeof(pm_constant_path_target_node_t) == sizeof(pm_constant_path_node_t));
12727 target->type = PM_CONSTANT_PATH_TARGET_NODE;
12728
12729 return target;
12730 case PM_CONSTANT_READ_NODE:
12731 if (context_def_p(parser)) {
12732 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
12733 }
12734
12735 assert(sizeof(pm_constant_target_node_t) == sizeof(pm_constant_read_node_t));
12736 target->type = PM_CONSTANT_TARGET_NODE;
12737
12738 return target;
12739 case PM_BACK_REFERENCE_READ_NODE:
12740 case PM_NUMBERED_REFERENCE_READ_NODE:
12741 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
12742 return target;
12743 case PM_GLOBAL_VARIABLE_READ_NODE:
12744 assert(sizeof(pm_global_variable_target_node_t) == sizeof(pm_global_variable_read_node_t));
12745 target->type = PM_GLOBAL_VARIABLE_TARGET_NODE;
12746 return target;
12747 case PM_LOCAL_VARIABLE_READ_NODE: {
12748 if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) {
12749 PM_PARSER_ERR_FORMAT(parser, target->location.start, target->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, target->location.start);
12750 pm_node_unreference(parser, target);
12751 }
12752
12753 const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target;
12754 uint32_t name = cast->name;
12755 uint32_t depth = cast->depth;
12756 pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name);
12757
12758 assert(sizeof(pm_local_variable_target_node_t) == sizeof(pm_local_variable_read_node_t));
12759 target->type = PM_LOCAL_VARIABLE_TARGET_NODE;
12760
12761 return target;
12762 }
12763 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
12764 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
12765 pm_node_t *node = UP(pm_local_variable_target_node_create(parser, &target->location, name, 0));
12766
12767 pm_node_unreference(parser, target);
12768 pm_node_destroy(parser, target);
12769
12770 return node;
12771 }
12772 case PM_INSTANCE_VARIABLE_READ_NODE:
12773 assert(sizeof(pm_instance_variable_target_node_t) == sizeof(pm_instance_variable_read_node_t));
12774 target->type = PM_INSTANCE_VARIABLE_TARGET_NODE;
12775 return target;
12776 case PM_MULTI_TARGET_NODE:
12777 if (splat_parent) {
12778 // Multi target is not accepted in all positions. If this is one
12779 // of them, then we need to add an error.
12780 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
12781 }
12782
12783 return target;
12784 case PM_SPLAT_NODE: {
12785 pm_splat_node_t *splat = (pm_splat_node_t *) target;
12786
12787 if (splat->expression != NULL) {
12788 splat->expression = parse_target(parser, splat->expression, multiple, true);
12789 }
12790
12791 return UP(splat);
12792 }
12793 case PM_CALL_NODE: {
12794 pm_call_node_t *call = (pm_call_node_t *) target;
12795
12796 // If we have no arguments to the call node and we need this to be a
12797 // target then this is either a method call or a local variable
12798 // write.
12799 if (
12800 (call->message_loc.start != NULL) &&
12801 (call->message_loc.end[-1] != '!') &&
12802 (call->message_loc.end[-1] != '?') &&
12803 (call->opening_loc.start == NULL) &&
12804 (call->arguments == NULL) &&
12805 (call->block == NULL)
12806 ) {
12807 if (call->receiver == NULL) {
12808 // When we get here, we have a local variable write, because it
12809 // was previously marked as a method call but now we have an =.
12810 // This looks like:
12811 //
12812 // foo = 1
12813 //
12814 // When it was parsed in the prefix position, foo was seen as a
12815 // method call with no receiver and no arguments. Now we have an
12816 // =, so we know it's a local variable write.
12817 const pm_location_t message_loc = call->message_loc;
12818
12819 pm_constant_id_t name = pm_parser_local_add_location(parser, message_loc.start, message_loc.end, 0);
12820 pm_node_destroy(parser, target);
12821
12822 return UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0));
12823 }
12824
12825 if (peek_at(parser, call->message_loc.start) == '_' || parser->encoding->alnum_char(call->message_loc.start, call->message_loc.end - call->message_loc.start)) {
12826 if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
12827 pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION);
12828 }
12829
12830 parse_write_name(parser, &call->name);
12831 return UP(pm_call_target_node_create(parser, call));
12832 }
12833 }
12834
12835 // If there is no call operator and the message is "[]" then this is
12836 // an aref expression, and we can transform it into an aset
12837 // expression.
12838 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
12839 return UP(pm_index_target_node_create(parser, call));
12840 }
12841 }
12843 default:
12844 // In this case we have a node that we don't know how to convert
12845 // into a target. We need to treat it as an error. For now, we'll
12846 // mark it as an error and just skip right past it.
12847 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
12848 return target;
12849 }
12850}
12851
12856static pm_node_t *
12857parse_target_validate(pm_parser_t *parser, pm_node_t *target, bool multiple) {
12858 pm_node_t *result = parse_target(parser, target, multiple, false);
12859
12860 // Ensure that we have one of an =, an 'in' in for indexes, and a ')' in
12861 // parens after the targets.
12862 if (
12863 !match1(parser, PM_TOKEN_EQUAL) &&
12864 !(context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) &&
12865 !(context_p(parser, PM_CONTEXT_PARENS) && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT))
12866 ) {
12867 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
12868 }
12869
12870 return result;
12871}
12872
12877static pm_node_t *
12878parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) {
12879 pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser);
12880
12881 if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) {
12882 return UP(pm_shareable_constant_node_create(parser, write, shareable_constant));
12883 }
12884
12885 return write;
12886}
12887
12891static pm_node_t *
12892parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_node_t *value) {
12893 switch (PM_NODE_TYPE(target)) {
12894 case PM_MISSING_NODE:
12895 pm_node_destroy(parser, value);
12896 return target;
12897 case PM_CLASS_VARIABLE_READ_NODE: {
12898 pm_class_variable_write_node_t *node = pm_class_variable_write_node_create(parser, (pm_class_variable_read_node_t *) target, operator, value);
12899 pm_node_destroy(parser, target);
12900 return UP(node);
12901 }
12902 case PM_CONSTANT_PATH_NODE: {
12903 pm_node_t *node = UP(pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value));
12904
12905 if (context_def_p(parser)) {
12906 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
12907 }
12908
12909 return parse_shareable_constant_write(parser, node);
12910 }
12911 case PM_CONSTANT_READ_NODE: {
12912 pm_node_t *node = UP(pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value));
12913
12914 if (context_def_p(parser)) {
12915 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
12916 }
12917
12918 pm_node_destroy(parser, target);
12919 return parse_shareable_constant_write(parser, node);
12920 }
12921 case PM_BACK_REFERENCE_READ_NODE:
12922 case PM_NUMBERED_REFERENCE_READ_NODE:
12923 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
12925 case PM_GLOBAL_VARIABLE_READ_NODE: {
12926 pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value);
12927 pm_node_destroy(parser, target);
12928 return UP(node);
12929 }
12930 case PM_LOCAL_VARIABLE_READ_NODE: {
12931 pm_local_variable_read_node_t *local_read = (pm_local_variable_read_node_t *) target;
12932
12933 pm_constant_id_t name = local_read->name;
12934 pm_location_t name_loc = target->location;
12935
12936 uint32_t depth = local_read->depth;
12937 pm_scope_t *scope = pm_parser_scope_find(parser, depth);
12938
12939 if (pm_token_is_numbered_parameter(target->location.start, target->location.end)) {
12940 pm_diagnostic_id_t diag_id = (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED;
12941 PM_PARSER_ERR_FORMAT(parser, target->location.start, target->location.end, diag_id, target->location.start);
12942 pm_node_unreference(parser, target);
12943 }
12944
12945 pm_locals_unread(&scope->locals, name);
12946 pm_node_destroy(parser, target);
12947
12948 return UP(pm_local_variable_write_node_create(parser, name, depth, value, &name_loc, operator));
12949 }
12950 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
12951 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
12952 pm_node_t *node = UP(pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator));
12953
12954 pm_node_unreference(parser, target);
12955 pm_node_destroy(parser, target);
12956
12957 return node;
12958 }
12959 case PM_INSTANCE_VARIABLE_READ_NODE: {
12960 pm_node_t *write_node = UP(pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value));
12961 pm_node_destroy(parser, target);
12962 return write_node;
12963 }
12964 case PM_MULTI_TARGET_NODE:
12965 return UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value));
12966 case PM_SPLAT_NODE: {
12967 pm_splat_node_t *splat = (pm_splat_node_t *) target;
12968
12969 if (splat->expression != NULL) {
12970 splat->expression = parse_write(parser, splat->expression, operator, value);
12971 }
12972
12973 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
12974 pm_multi_target_node_targets_append(parser, multi_target, UP(splat));
12975
12976 return UP(pm_multi_write_node_create(parser, multi_target, operator, value));
12977 }
12978 case PM_CALL_NODE: {
12979 pm_call_node_t *call = (pm_call_node_t *) target;
12980
12981 // If we have no arguments to the call node and we need this to be a
12982 // target then this is either a method call or a local variable
12983 // write.
12984 if (
12985 (call->message_loc.start != NULL) &&
12986 (call->message_loc.end[-1] != '!') &&
12987 (call->message_loc.end[-1] != '?') &&
12988 (call->opening_loc.start == NULL) &&
12989 (call->arguments == NULL) &&
12990 (call->block == NULL)
12991 ) {
12992 if (call->receiver == NULL) {
12993 // When we get here, we have a local variable write, because it
12994 // was previously marked as a method call but now we have an =.
12995 // This looks like:
12996 //
12997 // foo = 1
12998 //
12999 // When it was parsed in the prefix position, foo was seen as a
13000 // method call with no receiver and no arguments. Now we have an
13001 // =, so we know it's a local variable write.
13002 const pm_location_t message = call->message_loc;
13003
13004 pm_parser_local_add_location(parser, message.start, message.end, 0);
13005 pm_node_destroy(parser, target);
13006
13007 pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, message.start, message.end);
13008 target = UP(pm_local_variable_write_node_create(parser, constant_id, 0, value, &message, operator));
13009
13010 pm_refute_numbered_parameter(parser, message.start, message.end);
13011 return target;
13012 }
13013
13014 if (char_is_identifier_start(parser, call->message_loc.start, parser->end - call->message_loc.start)) {
13015 // When we get here, we have a method call, because it was
13016 // previously marked as a method call but now we have an =. This
13017 // looks like:
13018 //
13019 // foo.bar = 1
13020 //
13021 // When it was parsed in the prefix position, foo.bar was seen as a
13022 // method call with no arguments. Now we have an =, so we know it's
13023 // a method call with an argument. In this case we will create the
13024 // arguments node, parse the argument, and add it to the list.
13025 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
13026 call->arguments = arguments;
13027
13028 pm_arguments_node_arguments_append(arguments, value);
13029 call->base.location.end = arguments->base.location.end;
13030 call->equal_loc = PM_LOCATION_TOKEN_VALUE(operator);
13031
13032 parse_write_name(parser, &call->name);
13033 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13034
13035 return UP(call);
13036 }
13037 }
13038
13039 // If there is no call operator and the message is "[]" then this is
13040 // an aref expression, and we can transform it into an aset
13041 // expression.
13042 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13043 if (call->arguments == NULL) {
13044 call->arguments = pm_arguments_node_create(parser);
13045 }
13046
13047 pm_arguments_node_arguments_append(call->arguments, value);
13048 target->location.end = value->location.end;
13049
13050 // Replace the name with "[]=".
13051 call->name = pm_parser_constant_id_constant(parser, "[]=", 3);
13052 call->equal_loc = PM_LOCATION_TOKEN_VALUE(operator);
13053
13054 // Ensure that the arguments for []= don't contain keywords
13055 pm_index_arguments_check(parser, call->arguments, call->block);
13056 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13057
13058 return target;
13059 }
13060
13061 // If there are arguments on the call node, then it can't be a
13062 // method call ending with = or a local variable write, so it must
13063 // be a syntax error. In this case we'll fall through to our default
13064 // handling. We need to free the value that we parsed because there
13065 // is no way for us to attach it to the tree at this point.
13066 //
13067 // Since it is possible for the value to contain an implicit
13068 // parameter somewhere in its subtree, we need to walk it and remove
13069 // any implicit parameters from the list of implicit parameters for
13070 // the current scope.
13071 pm_node_unreference(parser, value);
13072 pm_node_destroy(parser, value);
13073 }
13075 default:
13076 // In this case we have a node that we don't know how to convert into a
13077 // target. We need to treat it as an error. For now, we'll mark it as an
13078 // error and just skip right past it.
13079 pm_parser_err_token(parser, operator, PM_ERR_WRITE_TARGET_UNEXPECTED);
13080 return target;
13081 }
13082}
13083
13090static pm_node_t *
13091parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) {
13092 switch (PM_NODE_TYPE(target)) {
13093 case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13094 case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13095 case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13096 case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13097 case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13098 case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13099 case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13100 default: break;
13101 }
13102
13103 pm_constant_id_t name = pm_parser_local_add_location(parser, target->location.start, target->location.end, 1);
13104 pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals);
13105
13106 pm_node_destroy(parser, target);
13107 return UP(result);
13108}
13109
13120static pm_node_t *
13121parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13122 bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE);
13123
13124 pm_multi_target_node_t *result = pm_multi_target_node_create(parser);
13125 pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target, true, false));
13126
13127 while (accept1(parser, PM_TOKEN_COMMA)) {
13128 if (accept1(parser, PM_TOKEN_USTAR)) {
13129 // Here we have a splat operator. It can have a name or be
13130 // anonymous. It can be the final target or be in the middle if
13131 // there haven't been any others yet.
13132 if (has_rest) {
13133 pm_parser_err_previous(parser, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
13134 }
13135
13136 pm_token_t star_operator = parser->previous;
13137 pm_node_t *name = NULL;
13138
13139 if (token_begins_expression_p(parser->current.type)) {
13140 name = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
13141 name = parse_target(parser, name, true, true);
13142 }
13143
13144 pm_node_t *splat = UP(pm_splat_node_create(parser, &star_operator, name));
13145 pm_multi_target_node_targets_append(parser, result, splat);
13146 has_rest = true;
13147 } else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
13148 context_push(parser, PM_CONTEXT_MULTI_TARGET);
13149 pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13150 target = parse_target(parser, target, true, false);
13151
13152 pm_multi_target_node_targets_append(parser, result, target);
13153 context_pop(parser);
13154 } else if (token_begins_expression_p(parser->current.type)) {
13155 pm_node_t *target = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13156 target = parse_target(parser, target, true, false);
13157
13158 pm_multi_target_node_targets_append(parser, result, target);
13159 } else if (!match1(parser, PM_TOKEN_EOF)) {
13160 // If we get here, then we have a trailing , in a multi target node.
13161 // We'll add an implicit rest node to represent this.
13162 pm_node_t *rest = UP(pm_implicit_rest_node_create(parser, &parser->previous));
13163 pm_multi_target_node_targets_append(parser, result, rest);
13164 break;
13165 }
13166 }
13167
13168 return UP(result);
13169}
13170
13175static pm_node_t *
13176parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13177 pm_node_t *result = parse_targets(parser, first_target, binding_power, depth);
13178 accept1(parser, PM_TOKEN_NEWLINE);
13179
13180 // Ensure that we have either an = or a ) after the targets.
13181 if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
13182 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13183 }
13184
13185 return result;
13186}
13187
13191static pm_statements_node_t *
13192parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
13193 // First, skip past any optional terminators that might be at the beginning
13194 // of the statements.
13195 while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE));
13196
13197 // If we have a terminator, then we can just return NULL.
13198 if (context_terminator(context, &parser->current)) return NULL;
13199
13200 pm_statements_node_t *statements = pm_statements_node_create(parser);
13201
13202 // At this point we know we have at least one statement, and that it
13203 // immediately follows the current token.
13204 context_push(parser, context);
13205
13206 while (true) {
13207 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, false, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
13208 pm_statements_node_body_append(parser, statements, node, true);
13209
13210 // If we're recovering from a syntax error, then we need to stop parsing
13211 // the statements now.
13212 if (parser->recovering) {
13213 // If this is the level of context where the recovery has happened,
13214 // then we can mark the parser as done recovering.
13215 if (context_terminator(context, &parser->current)) parser->recovering = false;
13216 break;
13217 }
13218
13219 // If we have a terminator, then we will parse all consecutive
13220 // terminators and then continue parsing the statements list.
13221 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
13222 // If we have a terminator, then we will continue parsing the
13223 // statements list.
13224 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13225 if (context_terminator(context, &parser->current)) break;
13226
13227 // Now we can continue parsing the list of statements.
13228 continue;
13229 }
13230
13231 // At this point we have a list of statements that are not terminated by
13232 // a newline or semicolon. At this point we need to check if we're at
13233 // the end of the statements list. If we are, then we should break out
13234 // of the loop.
13235 if (context_terminator(context, &parser->current)) break;
13236
13237 // At this point, we have a syntax error, because the statement was not
13238 // terminated by a newline or semicolon, and we're not at the end of the
13239 // statements list. Ideally we should scan forward to determine if we
13240 // should insert a missing terminator or break out of parsing the
13241 // statements list at this point.
13242 //
13243 // We don't have that yet, so instead we'll do a more naive approach. If
13244 // we were unable to parse an expression, then we will skip past this
13245 // token and continue parsing the statements list. Otherwise we'll add
13246 // an error and continue parsing the statements list.
13247 if (PM_NODE_TYPE_P(node, PM_MISSING_NODE)) {
13248 parser_lex(parser);
13249
13250 // If we are at the end of the file, then we need to stop parsing
13251 // the statements entirely at this point. Mark the parser as
13252 // recovering, as we know that EOF closes the top-level context, and
13253 // then break out of the loop.
13254 if (match1(parser, PM_TOKEN_EOF)) {
13255 parser->recovering = true;
13256 break;
13257 }
13258
13259 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13260 if (context_terminator(context, &parser->current)) break;
13261 } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) {
13262 // This is an inlined version of accept1 because the error that we
13263 // want to add has varargs. If this happens again, we should
13264 // probably extract a helper function.
13265 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type));
13266 parser->previous.start = parser->previous.end;
13267 parser->previous.type = PM_TOKEN_MISSING;
13268 }
13269 }
13270
13271 context_pop(parser);
13272 bool last_value = true;
13273 switch (context) {
13276 last_value = false;
13277 break;
13278 default:
13279 break;
13280 }
13281 pm_void_statements_check(parser, statements, last_value);
13282
13283 return statements;
13284}
13285
13290static void
13291pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13292 const pm_node_t *duplicated = pm_static_literals_add(&parser->newline_list, parser->start_line, literals, node, true);
13293
13294 if (duplicated != NULL) {
13295 pm_buffer_t buffer = { 0 };
13296 pm_static_literal_inspect(&buffer, &parser->newline_list, parser->start_line, parser->encoding->name, duplicated);
13297
13298 pm_diagnostic_list_append_format(
13299 &parser->warning_list,
13300 duplicated->location.start,
13301 duplicated->location.end,
13302 PM_WARN_DUPLICATED_HASH_KEY,
13303 (int) pm_buffer_length(&buffer),
13304 pm_buffer_value(&buffer),
13305 pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line).line
13306 );
13307
13308 pm_buffer_free(&buffer);
13309 }
13310}
13311
13316static void
13317pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13318 pm_node_t *previous;
13319
13320 if ((previous = pm_static_literals_add(&parser->newline_list, parser->start_line, literals, node, false)) != NULL) {
13321 pm_diagnostic_list_append_format(
13322 &parser->warning_list,
13323 node->location.start,
13324 node->location.end,
13325 PM_WARN_DUPLICATED_WHEN_CLAUSE,
13326 pm_newline_list_line_column(&parser->newline_list, node->location.start, parser->start_line).line,
13327 pm_newline_list_line_column(&parser->newline_list, previous->location.start, parser->start_line).line
13328 );
13329 }
13330}
13331
13335static bool
13336parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node, uint16_t depth) {
13337 assert(PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE));
13338 bool contains_keyword_splat = false;
13339
13340 while (true) {
13341 pm_node_t *element;
13342
13343 switch (parser->current.type) {
13344 case PM_TOKEN_USTAR_STAR: {
13345 parser_lex(parser);
13346 pm_token_t operator = parser->previous;
13347 pm_node_t *value = NULL;
13348
13349 if (match1(parser, PM_TOKEN_BRACE_LEFT)) {
13350 // If we're about to parse a nested hash that is being
13351 // pushed into this hash directly with **, then we want the
13352 // inner hash to share the static literals with the outer
13353 // hash.
13354 parser->current_hash_keys = literals;
13355 value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, (uint16_t) (depth + 1));
13356 } else if (token_begins_expression_p(parser->current.type)) {
13357 value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, (uint16_t) (depth + 1));
13358 } else {
13359 pm_parser_scope_forwarding_keywords_check(parser, &operator);
13360 }
13361
13362 element = UP(pm_assoc_splat_node_create(parser, value, &operator));
13363 contains_keyword_splat = true;
13364 break;
13365 }
13366 case PM_TOKEN_LABEL: {
13367 pm_token_t label = parser->current;
13368 parser_lex(parser);
13369
13370 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &label));
13371 pm_hash_key_static_literals_add(parser, literals, key);
13372
13373 pm_token_t operator = not_provided(parser);
13374 pm_node_t *value = NULL;
13375
13376 if (token_begins_expression_p(parser->current.type)) {
13377 value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_HASH_EXPRESSION_AFTER_LABEL, (uint16_t) (depth + 1));
13378 } else {
13379 if (parser->encoding->isupper_char(label.start, (label.end - 1) - label.start)) {
13380 pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 };
13381 value = UP(pm_constant_read_node_create(parser, &constant));
13382 } else {
13383 int depth = -1;
13384 pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 };
13385
13386 if (identifier.end[-1] == '!' || identifier.end[-1] == '?') {
13387 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ);
13388 } else {
13389 depth = pm_parser_local_depth(parser, &identifier);
13390 }
13391
13392 if (depth == -1) {
13393 value = UP(pm_call_node_variable_call_create(parser, &identifier));
13394 } else {
13395 value = UP(pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth));
13396 }
13397 }
13398
13399 value->location.end++;
13400 value = UP(pm_implicit_node_create(parser, value));
13401 }
13402
13403 element = UP(pm_assoc_node_create(parser, key, &operator, value));
13404 break;
13405 }
13406 default: {
13407 pm_node_t *key = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, true, PM_ERR_HASH_KEY, (uint16_t) (depth + 1));
13408
13409 // Hash keys that are strings are automatically frozen. We will
13410 // mark that here.
13411 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
13412 pm_node_flag_set(key, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
13413 }
13414
13415 pm_hash_key_static_literals_add(parser, literals, key);
13416
13417 pm_token_t operator;
13418 if (pm_symbol_node_label_p(key)) {
13419 operator = not_provided(parser);
13420 } else {
13421 expect1(parser, PM_TOKEN_EQUAL_GREATER, PM_ERR_HASH_ROCKET);
13422 operator = parser->previous;
13423 }
13424
13425 pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1));
13426 element = UP(pm_assoc_node_create(parser, key, &operator, value));
13427 break;
13428 }
13429 }
13430
13431 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
13432 pm_hash_node_elements_append((pm_hash_node_t *) node, element);
13433 } else {
13434 pm_keyword_hash_node_elements_append((pm_keyword_hash_node_t *) node, element);
13435 }
13436
13437 // If there's no comma after the element, then we're done.
13438 if (!accept1(parser, PM_TOKEN_COMMA)) break;
13439
13440 // If the next element starts with a label or a **, then we know we have
13441 // another element in the hash, so we'll continue parsing.
13442 if (match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)) continue;
13443
13444 // Otherwise we need to check if the subsequent token begins an expression.
13445 // If it does, then we'll continue parsing.
13446 if (token_begins_expression_p(parser->current.type)) continue;
13447
13448 // Otherwise by default we will exit out of this loop.
13449 break;
13450 }
13451
13452 return contains_keyword_splat;
13453}
13454
13455static inline bool
13456argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) {
13457 if (pm_symbol_node_label_p(argument)) {
13458 return true;
13459 }
13460
13461 switch (PM_NODE_TYPE(argument)) {
13462 case PM_CALL_NODE: {
13463 pm_call_node_t *cast = (pm_call_node_t *) argument;
13464 if (cast->opening_loc.start == NULL && cast->arguments != NULL) {
13465 if (PM_NODE_FLAG_P(cast->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS | PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
13466 return false;
13467 }
13468 if (cast->block != NULL) {
13469 return false;
13470 }
13471 }
13472 break;
13473 }
13474 default: break;
13475 }
13476 return accept1(parser, PM_TOKEN_EQUAL_GREATER);
13477}
13478
13482static inline void
13483parse_arguments_append(pm_parser_t *parser, pm_arguments_t *arguments, pm_node_t *argument) {
13484 if (arguments->arguments == NULL) {
13485 arguments->arguments = pm_arguments_node_create(parser);
13486 }
13487
13488 pm_arguments_node_arguments_append(arguments->arguments, argument);
13489}
13490
13494static void
13495parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator, uint16_t depth) {
13496 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
13497
13498 // First we need to check if the next token is one that could be the start
13499 // of an argument. If it's not, then we can just return.
13500 if (
13501 match2(parser, terminator, PM_TOKEN_EOF) ||
13502 (binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) ||
13503 context_terminator(parser->current_context->context, &parser->current)
13504 ) {
13505 return;
13506 }
13507
13508 bool parsed_first_argument = false;
13509 bool parsed_bare_hash = false;
13510 bool parsed_block_argument = false;
13511 bool parsed_forwarding_arguments = false;
13512
13513 while (!match1(parser, PM_TOKEN_EOF)) {
13514 if (parsed_forwarding_arguments) {
13515 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES);
13516 }
13517
13518 pm_node_t *argument = NULL;
13519
13520 switch (parser->current.type) {
13521 case PM_TOKEN_USTAR_STAR:
13522 case PM_TOKEN_LABEL: {
13523 if (parsed_bare_hash) {
13524 pm_parser_err_current(parser, PM_ERR_ARGUMENT_BARE_HASH);
13525 }
13526
13527 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
13528 argument = UP(hash);
13529
13530 pm_static_literals_t hash_keys = { 0 };
13531 bool contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1));
13532
13533 parse_arguments_append(parser, arguments, argument);
13534
13535 pm_node_flags_t flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
13536 if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
13537 pm_node_flag_set(UP(arguments->arguments), flags);
13538
13539 pm_static_literals_free(&hash_keys);
13540 parsed_bare_hash = true;
13541
13542 break;
13543 }
13544 case PM_TOKEN_UAMPERSAND: {
13545 parser_lex(parser);
13546 pm_token_t operator = parser->previous;
13547 pm_node_t *expression = NULL;
13548
13549 if (token_begins_expression_p(parser->current.type)) {
13550 expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
13551 } else {
13552 pm_parser_scope_forwarding_block_check(parser, &operator);
13553 }
13554
13555 argument = UP(pm_block_argument_node_create(parser, &operator, expression));
13556 if (parsed_block_argument) {
13557 parse_arguments_append(parser, arguments, argument);
13558 } else {
13559 arguments->block = argument;
13560 }
13561
13562 if (match1(parser, PM_TOKEN_COMMA)) {
13563 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK);
13564 }
13565
13566 parsed_block_argument = true;
13567 break;
13568 }
13569 case PM_TOKEN_USTAR: {
13570 parser_lex(parser);
13571 pm_token_t operator = parser->previous;
13572
13573 if (match4(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_SEMICOLON, PM_TOKEN_BRACKET_RIGHT)) {
13574 pm_parser_scope_forwarding_positionals_check(parser, &operator);
13575 argument = UP(pm_splat_node_create(parser, &operator, NULL));
13576 if (parsed_bare_hash) {
13577 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT);
13578 }
13579 } else {
13580 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT, (uint16_t) (depth + 1));
13581
13582 if (parsed_bare_hash) {
13583 pm_parser_err(parser, operator.start, expression->location.end, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT);
13584 }
13585
13586 argument = UP(pm_splat_node_create(parser, &operator, expression));
13587 }
13588
13589 parse_arguments_append(parser, arguments, argument);
13590 break;
13591 }
13592 case PM_TOKEN_UDOT_DOT_DOT: {
13593 if (accepts_forwarding) {
13594 parser_lex(parser);
13595
13596 if (token_begins_expression_p(parser->current.type)) {
13597 // If the token begins an expression then this ... was
13598 // not actually argument forwarding but was instead a
13599 // range.
13600 pm_token_t operator = parser->previous;
13601 pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_RANGE, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
13602
13603 // If we parse a range, we need to validate that we
13604 // didn't accidentally violate the nonassoc rules of the
13605 // ... operator.
13606 if (PM_NODE_TYPE_P(right, PM_RANGE_NODE)) {
13607 pm_range_node_t *range = (pm_range_node_t *) right;
13608 pm_parser_err(parser, range->operator_loc.start, range->operator_loc.end, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
13609 }
13610
13611 argument = UP(pm_range_node_create(parser, NULL, &operator, right));
13612 } else {
13613 pm_parser_scope_forwarding_all_check(parser, &parser->previous);
13614 if (parsed_first_argument && terminator == PM_TOKEN_EOF) {
13615 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND);
13616 }
13617
13618 argument = UP(pm_forwarding_arguments_node_create(parser, &parser->previous));
13619 parse_arguments_append(parser, arguments, argument);
13620 pm_node_flag_set(UP(arguments->arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
13621 arguments->has_forwarding = true;
13622 parsed_forwarding_arguments = true;
13623 break;
13624 }
13625 }
13626 }
13628 default: {
13629 if (argument == NULL) {
13630 argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, !parsed_first_argument, true, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
13631 }
13632
13633 bool contains_keywords = false;
13634 bool contains_keyword_splat = false;
13635
13636 if (argument_allowed_for_bare_hash(parser, argument)){
13637 if (parsed_bare_hash) {
13638 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH);
13639 }
13640
13641 pm_token_t operator;
13642 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
13643 operator = parser->previous;
13644 } else {
13645 operator = not_provided(parser);
13646 }
13647
13648 pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser);
13649 contains_keywords = true;
13650
13651 // Create the set of static literals for this hash.
13652 pm_static_literals_t hash_keys = { 0 };
13653 pm_hash_key_static_literals_add(parser, &hash_keys, argument);
13654
13655 // Finish parsing the one we are part way through.
13656 pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1));
13657 argument = UP(pm_assoc_node_create(parser, argument, &operator, value));
13658
13659 pm_keyword_hash_node_elements_append(bare_hash, argument);
13660 argument = UP(bare_hash);
13661
13662 // Then parse more if we have a comma
13663 if (accept1(parser, PM_TOKEN_COMMA) && (
13664 token_begins_expression_p(parser->current.type) ||
13665 match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)
13666 )) {
13667 contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(bare_hash), (uint16_t) (depth + 1));
13668 }
13669
13670 pm_static_literals_free(&hash_keys);
13671 parsed_bare_hash = true;
13672 }
13673
13674 parse_arguments_append(parser, arguments, argument);
13675
13676 pm_node_flags_t flags = 0;
13677 if (contains_keywords) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
13678 if (contains_keyword_splat) flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
13679 pm_node_flag_set(UP(arguments->arguments), flags);
13680
13681 break;
13682 }
13683 }
13684
13685 parsed_first_argument = true;
13686
13687 // If parsing the argument failed, we need to stop parsing arguments.
13688 if (PM_NODE_TYPE_P(argument, PM_MISSING_NODE) || parser->recovering) break;
13689
13690 // If the terminator of these arguments is not EOF, then we have a
13691 // specific token we're looking for. In that case we can accept a
13692 // newline here because it is not functioning as a statement terminator.
13693 bool accepted_newline = false;
13694 if (terminator != PM_TOKEN_EOF) {
13695 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
13696 }
13697
13698 if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) {
13699 // If we previously were on a comma and we just parsed a bare hash,
13700 // then we want to continue parsing arguments. This is because the
13701 // comma was grabbed up by the hash parser.
13702 } else if (accept1(parser, PM_TOKEN_COMMA)) {
13703 // If there was a comma, then we need to check if we also accepted a
13704 // newline. If we did, then this is a syntax error.
13705 if (accepted_newline) {
13706 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
13707 }
13708
13709 // If this is a command call and an argument takes a block,
13710 // there can be no further arguments. For example,
13711 // `foo(bar 1 do end, 2)` should be rejected.
13712 if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) {
13713 pm_call_node_t *call = (pm_call_node_t *) argument;
13714 if (call->opening_loc.start == NULL && call->arguments != NULL && call->block != NULL) {
13715 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
13716 break;
13717 }
13718 }
13719 } else {
13720 // If there is no comma at the end of the argument list then we're
13721 // done parsing arguments and can break out of this loop.
13722 break;
13723 }
13724
13725 // If we hit the terminator, then that means we have a trailing comma so
13726 // we can accept that output as well.
13727 if (match1(parser, terminator)) break;
13728 }
13729}
13730
13741static pm_multi_target_node_t *
13742parse_required_destructured_parameter(pm_parser_t *parser) {
13743 expect1(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER);
13744
13745 pm_multi_target_node_t *node = pm_multi_target_node_create(parser);
13746 pm_multi_target_node_opening_set(node, &parser->previous);
13747
13748 do {
13749 pm_node_t *param;
13750
13751 // If we get here then we have a trailing comma, which isn't allowed in
13752 // the grammar. In other places, multi targets _do_ allow trailing
13753 // commas, so here we'll assume this is a mistake of the user not
13754 // knowing it's not allowed here.
13755 if (node->lefts.size > 0 && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
13756 param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
13757 pm_multi_target_node_targets_append(parser, node, param);
13758 pm_parser_err_current(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
13759 break;
13760 }
13761
13762 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
13763 param = UP(parse_required_destructured_parameter(parser));
13764 } else if (accept1(parser, PM_TOKEN_USTAR)) {
13765 pm_token_t star = parser->previous;
13766 pm_node_t *value = NULL;
13767
13768 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
13769 pm_token_t name = parser->previous;
13770 value = UP(pm_required_parameter_node_create(parser, &name));
13771 if (pm_parser_parameter_name_check(parser, &name)) {
13772 pm_node_flag_set_repeated_parameter(value);
13773 }
13774 pm_parser_local_add_token(parser, &name, 1);
13775 }
13776
13777 param = UP(pm_splat_node_create(parser, &star, value));
13778 } else {
13779 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER);
13780 pm_token_t name = parser->previous;
13781
13782 param = UP(pm_required_parameter_node_create(parser, &name));
13783 if (pm_parser_parameter_name_check(parser, &name)) {
13784 pm_node_flag_set_repeated_parameter(param);
13785 }
13786 pm_parser_local_add_token(parser, &name, 1);
13787 }
13788
13789 pm_multi_target_node_targets_append(parser, node, param);
13790 } while (accept1(parser, PM_TOKEN_COMMA));
13791
13792 accept1(parser, PM_TOKEN_NEWLINE);
13793 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN_REQ_PARAMETER);
13794 pm_multi_target_node_closing_set(node, &parser->previous);
13795
13796 return node;
13797}
13798
13803typedef enum {
13804 PM_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state
13805 PM_PARAMETERS_ORDER_NOTHING_AFTER = 1,
13806 PM_PARAMETERS_ORDER_KEYWORDS_REST,
13807 PM_PARAMETERS_ORDER_KEYWORDS,
13808 PM_PARAMETERS_ORDER_REST,
13809 PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
13810 PM_PARAMETERS_ORDER_OPTIONAL,
13811 PM_PARAMETERS_ORDER_NAMED,
13812 PM_PARAMETERS_ORDER_NONE,
13813} pm_parameters_order_t;
13814
13818static pm_parameters_order_t parameters_ordering[PM_TOKEN_MAXIMUM] = {
13819 [0] = PM_PARAMETERS_NO_CHANGE,
13820 [PM_TOKEN_UAMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
13821 [PM_TOKEN_AMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
13822 [PM_TOKEN_UDOT_DOT_DOT] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
13823 [PM_TOKEN_IDENTIFIER] = PM_PARAMETERS_ORDER_NAMED,
13824 [PM_TOKEN_PARENTHESIS_LEFT] = PM_PARAMETERS_ORDER_NAMED,
13825 [PM_TOKEN_EQUAL] = PM_PARAMETERS_ORDER_OPTIONAL,
13826 [PM_TOKEN_LABEL] = PM_PARAMETERS_ORDER_KEYWORDS,
13827 [PM_TOKEN_USTAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
13828 [PM_TOKEN_STAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
13829 [PM_TOKEN_USTAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST,
13830 [PM_TOKEN_STAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST
13831};
13832
13840static bool
13841update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_order_t *current) {
13842 pm_parameters_order_t state = parameters_ordering[token->type];
13843 if (state == PM_PARAMETERS_NO_CHANGE) return true;
13844
13845 // If we see another ordered argument after a optional argument
13846 // we only continue parsing ordered arguments until we stop seeing ordered arguments.
13847 if (*current == PM_PARAMETERS_ORDER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
13848 *current = PM_PARAMETERS_ORDER_AFTER_OPTIONAL;
13849 return true;
13850 } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
13851 return true;
13852 }
13853
13854 if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
13855 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR);
13856 return false;
13857 } else if (token->type == PM_TOKEN_UDOT_DOT_DOT && (*current >= PM_PARAMETERS_ORDER_KEYWORDS_REST && *current <= PM_PARAMETERS_ORDER_AFTER_OPTIONAL)) {
13858 pm_parser_err_token(parser, token, *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL ? PM_ERR_PARAMETER_FORWARDING_AFTER_REST : PM_ERR_PARAMETER_ORDER);
13859 return false;
13860 } else if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) {
13861 // We know what transition we failed on, so we can provide a better error here.
13862 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_ORDER);
13863 return false;
13864 }
13865
13866 if (state < *current) *current = state;
13867 return true;
13868}
13869
13873static pm_parameters_node_t *
13874parse_parameters(
13875 pm_parser_t *parser,
13876 pm_binding_power_t binding_power,
13877 bool uses_parentheses,
13878 bool allows_trailing_comma,
13879 bool allows_forwarding_parameters,
13880 bool accepts_blocks_in_defaults,
13881 bool in_block,
13882 pm_diagnostic_id_t diag_id_forwarding,
13883 uint16_t depth
13884) {
13885 pm_do_loop_stack_push(parser, false);
13886
13887 pm_parameters_node_t *params = pm_parameters_node_create(parser);
13888 pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE;
13889
13890 while (true) {
13891 bool parsing = true;
13892
13893 switch (parser->current.type) {
13894 case PM_TOKEN_PARENTHESIS_LEFT: {
13895 update_parameter_state(parser, &parser->current, &order);
13896 pm_node_t *param = UP(parse_required_destructured_parameter(parser));
13897
13898 if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
13899 pm_parameters_node_requireds_append(params, param);
13900 } else {
13901 pm_parameters_node_posts_append(params, param);
13902 }
13903 break;
13904 }
13905 case PM_TOKEN_UAMPERSAND:
13906 case PM_TOKEN_AMPERSAND: {
13907 update_parameter_state(parser, &parser->current, &order);
13908 parser_lex(parser);
13909
13910 pm_token_t operator = parser->previous;
13911 pm_token_t name;
13912
13913 bool repeated = false;
13914 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
13915 name = parser->previous;
13916 repeated = pm_parser_parameter_name_check(parser, &name);
13917 pm_parser_local_add_token(parser, &name, 1);
13918 } else {
13919 name = not_provided(parser);
13920 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
13921 }
13922
13923 pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator);
13924 if (repeated) {
13925 pm_node_flag_set_repeated_parameter(UP(param));
13926 }
13927 if (params->block == NULL) {
13928 pm_parameters_node_block_set(params, param);
13929 } else {
13930 pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_BLOCK_MULTI);
13931 pm_parameters_node_posts_append(params, UP(param));
13932 }
13933
13934 break;
13935 }
13936 case PM_TOKEN_UDOT_DOT_DOT: {
13937 if (!allows_forwarding_parameters) {
13938 pm_parser_err_current(parser, diag_id_forwarding);
13939 }
13940
13941 bool succeeded = update_parameter_state(parser, &parser->current, &order);
13942 parser_lex(parser);
13943
13944 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
13945 pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
13946
13947 if (params->keyword_rest != NULL) {
13948 // If we already have a keyword rest parameter, then we replace it with the
13949 // forwarding parameter and move the keyword rest parameter to the posts list.
13950 pm_node_t *keyword_rest = params->keyword_rest;
13951 pm_parameters_node_posts_append(params, keyword_rest);
13952 if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD);
13953 params->keyword_rest = NULL;
13954 }
13955
13956 pm_parameters_node_keyword_rest_set(params, UP(param));
13957 break;
13958 }
13959 case PM_TOKEN_CLASS_VARIABLE:
13960 case PM_TOKEN_IDENTIFIER:
13961 case PM_TOKEN_CONSTANT:
13962 case PM_TOKEN_INSTANCE_VARIABLE:
13963 case PM_TOKEN_GLOBAL_VARIABLE:
13964 case PM_TOKEN_METHOD_NAME: {
13965 parser_lex(parser);
13966 switch (parser->previous.type) {
13967 case PM_TOKEN_CONSTANT:
13968 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
13969 break;
13970 case PM_TOKEN_INSTANCE_VARIABLE:
13971 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
13972 break;
13973 case PM_TOKEN_GLOBAL_VARIABLE:
13974 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
13975 break;
13976 case PM_TOKEN_CLASS_VARIABLE:
13977 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
13978 break;
13979 case PM_TOKEN_METHOD_NAME:
13980 pm_parser_err_previous(parser, PM_ERR_PARAMETER_METHOD_NAME);
13981 break;
13982 default: break;
13983 }
13984
13985 if (parser->current.type == PM_TOKEN_EQUAL) {
13986 update_parameter_state(parser, &parser->current, &order);
13987 } else {
13988 update_parameter_state(parser, &parser->previous, &order);
13989 }
13990
13991 pm_token_t name = parser->previous;
13992 bool repeated = pm_parser_parameter_name_check(parser, &name);
13993 pm_parser_local_add_token(parser, &name, 1);
13994
13995 if (match1(parser, PM_TOKEN_EQUAL)) {
13996 pm_token_t operator = parser->current;
13997 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
13998 parser_lex(parser);
13999
14000 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
14001 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14002
14003 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14004 pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1));
14005 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14006
14007 pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
14008
14009 if (repeated) {
14010 pm_node_flag_set_repeated_parameter(UP(param));
14011 }
14012 pm_parameters_node_optionals_append(params, param);
14013
14014 // If the value of the parameter increased the number of
14015 // reads of that parameter, then we need to warn that we
14016 // have a circular definition.
14017 if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14018 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR);
14019 }
14020
14021 context_pop(parser);
14022
14023 // If parsing the value of the parameter resulted in error recovery,
14024 // then we can put a missing node in its place and stop parsing the
14025 // parameters entirely now.
14026 if (parser->recovering) {
14027 parsing = false;
14028 break;
14029 }
14030 } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14031 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14032 if (repeated) {
14033 pm_node_flag_set_repeated_parameter(UP(param));
14034 }
14035 pm_parameters_node_requireds_append(params, UP(param));
14036 } else {
14037 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14038 if (repeated) {
14039 pm_node_flag_set_repeated_parameter(UP(param));
14040 }
14041 pm_parameters_node_posts_append(params, UP(param));
14042 }
14043
14044 break;
14045 }
14046 case PM_TOKEN_LABEL: {
14047 if (!uses_parentheses && !in_block) parser->in_keyword_arg = true;
14048 update_parameter_state(parser, &parser->current, &order);
14049
14050 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14051 parser_lex(parser);
14052
14053 pm_token_t name = parser->previous;
14054 pm_token_t local = name;
14055 local.end -= 1;
14056
14057 if (parser->encoding_changed ? parser->encoding->isupper_char(local.start, local.end - local.start) : pm_encoding_utf_8_isupper_char(local.start, local.end - local.start)) {
14058 pm_parser_err(parser, local.start, local.end, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14059 } else if (local.end[-1] == '!' || local.end[-1] == '?') {
14060 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE);
14061 }
14062
14063 bool repeated = pm_parser_parameter_name_check(parser, &local);
14064 pm_parser_local_add_token(parser, &local, 1);
14065
14066 switch (parser->current.type) {
14067 case PM_TOKEN_COMMA:
14068 case PM_TOKEN_PARENTHESIS_RIGHT:
14069 case PM_TOKEN_PIPE: {
14070 context_pop(parser);
14071
14072 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14073 if (repeated) {
14074 pm_node_flag_set_repeated_parameter(param);
14075 }
14076
14077 pm_parameters_node_keywords_append(params, param);
14078 break;
14079 }
14080 case PM_TOKEN_SEMICOLON:
14081 case PM_TOKEN_NEWLINE: {
14082 context_pop(parser);
14083
14084 if (uses_parentheses) {
14085 parsing = false;
14086 break;
14087 }
14088
14089 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14090 if (repeated) {
14091 pm_node_flag_set_repeated_parameter(param);
14092 }
14093
14094 pm_parameters_node_keywords_append(params, param);
14095 break;
14096 }
14097 default: {
14098 pm_node_t *param;
14099
14100 if (token_begins_expression_p(parser->current.type)) {
14101 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
14102 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14103
14104 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14105 pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT_KW, (uint16_t) (depth + 1));
14106 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14107
14108 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14109 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR);
14110 }
14111
14112 param = UP(pm_optional_keyword_parameter_node_create(parser, &name, value));
14113 }
14114 else {
14115 param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14116 }
14117
14118 if (repeated) {
14119 pm_node_flag_set_repeated_parameter(param);
14120 }
14121
14122 context_pop(parser);
14123 pm_parameters_node_keywords_append(params, param);
14124
14125 // If parsing the value of the parameter resulted in error recovery,
14126 // then we can put a missing node in its place and stop parsing the
14127 // parameters entirely now.
14128 if (parser->recovering) {
14129 parsing = false;
14130 break;
14131 }
14132 }
14133 }
14134
14135 parser->in_keyword_arg = false;
14136 break;
14137 }
14138 case PM_TOKEN_USTAR:
14139 case PM_TOKEN_STAR: {
14140 update_parameter_state(parser, &parser->current, &order);
14141 parser_lex(parser);
14142
14143 pm_token_t operator = parser->previous;
14144 pm_token_t name;
14145 bool repeated = false;
14146
14147 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14148 name = parser->previous;
14149 repeated = pm_parser_parameter_name_check(parser, &name);
14150 pm_parser_local_add_token(parser, &name, 1);
14151 } else {
14152 name = not_provided(parser);
14153 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
14154 }
14155
14156 pm_node_t *param = UP(pm_rest_parameter_node_create(parser, &operator, &name));
14157 if (repeated) {
14158 pm_node_flag_set_repeated_parameter(param);
14159 }
14160
14161 if (params->rest == NULL) {
14162 pm_parameters_node_rest_set(params, param);
14163 } else {
14164 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_SPLAT_MULTI);
14165 pm_parameters_node_posts_append(params, param);
14166 }
14167
14168 break;
14169 }
14170 case PM_TOKEN_STAR_STAR:
14171 case PM_TOKEN_USTAR_STAR: {
14172 pm_parameters_order_t previous_order = order;
14173 update_parameter_state(parser, &parser->current, &order);
14174 parser_lex(parser);
14175
14176 pm_token_t operator = parser->previous;
14177 pm_node_t *param;
14178
14179 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
14180 if (previous_order <= PM_PARAMETERS_ORDER_KEYWORDS) {
14181 pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW);
14182 }
14183
14184 param = UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
14185 } else {
14186 pm_token_t name;
14187
14188 bool repeated = false;
14189 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14190 name = parser->previous;
14191 repeated = pm_parser_parameter_name_check(parser, &name);
14192 pm_parser_local_add_token(parser, &name, 1);
14193 } else {
14194 name = not_provided(parser);
14195 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS;
14196 }
14197
14198 param = UP(pm_keyword_rest_parameter_node_create(parser, &operator, &name));
14199 if (repeated) {
14200 pm_node_flag_set_repeated_parameter(param);
14201 }
14202 }
14203
14204 if (params->keyword_rest == NULL) {
14205 pm_parameters_node_keyword_rest_set(params, param);
14206 } else {
14207 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI);
14208 pm_parameters_node_posts_append(params, param);
14209 }
14210
14211 break;
14212 }
14213 default:
14214 if (parser->previous.type == PM_TOKEN_COMMA) {
14215 if (allows_trailing_comma && order >= PM_PARAMETERS_ORDER_NAMED) {
14216 // If we get here, then we have a trailing comma in a
14217 // block parameter list.
14218 pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14219
14220 if (params->rest == NULL) {
14221 pm_parameters_node_rest_set(params, param);
14222 } else {
14223 pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI);
14224 pm_parameters_node_posts_append(params, UP(param));
14225 }
14226 } else {
14227 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14228 }
14229 }
14230
14231 parsing = false;
14232 break;
14233 }
14234
14235 // If we hit some kind of issue while parsing the parameter, this would
14236 // have been set to false. In that case, we need to break out of the
14237 // loop.
14238 if (!parsing) break;
14239
14240 bool accepted_newline = false;
14241 if (uses_parentheses) {
14242 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14243 }
14244
14245 if (accept1(parser, PM_TOKEN_COMMA)) {
14246 // If there was a comma, but we also accepted a newline, then this
14247 // is a syntax error.
14248 if (accepted_newline) {
14249 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14250 }
14251 } else {
14252 // If there was no comma, then we're done parsing parameters.
14253 break;
14254 }
14255 }
14256
14257 pm_do_loop_stack_pop(parser);
14258
14259 // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`.
14260 if (params->base.location.start == params->base.location.end) {
14261 pm_node_destroy(parser, UP(params));
14262 return NULL;
14263 }
14264
14265 return params;
14266}
14267
14272static size_t
14273token_newline_index(const pm_parser_t *parser) {
14274 if (parser->heredoc_end == NULL) {
14275 // This is the common case. In this case we can look at the previously
14276 // recorded newline in the newline list and subtract from the current
14277 // offset.
14278 return parser->newline_list.size - 1;
14279 } else {
14280 // This is unlikely. This is the case that we have already parsed the
14281 // start of a heredoc, so we cannot rely on looking at the previous
14282 // offset of the newline list, and instead must go through the whole
14283 // process of a binary search for the line number.
14284 return (size_t) pm_newline_list_line(&parser->newline_list, parser->current.start, 0);
14285 }
14286}
14287
14292static int64_t
14293token_column(const pm_parser_t *parser, size_t newline_index, const pm_token_t *token, bool break_on_non_space) {
14294 const uint8_t *cursor = parser->start + parser->newline_list.offsets[newline_index];
14295 const uint8_t *end = token->start;
14296
14297 // Skip over the BOM if it is present.
14298 if (
14299 newline_index == 0 &&
14300 parser->start[0] == 0xef &&
14301 parser->start[1] == 0xbb &&
14302 parser->start[2] == 0xbf
14303 ) cursor += 3;
14304
14305 int64_t column = 0;
14306 for (; cursor < end; cursor++) {
14307 switch (*cursor) {
14308 case '\t':
14309 column = ((column / PM_TAB_WHITESPACE_SIZE) + 1) * PM_TAB_WHITESPACE_SIZE;
14310 break;
14311 case ' ':
14312 column++;
14313 break;
14314 default:
14315 column++;
14316 if (break_on_non_space) return -1;
14317 break;
14318 }
14319 }
14320
14321 return column;
14322}
14323
14328static void
14329parser_warn_indentation_mismatch(pm_parser_t *parser, size_t opening_newline_index, const pm_token_t *opening_token, bool if_after_else, bool allow_indent) {
14330 // If these warnings are disabled (unlikely), then we can just return.
14331 if (!parser->warn_mismatched_indentation) return;
14332
14333 // If the tokens are on the same line, we do not warn.
14334 size_t closing_newline_index = token_newline_index(parser);
14335 if (opening_newline_index == closing_newline_index) return;
14336
14337 // If the opening token has anything other than spaces or tabs before it,
14338 // then we do not warn. This is unless we are matching up an `if`/`end` pair
14339 // and the `if` immediately follows an `else` keyword.
14340 int64_t opening_column = token_column(parser, opening_newline_index, opening_token, !if_after_else);
14341 if (!if_after_else && (opening_column == -1)) return;
14342
14343 // Get a reference to the closing token off the current parser. This assumes
14344 // that the caller has placed this in the correct position.
14345 pm_token_t *closing_token = &parser->current;
14346
14347 // If the tokens are at the same indentation, we do not warn.
14348 int64_t closing_column = token_column(parser, closing_newline_index, closing_token, true);
14349 if ((closing_column == -1) || (opening_column == closing_column)) return;
14350
14351 // If the closing column is greater than the opening column and we are
14352 // allowing indentation, then we do not warn.
14353 if (allow_indent && (closing_column > opening_column)) return;
14354
14355 // Otherwise, add a warning.
14356 PM_PARSER_WARN_FORMAT(
14357 parser,
14358 closing_token->start,
14359 closing_token->end,
14360 PM_WARN_INDENTATION_MISMATCH,
14361 (int) (closing_token->end - closing_token->start),
14362 (const char *) closing_token->start,
14363 (int) (opening_token->end - opening_token->start),
14364 (const char *) opening_token->start,
14365 ((int32_t) opening_newline_index) + parser->start_line
14366 );
14367}
14368
14369typedef enum {
14370 PM_RESCUES_BEGIN = 1,
14371 PM_RESCUES_BLOCK,
14372 PM_RESCUES_CLASS,
14373 PM_RESCUES_DEF,
14374 PM_RESCUES_LAMBDA,
14375 PM_RESCUES_MODULE,
14376 PM_RESCUES_SCLASS
14377} pm_rescues_type_t;
14378
14383static inline void
14384parse_rescues(pm_parser_t *parser, size_t opening_newline_index, const pm_token_t *opening, pm_begin_node_t *parent_node, pm_rescues_type_t type, uint16_t depth) {
14385 pm_rescue_node_t *current = NULL;
14386
14387 while (match1(parser, PM_TOKEN_KEYWORD_RESCUE)) {
14388 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
14389 parser_lex(parser);
14390
14391 pm_rescue_node_t *rescue = pm_rescue_node_create(parser, &parser->previous);
14392
14393 switch (parser->current.type) {
14394 case PM_TOKEN_EQUAL_GREATER: {
14395 // Here we have an immediate => after the rescue keyword, in which case
14396 // we're going to have an empty list of exceptions to rescue (which
14397 // implies StandardError).
14398 parser_lex(parser);
14399 pm_rescue_node_operator_set(rescue, &parser->previous);
14400
14401 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
14402 reference = parse_target(parser, reference, false, false);
14403
14404 pm_rescue_node_reference_set(rescue, reference);
14405 break;
14406 }
14407 case PM_TOKEN_NEWLINE:
14408 case PM_TOKEN_SEMICOLON:
14409 case PM_TOKEN_KEYWORD_THEN:
14410 // Here we have a terminator for the rescue keyword, in which
14411 // case we're going to just continue on.
14412 break;
14413 default: {
14414 if (token_begins_expression_p(parser->current.type) || match1(parser, PM_TOKEN_USTAR)) {
14415 // Here we have something that could be an exception expression, so
14416 // we'll attempt to parse it here and any others delimited by commas.
14417
14418 do {
14419 pm_node_t *expression = parse_starred_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_RESCUE_EXPRESSION, (uint16_t) (depth + 1));
14420 pm_rescue_node_exceptions_append(rescue, expression);
14421
14422 // If we hit a newline, then this is the end of the rescue expression. We
14423 // can continue on to parse the statements.
14424 if (match3(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_THEN)) break;
14425
14426 // If we hit a `=>` then we're going to parse the exception variable. Once
14427 // we've done that, we'll break out of the loop and parse the statements.
14428 if (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
14429 pm_rescue_node_operator_set(rescue, &parser->previous);
14430
14431 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
14432 reference = parse_target(parser, reference, false, false);
14433
14434 pm_rescue_node_reference_set(rescue, reference);
14435 break;
14436 }
14437 } while (accept1(parser, PM_TOKEN_COMMA));
14438 }
14439 }
14440 }
14441
14442 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
14443 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
14444 rescue->then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(&parser->previous);
14445 }
14446 } else {
14447 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM);
14448 rescue->then_keyword_loc = PM_OPTIONAL_LOCATION_TOKEN_VALUE(&parser->previous);
14449 }
14450
14451 if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
14452 pm_accepts_block_stack_push(parser, true);
14453 pm_context_t context;
14454
14455 switch (type) {
14456 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break;
14457 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break;
14458 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break;
14459 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break;
14460 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break;
14461 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break;
14462 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break;
14463 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
14464 }
14465
14466 pm_statements_node_t *statements = parse_statements(parser, context, (uint16_t) (depth + 1));
14467 if (statements != NULL) pm_rescue_node_statements_set(rescue, statements);
14468
14469 pm_accepts_block_stack_pop(parser);
14470 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
14471 }
14472
14473 if (current == NULL) {
14474 pm_begin_node_rescue_clause_set(parent_node, rescue);
14475 } else {
14476 pm_rescue_node_subsequent_set(current, rescue);
14477 }
14478
14479 current = rescue;
14480 }
14481
14482 // The end node locations on rescue nodes will not be set correctly
14483 // since we won't know the end until we've found all subsequent
14484 // clauses. This sets the end location on all rescues once we know it.
14485 if (current != NULL) {
14486 const uint8_t *end_to_set = current->base.location.end;
14487 pm_rescue_node_t *clause = parent_node->rescue_clause;
14488
14489 while (clause != NULL) {
14490 clause->base.location.end = end_to_set;
14491 clause = clause->subsequent;
14492 }
14493 }
14494
14495 pm_token_t else_keyword;
14496 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
14497 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
14498 opening_newline_index = token_newline_index(parser);
14499
14500 else_keyword = parser->current;
14501 opening = &else_keyword;
14502
14503 parser_lex(parser);
14504 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
14505
14506 pm_statements_node_t *else_statements = NULL;
14507 if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) {
14508 pm_accepts_block_stack_push(parser, true);
14509 pm_context_t context;
14510
14511 switch (type) {
14512 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break;
14513 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break;
14514 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break;
14515 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break;
14516 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break;
14517 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break;
14518 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break;
14519 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_ELSE; break;
14520 }
14521
14522 else_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
14523 pm_accepts_block_stack_pop(parser);
14524
14525 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
14526 }
14527
14528 pm_else_node_t *else_clause = pm_else_node_create(parser, &else_keyword, else_statements, &parser->current);
14529 pm_begin_node_else_clause_set(parent_node, else_clause);
14530
14531 // If we don't have a `current` rescue node, then this is a dangling
14532 // else, and it's an error.
14533 if (current == NULL) pm_parser_err_node(parser, UP(else_clause), PM_ERR_BEGIN_LONELY_ELSE);
14534 }
14535
14536 if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) {
14537 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
14538 pm_token_t ensure_keyword = parser->current;
14539
14540 parser_lex(parser);
14541 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
14542
14543 pm_statements_node_t *ensure_statements = NULL;
14544 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
14545 pm_accepts_block_stack_push(parser, true);
14546 pm_context_t context;
14547
14548 switch (type) {
14549 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break;
14550 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break;
14551 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break;
14552 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break;
14553 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break;
14554 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break;
14555 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break;
14556 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
14557 }
14558
14559 ensure_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
14560 pm_accepts_block_stack_pop(parser);
14561
14562 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
14563 }
14564
14565 pm_ensure_node_t *ensure_clause = pm_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current);
14566 pm_begin_node_ensure_clause_set(parent_node, ensure_clause);
14567 }
14568
14569 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
14570 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
14571 pm_begin_node_end_keyword_set(parent_node, &parser->current);
14572 } else {
14573 pm_token_t end_keyword = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
14574 pm_begin_node_end_keyword_set(parent_node, &end_keyword);
14575 }
14576}
14577
14582static pm_begin_node_t *
14583parse_rescues_implicit_begin(pm_parser_t *parser, size_t opening_newline_index, const pm_token_t *opening, const uint8_t *start, pm_statements_node_t *statements, pm_rescues_type_t type, uint16_t depth) {
14584 pm_token_t begin_keyword = not_provided(parser);
14585 pm_begin_node_t *node = pm_begin_node_create(parser, &begin_keyword, statements);
14586
14587 parse_rescues(parser, opening_newline_index, opening, node, type, (uint16_t) (depth + 1));
14588 node->base.location.start = start;
14589
14590 return node;
14591}
14592
14596static pm_block_parameters_node_t *
14597parse_block_parameters(
14598 pm_parser_t *parser,
14599 bool allows_trailing_comma,
14600 const pm_token_t *opening,
14601 bool is_lambda_literal,
14602 bool accepts_blocks_in_defaults,
14603 uint16_t depth
14604) {
14605 pm_parameters_node_t *parameters = NULL;
14606 if (!match1(parser, PM_TOKEN_SEMICOLON)) {
14607 if (!is_lambda_literal) {
14608 context_push(parser, PM_CONTEXT_BLOCK_PARAMETERS);
14609 }
14610 parameters = parse_parameters(
14611 parser,
14612 is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX,
14613 false,
14614 allows_trailing_comma,
14615 false,
14616 accepts_blocks_in_defaults,
14617 true,
14618 is_lambda_literal ? PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA : PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK,
14619 (uint16_t) (depth + 1)
14620 );
14621 if (!is_lambda_literal) {
14622 context_pop(parser);
14623 }
14624 }
14625
14626 pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening);
14627 if ((opening->type != PM_TOKEN_NOT_PROVIDED)) {
14628 accept1(parser, PM_TOKEN_NEWLINE);
14629
14630 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
14631 do {
14632 switch (parser->current.type) {
14633 case PM_TOKEN_CONSTANT:
14634 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14635 parser_lex(parser);
14636 break;
14637 case PM_TOKEN_INSTANCE_VARIABLE:
14638 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
14639 parser_lex(parser);
14640 break;
14641 case PM_TOKEN_GLOBAL_VARIABLE:
14642 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
14643 parser_lex(parser);
14644 break;
14645 case PM_TOKEN_CLASS_VARIABLE:
14646 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
14647 parser_lex(parser);
14648 break;
14649 default:
14650 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
14651 break;
14652 }
14653
14654 bool repeated = pm_parser_parameter_name_check(parser, &parser->previous);
14655 pm_parser_local_add_token(parser, &parser->previous, 1);
14656
14657 pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous);
14658 if (repeated) pm_node_flag_set_repeated_parameter(UP(local));
14659
14660 pm_block_parameters_node_append_local(block_parameters, local);
14661 } while (accept1(parser, PM_TOKEN_COMMA));
14662 }
14663 }
14664
14665 return block_parameters;
14666}
14667
14672static bool
14673outer_scope_using_numbered_parameters_p(pm_parser_t *parser) {
14674 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
14675 if (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) return true;
14676 }
14677
14678 return false;
14679}
14680
14686static const char * const pm_numbered_parameter_names[] = {
14687 "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9"
14688};
14689
14695static pm_node_t *
14696parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_token_t *opening, const pm_token_t *closing) {
14697 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
14698
14699 // If we have ordinary parameters, then we will return them as the set of
14700 // parameters.
14701 if (parameters != NULL) {
14702 // If we also have implicit parameters, then this is an error.
14703 if (implicit_parameters->size > 0) {
14704 pm_node_t *node = implicit_parameters->nodes[0];
14705
14706 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
14707 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_ORDINARY);
14708 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
14709 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_ORDINARY);
14710 } else {
14711 assert(false && "unreachable");
14712 }
14713 }
14714
14715 return parameters;
14716 }
14717
14718 // If we don't have any implicit parameters, then the set of parameters is
14719 // NULL.
14720 if (implicit_parameters->size == 0) {
14721 return NULL;
14722 }
14723
14724 // If we don't have ordinary parameters, then we now must validate our set
14725 // of implicit parameters. We can only have numbered parameters or it, but
14726 // they cannot be mixed.
14727 uint8_t numbered_parameter = 0;
14728 bool it_parameter = false;
14729
14730 for (size_t index = 0; index < implicit_parameters->size; index++) {
14731 pm_node_t *node = implicit_parameters->nodes[index];
14732
14733 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
14734 if (it_parameter) {
14735 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_IT);
14736 } else if (outer_scope_using_numbered_parameters_p(parser)) {
14737 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK);
14738 } else if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_INNER) {
14739 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK);
14740 } else if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) {
14741 numbered_parameter = MAX(numbered_parameter, (uint8_t) (node->location.start[1] - '0'));
14742 } else {
14743 assert(false && "unreachable");
14744 }
14745 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
14746 if (numbered_parameter > 0) {
14747 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_NUMBERED);
14748 } else {
14749 it_parameter = true;
14750 }
14751 }
14752 }
14753
14754 if (numbered_parameter > 0) {
14755 // Go through the parent scopes and mark them as being disallowed from
14756 // using numbered parameters because this inner scope is using them.
14757 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
14758 scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_INNER;
14759 }
14760
14761 const pm_location_t location = { .start = opening->start, .end = closing->end };
14762 return UP(pm_numbered_parameters_node_create(parser, &location, numbered_parameter));
14763 }
14764
14765 if (it_parameter) {
14766 return UP(pm_it_parameters_node_create(parser, opening, closing));
14767 }
14768
14769 return NULL;
14770}
14771
14775static pm_block_node_t *
14776parse_block(pm_parser_t *parser, uint16_t depth) {
14777 pm_token_t opening = parser->previous;
14778 accept1(parser, PM_TOKEN_NEWLINE);
14779
14780 pm_accepts_block_stack_push(parser, true);
14781 pm_parser_scope_push(parser, false);
14782
14783 pm_block_parameters_node_t *block_parameters = NULL;
14784
14785 if (accept1(parser, PM_TOKEN_PIPE)) {
14786 pm_token_t block_parameters_opening = parser->previous;
14787 if (match1(parser, PM_TOKEN_PIPE)) {
14788 block_parameters = pm_block_parameters_node_create(parser, NULL, &block_parameters_opening);
14789 parser->command_start = true;
14790 parser_lex(parser);
14791 } else {
14792 block_parameters = parse_block_parameters(parser, true, &block_parameters_opening, false, true, (uint16_t) (depth + 1));
14793 accept1(parser, PM_TOKEN_NEWLINE);
14794 parser->command_start = true;
14795 expect1(parser, PM_TOKEN_PIPE, PM_ERR_BLOCK_PARAM_PIPE_TERM);
14796 }
14797
14798 pm_block_parameters_node_closing_set(block_parameters, &parser->previous);
14799 }
14800
14801 accept1(parser, PM_TOKEN_NEWLINE);
14802 pm_node_t *statements = NULL;
14803
14804 if (opening.type == PM_TOKEN_BRACE_LEFT) {
14805 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
14806 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1)));
14807 }
14808
14809 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening);
14810 } else {
14811 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
14812 if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) {
14813 pm_accepts_block_stack_push(parser, true);
14814 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS, (uint16_t) (depth + 1)));
14815 pm_accepts_block_stack_pop(parser);
14816 }
14817
14818 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
14819 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
14820 statements = UP(parse_rescues_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1)));
14821 }
14822 }
14823
14824 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening);
14825 }
14826
14827 pm_constant_id_list_t locals;
14828 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
14829 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &opening, &parser->previous);
14830
14831 pm_parser_scope_pop(parser);
14832 pm_accepts_block_stack_pop(parser);
14833
14834 return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous);
14835}
14836
14842static bool
14843parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_block, bool accepts_command_call, uint16_t depth) {
14844 bool found = false;
14845
14846 if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
14847 found |= true;
14848 arguments->opening_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
14849
14850 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
14851 arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
14852 } else {
14853 pm_accepts_block_stack_push(parser, true);
14854 parse_arguments(parser, arguments, accepts_block, PM_TOKEN_PARENTHESIS_RIGHT, (uint16_t) (depth + 1));
14855
14856 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
14857 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_type_human(parser->current.type));
14858 parser->previous.start = parser->previous.end;
14859 parser->previous.type = PM_TOKEN_MISSING;
14860 }
14861
14862 pm_accepts_block_stack_pop(parser);
14863 arguments->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
14864 }
14865 } else if (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
14866 found |= true;
14867 pm_accepts_block_stack_push(parser, false);
14868
14869 // If we get here, then the subsequent token cannot be used as an infix
14870 // operator. In this case we assume the subsequent token is part of an
14871 // argument to this method call.
14872 parse_arguments(parser, arguments, accepts_block, PM_TOKEN_EOF, (uint16_t) (depth + 1));
14873
14874 // If we have done with the arguments and still not consumed the comma,
14875 // then we have a trailing comma where we need to check whether it is
14876 // allowed or not.
14877 if (parser->previous.type == PM_TOKEN_COMMA && !match1(parser, PM_TOKEN_SEMICOLON)) {
14878 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_type_human(parser->current.type));
14879 }
14880
14881 pm_accepts_block_stack_pop(parser);
14882 }
14883
14884 // If we're at the end of the arguments, we can now check if there is a block
14885 // node that starts with a {. If there is, then we can parse it and add it to
14886 // the arguments.
14887 if (accepts_block) {
14888 pm_block_node_t *block = NULL;
14889
14890 if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
14891 found |= true;
14892 block = parse_block(parser, (uint16_t) (depth + 1));
14893 pm_arguments_validate_block(parser, arguments, block);
14894 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
14895 found |= true;
14896 block = parse_block(parser, (uint16_t) (depth + 1));
14897 }
14898
14899 if (block != NULL) {
14900 if (arguments->block == NULL && !arguments->has_forwarding) {
14901 arguments->block = UP(block);
14902 } else {
14903 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_BLOCK_MULTI);
14904
14905 if (arguments->block != NULL) {
14906 if (arguments->arguments == NULL) {
14907 arguments->arguments = pm_arguments_node_create(parser);
14908 }
14909 pm_arguments_node_arguments_append(arguments->arguments, arguments->block);
14910 }
14911 arguments->block = UP(block);
14912 }
14913 }
14914 }
14915
14916 return found;
14917}
14918
14923static void
14924parse_return(pm_parser_t *parser, pm_node_t *node) {
14925 bool in_sclass = false;
14926 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
14927 switch (context_node->context) {
14931 case PM_CONTEXT_BEGIN:
14932 case PM_CONTEXT_CASE_IN:
14935 case PM_CONTEXT_DEFINED:
14936 case PM_CONTEXT_ELSE:
14937 case PM_CONTEXT_ELSIF:
14938 case PM_CONTEXT_EMBEXPR:
14940 case PM_CONTEXT_FOR:
14941 case PM_CONTEXT_IF:
14943 case PM_CONTEXT_MAIN:
14945 case PM_CONTEXT_PARENS:
14946 case PM_CONTEXT_POSTEXE:
14948 case PM_CONTEXT_PREEXE:
14950 case PM_CONTEXT_TERNARY:
14951 case PM_CONTEXT_UNLESS:
14952 case PM_CONTEXT_UNTIL:
14953 case PM_CONTEXT_WHILE:
14954 // Keep iterating up the lists of contexts, because returns can
14955 // see through these.
14956 continue;
14960 case PM_CONTEXT_SCLASS:
14961 in_sclass = true;
14962 continue;
14966 case PM_CONTEXT_CLASS:
14970 case PM_CONTEXT_MODULE:
14971 // These contexts are invalid for a return.
14972 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
14973 return;
14984 case PM_CONTEXT_DEF:
14990 // These contexts are valid for a return, and we should not
14991 // continue to loop.
14992 return;
14993 case PM_CONTEXT_NONE:
14994 // This case should never happen.
14995 assert(false && "unreachable");
14996 break;
14997 }
14998 }
14999 if (in_sclass && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
15000 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15001 }
15002}
15003
15008static void
15009parse_block_exit(pm_parser_t *parser, pm_node_t *node) {
15010 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15011 switch (context_node->context) {
15018 case PM_CONTEXT_DEFINED:
15019 case PM_CONTEXT_FOR:
15026 case PM_CONTEXT_POSTEXE:
15027 case PM_CONTEXT_UNTIL:
15028 case PM_CONTEXT_WHILE:
15029 // These are the good cases. We're allowed to have a block exit
15030 // in these contexts.
15031 return;
15032 case PM_CONTEXT_DEF:
15037 case PM_CONTEXT_MAIN:
15038 case PM_CONTEXT_PREEXE:
15039 case PM_CONTEXT_SCLASS:
15043 // These are the bad cases. We're not allowed to have a block
15044 // exit in these contexts.
15045 //
15046 // If we get here, then we're about to mark this block exit
15047 // as invalid. However, it could later _become_ valid if we
15048 // find a trailing while/until on the expression. In this
15049 // case instead of adding the error here, we'll add the
15050 // block exit to the list of exits for the expression, and
15051 // the node parsing will handle validating it instead.
15052 assert(parser->current_block_exits != NULL);
15053 pm_node_list_append(parser->current_block_exits, node);
15054 return;
15058 case PM_CONTEXT_BEGIN:
15059 case PM_CONTEXT_CASE_IN:
15064 case PM_CONTEXT_CLASS:
15066 case PM_CONTEXT_ELSE:
15067 case PM_CONTEXT_ELSIF:
15068 case PM_CONTEXT_EMBEXPR:
15070 case PM_CONTEXT_IF:
15074 case PM_CONTEXT_MODULE:
15076 case PM_CONTEXT_PARENS:
15079 case PM_CONTEXT_TERNARY:
15080 case PM_CONTEXT_UNLESS:
15081 // In these contexts we should continue walking up the list of
15082 // contexts.
15083 break;
15084 case PM_CONTEXT_NONE:
15085 // This case should never happen.
15086 assert(false && "unreachable");
15087 break;
15088 }
15089 }
15090}
15091
15096static pm_node_list_t *
15097push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
15098 pm_node_list_t *previous_block_exits = parser->current_block_exits;
15099 parser->current_block_exits = current_block_exits;
15100 return previous_block_exits;
15101}
15102
15108static void
15109flush_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
15110 pm_node_t *block_exit;
15111 PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
15112 const char *type;
15113
15114 switch (PM_NODE_TYPE(block_exit)) {
15115 case PM_BREAK_NODE: type = "break"; break;
15116 case PM_NEXT_NODE: type = "next"; break;
15117 case PM_REDO_NODE: type = "redo"; break;
15118 default: assert(false && "unreachable"); type = ""; break;
15119 }
15120
15121 PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
15122 }
15123
15124 parser->current_block_exits = previous_block_exits;
15125}
15126
15131static void
15132pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
15133 if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) {
15134 // If we matched a trailing while/until, then all of the block exits in
15135 // the contained list are valid. In this case we do not need to do
15136 // anything.
15137 parser->current_block_exits = previous_block_exits;
15138 } else if (previous_block_exits != NULL) {
15139 // If we did not matching a trailing while/until, then all of the block
15140 // exits contained in the list are invalid for this specific context.
15141 // However, they could still become valid in a higher level context if
15142 // there is another list above this one. In this case we'll push all of
15143 // the block exits up to the previous list.
15144 pm_node_list_concat(previous_block_exits, parser->current_block_exits);
15145 parser->current_block_exits = previous_block_exits;
15146 } else {
15147 // If we did not match a trailing while/until and this was the last
15148 // chance to do so, then all of the block exits in the list are invalid
15149 // and we need to add an error for each of them.
15150 flush_block_exits(parser, previous_block_exits);
15151 }
15152}
15153
15154static inline pm_node_t *
15155parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword, uint16_t depth) {
15156 context_push(parser, PM_CONTEXT_PREDICATE);
15157 pm_diagnostic_id_t error_id = context == PM_CONTEXT_IF ? PM_ERR_CONDITIONAL_IF_PREDICATE : PM_ERR_CONDITIONAL_UNLESS_PREDICATE;
15158 pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, error_id, (uint16_t) (depth + 1));
15159
15160 // Predicates are closed by a term, a "then", or a term and then a "then".
15161 bool predicate_closed = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15162
15163 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
15164 predicate_closed = true;
15165 *then_keyword = parser->previous;
15166 }
15167
15168 if (!predicate_closed) {
15169 pm_parser_err_current(parser, PM_ERR_CONDITIONAL_PREDICATE_TERM);
15170 }
15171
15172 context_pop(parser);
15173 return predicate;
15174}
15175
15176static inline pm_node_t *
15177parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newline_index, bool if_after_else, uint16_t depth) {
15178 pm_node_list_t current_block_exits = { 0 };
15179 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
15180
15181 pm_token_t keyword = parser->previous;
15182 pm_token_t then_keyword = not_provided(parser);
15183
15184 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER, context, &then_keyword, (uint16_t) (depth + 1));
15185 pm_statements_node_t *statements = NULL;
15186
15187 if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
15188 pm_accepts_block_stack_push(parser, true);
15189 statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15190 pm_accepts_block_stack_pop(parser);
15191 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15192 }
15193
15194 pm_token_t end_keyword = not_provided(parser);
15195 pm_node_t *parent = NULL;
15196
15197 switch (context) {
15198 case PM_CONTEXT_IF:
15199 parent = UP(pm_if_node_create(parser, &keyword, predicate, &then_keyword, statements, NULL, &end_keyword));
15200 break;
15201 case PM_CONTEXT_UNLESS:
15202 parent = UP(pm_unless_node_create(parser, &keyword, predicate, &then_keyword, statements));
15203 break;
15204 default:
15205 assert(false && "unreachable");
15206 break;
15207 }
15208
15209 pm_node_t *current = parent;
15210
15211 // Parse any number of elsif clauses. This will form a linked list of if
15212 // nodes pointing to each other from the top.
15213 if (context == PM_CONTEXT_IF) {
15214 while (match1(parser, PM_TOKEN_KEYWORD_ELSIF)) {
15215 if (parser_end_of_line_p(parser)) {
15216 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_KEYWORD_EOL);
15217 }
15218
15219 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
15220 pm_token_t elsif_keyword = parser->current;
15221 parser_lex(parser);
15222
15223 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_MODIFIER, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1));
15224 pm_accepts_block_stack_push(parser, true);
15225
15226 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1));
15227 pm_accepts_block_stack_pop(parser);
15228 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15229
15230 pm_node_t *elsif = UP(pm_if_node_create(parser, &elsif_keyword, predicate, &then_keyword, statements, NULL, &end_keyword));
15231 ((pm_if_node_t *) current)->subsequent = elsif;
15232 current = elsif;
15233 }
15234 }
15235
15236 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
15237 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
15238 opening_newline_index = token_newline_index(parser);
15239
15240 parser_lex(parser);
15241 pm_token_t else_keyword = parser->previous;
15242
15243 pm_accepts_block_stack_push(parser, true);
15244 pm_statements_node_t *else_statements = parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1));
15245 pm_accepts_block_stack_pop(parser);
15246
15247 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15248 parser_warn_indentation_mismatch(parser, opening_newline_index, &else_keyword, false, false);
15249 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE, &keyword);
15250
15251 pm_else_node_t *else_node = pm_else_node_create(parser, &else_keyword, else_statements, &parser->previous);
15252
15253 switch (context) {
15254 case PM_CONTEXT_IF:
15255 ((pm_if_node_t *) current)->subsequent = UP(else_node);
15256 break;
15257 case PM_CONTEXT_UNLESS:
15258 ((pm_unless_node_t *) parent)->else_clause = else_node;
15259 break;
15260 default:
15261 assert(false && "unreachable");
15262 break;
15263 }
15264 } else {
15265 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false);
15266 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM, &keyword);
15267 }
15268
15269 // Set the appropriate end location for all of the nodes in the subtree.
15270 switch (context) {
15271 case PM_CONTEXT_IF: {
15272 pm_node_t *current = parent;
15273 bool recursing = true;
15274
15275 while (recursing) {
15276 switch (PM_NODE_TYPE(current)) {
15277 case PM_IF_NODE:
15278 pm_if_node_end_keyword_loc_set((pm_if_node_t *) current, &parser->previous);
15279 current = ((pm_if_node_t *) current)->subsequent;
15280 recursing = current != NULL;
15281 break;
15282 case PM_ELSE_NODE:
15283 pm_else_node_end_keyword_loc_set((pm_else_node_t *) current, &parser->previous);
15284 recursing = false;
15285 break;
15286 default: {
15287 recursing = false;
15288 break;
15289 }
15290 }
15291 }
15292 break;
15293 }
15294 case PM_CONTEXT_UNLESS:
15295 pm_unless_node_end_keyword_loc_set((pm_unless_node_t *) parent, &parser->previous);
15296 break;
15297 default:
15298 assert(false && "unreachable");
15299 break;
15300 }
15301
15302 pop_block_exits(parser, previous_block_exits);
15303 pm_node_list_free(&current_block_exits);
15304
15305 return parent;
15306}
15307
15312#define PM_CASE_KEYWORD PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
15313 case PM_TOKEN_KEYWORD_ALIAS: case PM_TOKEN_KEYWORD_AND: case PM_TOKEN_KEYWORD_BEGIN: case PM_TOKEN_KEYWORD_BEGIN_UPCASE: \
15314 case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_CASE: case PM_TOKEN_KEYWORD_CLASS: case PM_TOKEN_KEYWORD_DEF: \
15315 case PM_TOKEN_KEYWORD_DEFINED: case PM_TOKEN_KEYWORD_DO: case PM_TOKEN_KEYWORD_DO_LOOP: case PM_TOKEN_KEYWORD_ELSE: \
15316 case PM_TOKEN_KEYWORD_ELSIF: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_END_UPCASE: case PM_TOKEN_KEYWORD_ENSURE: \
15317 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD_FOR: case PM_TOKEN_KEYWORD_IF: case PM_TOKEN_KEYWORD_IN: \
15318 case PM_TOKEN_KEYWORD_MODULE: case PM_TOKEN_KEYWORD_NEXT: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_NOT: \
15319 case PM_TOKEN_KEYWORD_OR: case PM_TOKEN_KEYWORD_REDO: case PM_TOKEN_KEYWORD_RESCUE: case PM_TOKEN_KEYWORD_RETRY: \
15320 case PM_TOKEN_KEYWORD_RETURN: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_SUPER: case PM_TOKEN_KEYWORD_THEN: \
15321 case PM_TOKEN_KEYWORD_TRUE: case PM_TOKEN_KEYWORD_UNDEF: case PM_TOKEN_KEYWORD_UNLESS: case PM_TOKEN_KEYWORD_UNTIL: \
15322 case PM_TOKEN_KEYWORD_WHEN: case PM_TOKEN_KEYWORD_WHILE: case PM_TOKEN_KEYWORD_YIELD
15323
15328#define PM_CASE_OPERATOR PM_TOKEN_AMPERSAND: case PM_TOKEN_BACKTICK: case PM_TOKEN_BANG_EQUAL: \
15329 case PM_TOKEN_BANG_TILDE: case PM_TOKEN_BANG: case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \
15330 case PM_TOKEN_BRACKET_LEFT_RIGHT: case PM_TOKEN_CARET: case PM_TOKEN_EQUAL_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL: \
15331 case PM_TOKEN_EQUAL_TILDE: case PM_TOKEN_GREATER_EQUAL: case PM_TOKEN_GREATER_GREATER: case PM_TOKEN_GREATER: \
15332 case PM_TOKEN_LESS_EQUAL_GREATER: case PM_TOKEN_LESS_EQUAL: case PM_TOKEN_LESS_LESS: case PM_TOKEN_LESS: \
15333 case PM_TOKEN_MINUS: case PM_TOKEN_PERCENT: case PM_TOKEN_PIPE: case PM_TOKEN_PLUS: case PM_TOKEN_SLASH: \
15334 case PM_TOKEN_STAR_STAR: case PM_TOKEN_STAR: case PM_TOKEN_TILDE: case PM_TOKEN_UAMPERSAND: case PM_TOKEN_UMINUS: \
15335 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_UPLUS: case PM_TOKEN_USTAR: case PM_TOKEN_USTAR_STAR
15336
15342#define PM_CASE_PRIMITIVE PM_TOKEN_INTEGER: case PM_TOKEN_INTEGER_IMAGINARY: case PM_TOKEN_INTEGER_RATIONAL: \
15343 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: case PM_TOKEN_FLOAT: case PM_TOKEN_FLOAT_IMAGINARY: \
15344 case PM_TOKEN_FLOAT_RATIONAL: case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: case PM_TOKEN_SYMBOL_BEGIN: \
15345 case PM_TOKEN_REGEXP_BEGIN: case PM_TOKEN_BACKTICK: case PM_TOKEN_PERCENT_LOWER_X: case PM_TOKEN_PERCENT_LOWER_I: \
15346 case PM_TOKEN_PERCENT_LOWER_W: case PM_TOKEN_PERCENT_UPPER_I: case PM_TOKEN_PERCENT_UPPER_W: \
15347 case PM_TOKEN_STRING_BEGIN: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_TRUE: \
15348 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
15349 case PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_MINUS_GREATER: case PM_TOKEN_HEREDOC_START: \
15350 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_CHARACTER_LITERAL
15351
15356#define PM_CASE_PARAMETER PM_TOKEN_UAMPERSAND: case PM_TOKEN_AMPERSAND: case PM_TOKEN_UDOT_DOT_DOT: \
15357 case PM_TOKEN_IDENTIFIER: case PM_TOKEN_LABEL: case PM_TOKEN_USTAR: case PM_TOKEN_STAR: case PM_TOKEN_STAR_STAR: \
15358 case PM_TOKEN_USTAR_STAR: case PM_TOKEN_CONSTANT: case PM_TOKEN_INSTANCE_VARIABLE: case PM_TOKEN_GLOBAL_VARIABLE: \
15359 case PM_TOKEN_CLASS_VARIABLE
15360
15365#define PM_CASE_WRITABLE PM_CLASS_VARIABLE_READ_NODE: case PM_CONSTANT_PATH_NODE: \
15366 case PM_CONSTANT_READ_NODE: case PM_GLOBAL_VARIABLE_READ_NODE: case PM_LOCAL_VARIABLE_READ_NODE: \
15367 case PM_INSTANCE_VARIABLE_READ_NODE: case PM_MULTI_TARGET_NODE: case PM_BACK_REFERENCE_READ_NODE: \
15368 case PM_NUMBERED_REFERENCE_READ_NODE: case PM_IT_LOCAL_VARIABLE_READ_NODE
15369
15370// Assert here that the flags are the same so that we can safely switch the type
15371// of the node without having to move the flags.
15372PM_STATIC_ASSERT(__LINE__, ((int) PM_STRING_FLAGS_FORCED_UTF8_ENCODING) == ((int) PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING), "Expected the flags to match.");
15373
15378static inline pm_node_flags_t
15379parse_unescaped_encoding(const pm_parser_t *parser) {
15380 if (parser->explicit_encoding != NULL) {
15382 // If the there's an explicit encoding and it's using a UTF-8 escape
15383 // sequence, then mark the string as UTF-8.
15384 return PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
15385 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
15386 // If there's a non-UTF-8 escape sequence being used, then the
15387 // string uses the source encoding, unless the source is marked as
15388 // US-ASCII. In that case the string is forced as ASCII-8BIT in
15389 // order to keep the string valid.
15390 return PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
15391 }
15392 }
15393 return 0;
15394}
15395
15400static pm_node_t *
15401parse_string_part(pm_parser_t *parser, uint16_t depth) {
15402 switch (parser->current.type) {
15403 // Here the lexer has returned to us plain string content. In this case
15404 // we'll create a string node that has no opening or closing and return that
15405 // as the part. These kinds of parts look like:
15406 //
15407 // "aaa #{bbb} #@ccc ddd"
15408 // ^^^^ ^ ^^^^
15409 case PM_TOKEN_STRING_CONTENT: {
15410 pm_token_t opening = not_provided(parser);
15411 pm_token_t closing = not_provided(parser);
15412
15413 pm_node_t *node = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing));
15414 pm_node_flag_set(node, parse_unescaped_encoding(parser));
15415
15416 parser_lex(parser);
15417 return node;
15418 }
15419 // Here the lexer has returned the beginning of an embedded expression. In
15420 // that case we'll parse the inner statements and return that as the part.
15421 // These kinds of parts look like:
15422 //
15423 // "aaa #{bbb} #@ccc ddd"
15424 // ^^^^^^
15425 case PM_TOKEN_EMBEXPR_BEGIN: {
15426 // Ruby disallows seeing encoding around interpolation in strings,
15427 // even though it is known at parse time.
15428 parser->explicit_encoding = NULL;
15429
15430 pm_lex_state_t state = parser->lex_state;
15431 int brace_nesting = parser->brace_nesting;
15432
15433 parser->brace_nesting = 0;
15434 lex_state_set(parser, PM_LEX_STATE_BEG);
15435 parser_lex(parser);
15436
15437 pm_token_t opening = parser->previous;
15438 pm_statements_node_t *statements = NULL;
15439
15440 if (!match1(parser, PM_TOKEN_EMBEXPR_END)) {
15441 pm_accepts_block_stack_push(parser, true);
15442 statements = parse_statements(parser, PM_CONTEXT_EMBEXPR, (uint16_t) (depth + 1));
15443 pm_accepts_block_stack_pop(parser);
15444 }
15445
15446 parser->brace_nesting = brace_nesting;
15447 lex_state_set(parser, state);
15448
15449 expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END);
15450 pm_token_t closing = parser->previous;
15451
15452 // If this set of embedded statements only contains a single
15453 // statement, then Ruby does not consider it as a possible statement
15454 // that could emit a line event.
15455 if (statements != NULL && statements->body.size == 1) {
15456 pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE);
15457 }
15458
15459 return UP(pm_embedded_statements_node_create(parser, &opening, statements, &closing));
15460 }
15461
15462 // Here the lexer has returned the beginning of an embedded variable.
15463 // In that case we'll parse the variable and create an appropriate node
15464 // for it and then return that node. These kinds of parts look like:
15465 //
15466 // "aaa #{bbb} #@ccc ddd"
15467 // ^^^^^
15468 case PM_TOKEN_EMBVAR: {
15469 // Ruby disallows seeing encoding around interpolation in strings,
15470 // even though it is known at parse time.
15471 parser->explicit_encoding = NULL;
15472
15473 lex_state_set(parser, PM_LEX_STATE_BEG);
15474 parser_lex(parser);
15475
15476 pm_token_t operator = parser->previous;
15477 pm_node_t *variable;
15478
15479 switch (parser->current.type) {
15480 // In this case a back reference is being interpolated. We'll
15481 // create a global variable read node.
15482 case PM_TOKEN_BACK_REFERENCE:
15483 parser_lex(parser);
15484 variable = UP(pm_back_reference_read_node_create(parser, &parser->previous));
15485 break;
15486 // In this case an nth reference is being interpolated. We'll
15487 // create a global variable read node.
15488 case PM_TOKEN_NUMBERED_REFERENCE:
15489 parser_lex(parser);
15490 variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
15491 break;
15492 // In this case a global variable is being interpolated. We'll
15493 // create a global variable read node.
15494 case PM_TOKEN_GLOBAL_VARIABLE:
15495 parser_lex(parser);
15496 variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
15497 break;
15498 // In this case an instance variable is being interpolated.
15499 // We'll create an instance variable read node.
15500 case PM_TOKEN_INSTANCE_VARIABLE:
15501 parser_lex(parser);
15502 variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
15503 break;
15504 // In this case a class variable is being interpolated. We'll
15505 // create a class variable read node.
15506 case PM_TOKEN_CLASS_VARIABLE:
15507 parser_lex(parser);
15508 variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
15509 break;
15510 // We can hit here if we got an invalid token. In that case
15511 // we'll not attempt to lex this token and instead just return a
15512 // missing node.
15513 default:
15514 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EMBVAR_INVALID);
15515 variable = UP(pm_missing_node_create(parser, parser->current.start, parser->current.end));
15516 break;
15517 }
15518
15519 return UP(pm_embedded_variable_node_create(parser, &operator, variable));
15520 }
15521 default:
15522 parser_lex(parser);
15523 pm_parser_err_previous(parser, PM_ERR_CANNOT_PARSE_STRING_PART);
15524 return NULL;
15525 }
15526}
15527
15533static const uint8_t *
15534parse_operator_symbol_name(const pm_token_t *name) {
15535 switch (name->type) {
15536 case PM_TOKEN_TILDE:
15537 case PM_TOKEN_BANG:
15538 if (name->end[-1] == '@') return name->end - 1;
15540 default:
15541 return name->end;
15542 }
15543}
15544
15545static pm_node_t *
15546parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_state_t next_state) {
15547 pm_token_t closing = not_provided(parser);
15548 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, opening, &parser->current, &closing);
15549
15550 const uint8_t *end = parse_operator_symbol_name(&parser->current);
15551
15552 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
15553 parser_lex(parser);
15554
15555 pm_string_shared_init(&symbol->unescaped, parser->previous.start, end);
15556 pm_node_flag_set(UP(symbol), PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING);
15557
15558 return UP(symbol);
15559}
15560
15566static pm_node_t *
15567parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_state, uint16_t depth) {
15568 const pm_token_t opening = parser->previous;
15569
15570 if (lex_mode->mode != PM_LEX_STRING) {
15571 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
15572
15573 switch (parser->current.type) {
15574 case PM_CASE_OPERATOR:
15575 return parse_operator_symbol(parser, &opening, next_state == PM_LEX_STATE_NONE ? PM_LEX_STATE_ENDFN : next_state);
15576 case PM_TOKEN_IDENTIFIER:
15577 case PM_TOKEN_CONSTANT:
15578 case PM_TOKEN_INSTANCE_VARIABLE:
15579 case PM_TOKEN_METHOD_NAME:
15580 case PM_TOKEN_CLASS_VARIABLE:
15581 case PM_TOKEN_GLOBAL_VARIABLE:
15582 case PM_TOKEN_NUMBERED_REFERENCE:
15583 case PM_TOKEN_BACK_REFERENCE:
15584 case PM_CASE_KEYWORD:
15585 parser_lex(parser);
15586 break;
15587 default:
15588 expect2(parser, PM_TOKEN_IDENTIFIER, PM_TOKEN_METHOD_NAME, PM_ERR_SYMBOL_INVALID);
15589 break;
15590 }
15591
15592 pm_token_t closing = not_provided(parser);
15593 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing);
15594
15595 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
15596 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false));
15597
15598 return UP(symbol);
15599 }
15600
15601 if (lex_mode->as.string.interpolation) {
15602 // If we have the end of the symbol, then we can return an empty symbol.
15603 if (match1(parser, PM_TOKEN_STRING_END)) {
15604 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
15605 parser_lex(parser);
15606
15607 pm_token_t content = not_provided(parser);
15608 pm_token_t closing = parser->previous;
15609 return UP(pm_symbol_node_create(parser, &opening, &content, &closing));
15610 }
15611
15612 // Now we can parse the first part of the symbol.
15613 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
15614
15615 // If we got a string part, then it's possible that we could transform
15616 // what looks like an interpolated symbol into a regular symbol.
15617 if (part && PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
15618 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
15619 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
15620
15621 return UP(pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous));
15622 }
15623
15624 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
15625 if (part) pm_interpolated_symbol_node_append(symbol, part);
15626
15627 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
15628 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
15629 pm_interpolated_symbol_node_append(symbol, part);
15630 }
15631 }
15632
15633 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
15634 if (match1(parser, PM_TOKEN_EOF)) {
15635 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_INTERPOLATED);
15636 } else {
15637 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
15638 }
15639
15640 pm_interpolated_symbol_node_closing_loc_set(symbol, &parser->previous);
15641 return UP(symbol);
15642 }
15643
15644 pm_token_t content;
15645 pm_string_t unescaped;
15646
15647 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
15648 content = parser->current;
15649 unescaped = parser->current_string;
15650 parser_lex(parser);
15651
15652 // If we have two string contents in a row, then the content of this
15653 // symbol is split because of heredoc contents. This looks like:
15654 //
15655 // <<A; :'a
15656 // A
15657 // b'
15658 //
15659 // In this case, the best way we have to represent this is as an
15660 // interpolated string node, so that's what we'll do here.
15661 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
15662 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
15663 pm_token_t bounds = not_provided(parser);
15664
15665 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &unescaped));
15666 pm_interpolated_symbol_node_append(symbol, part);
15667
15668 part = UP(pm_string_node_create_unescaped(parser, &bounds, &parser->current, &bounds, &parser->current_string));
15669 pm_interpolated_symbol_node_append(symbol, part);
15670
15671 if (next_state != PM_LEX_STATE_NONE) {
15672 lex_state_set(parser, next_state);
15673 }
15674
15675 parser_lex(parser);
15676 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
15677
15678 pm_interpolated_symbol_node_closing_loc_set(symbol, &parser->previous);
15679 return UP(symbol);
15680 }
15681 } else {
15682 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end };
15683 pm_string_shared_init(&unescaped, content.start, content.end);
15684 }
15685
15686 if (next_state != PM_LEX_STATE_NONE) {
15687 lex_state_set(parser, next_state);
15688 }
15689
15690 if (match1(parser, PM_TOKEN_EOF)) {
15691 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_DYNAMIC);
15692 } else {
15693 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
15694 }
15695
15696 return UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, false)));
15697}
15698
15703static inline pm_node_t *
15704parse_undef_argument(pm_parser_t *parser, uint16_t depth) {
15705 switch (parser->current.type) {
15706 case PM_CASE_OPERATOR: {
15707 const pm_token_t opening = not_provided(parser);
15708 return parse_operator_symbol(parser, &opening, PM_LEX_STATE_NONE);
15709 }
15710 case PM_CASE_KEYWORD:
15711 case PM_TOKEN_CONSTANT:
15712 case PM_TOKEN_IDENTIFIER:
15713 case PM_TOKEN_METHOD_NAME: {
15714 parser_lex(parser);
15715
15716 pm_token_t opening = not_provided(parser);
15717 pm_token_t closing = not_provided(parser);
15718 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing);
15719
15720 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
15721 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false));
15722
15723 return UP(symbol);
15724 }
15725 case PM_TOKEN_SYMBOL_BEGIN: {
15726 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
15727 parser_lex(parser);
15728
15729 return parse_symbol(parser, &lex_mode, PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
15730 }
15731 default:
15732 pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT);
15733 return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end));
15734 }
15735}
15736
15743static inline pm_node_t *
15744parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) {
15745 switch (parser->current.type) {
15746 case PM_CASE_OPERATOR: {
15747 const pm_token_t opening = not_provided(parser);
15748 return parse_operator_symbol(parser, &opening, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE);
15749 }
15750 case PM_CASE_KEYWORD:
15751 case PM_TOKEN_CONSTANT:
15752 case PM_TOKEN_IDENTIFIER:
15753 case PM_TOKEN_METHOD_NAME: {
15754 if (first) lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
15755 parser_lex(parser);
15756
15757 pm_token_t opening = not_provided(parser);
15758 pm_token_t closing = not_provided(parser);
15759 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, &closing);
15760
15761 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
15762 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, &parser->previous, &symbol->unescaped, false));
15763
15764 return UP(symbol);
15765 }
15766 case PM_TOKEN_SYMBOL_BEGIN: {
15767 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
15768 parser_lex(parser);
15769
15770 return parse_symbol(parser, &lex_mode, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
15771 }
15772 case PM_TOKEN_BACK_REFERENCE:
15773 parser_lex(parser);
15774 return UP(pm_back_reference_read_node_create(parser, &parser->previous));
15775 case PM_TOKEN_NUMBERED_REFERENCE:
15776 parser_lex(parser);
15777 return UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
15778 case PM_TOKEN_GLOBAL_VARIABLE:
15779 parser_lex(parser);
15780 return UP(pm_global_variable_read_node_create(parser, &parser->previous));
15781 default:
15782 pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT);
15783 return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end));
15784 }
15785}
15786
15791static pm_node_t *
15792parse_variable(pm_parser_t *parser) {
15793 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &parser->previous);
15794 int depth;
15795 bool is_numbered_param = pm_token_is_numbered_parameter(parser->previous.start, parser->previous.end);
15796
15797 if (!is_numbered_param && ((depth = pm_parser_local_depth_constant_id(parser, name_id)) != -1)) {
15798 return UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false));
15799 }
15800
15801 pm_scope_t *current_scope = parser->current_scope;
15802 if (!current_scope->closed && !(current_scope->parameters & PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED)) {
15803 if (is_numbered_param) {
15804 // When you use a numbered parameter, it implies the existence of
15805 // all of the locals that exist before it. For example, referencing
15806 // _2 means that _1 must exist. Therefore here we loop through all
15807 // of the possibilities and add them into the constant pool.
15808 uint8_t maximum = (uint8_t) (parser->previous.start[1] - '0');
15809 for (uint8_t number = 1; number <= maximum; number++) {
15810 pm_parser_local_add_constant(parser, pm_numbered_parameter_names[number - 1], 2);
15811 }
15812
15813 if (!match1(parser, PM_TOKEN_EQUAL)) {
15814 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_FOUND;
15815 }
15816
15817 pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false));
15818 pm_node_list_append(&current_scope->implicit_parameters, node);
15819
15820 return node;
15821 } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
15822 pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous));
15823 pm_node_list_append(&current_scope->implicit_parameters, node);
15824
15825 return node;
15826 }
15827 }
15828
15829 return NULL;
15830}
15831
15835static pm_node_t *
15836parse_variable_call(pm_parser_t *parser) {
15837 pm_node_flags_t flags = 0;
15838
15839 if (!match1(parser, PM_TOKEN_PARENTHESIS_LEFT) && (parser->previous.end[-1] != '!') && (parser->previous.end[-1] != '?')) {
15840 pm_node_t *node = parse_variable(parser);
15841 if (node != NULL) return node;
15842 flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL;
15843 }
15844
15845 pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous);
15846 pm_node_flag_set(UP(node), flags);
15847
15848 return UP(node);
15849}
15850
15856static inline pm_token_t
15857parse_method_definition_name(pm_parser_t *parser) {
15858 switch (parser->current.type) {
15859 case PM_CASE_KEYWORD:
15860 case PM_TOKEN_CONSTANT:
15861 case PM_TOKEN_METHOD_NAME:
15862 parser_lex(parser);
15863 return parser->previous;
15864 case PM_TOKEN_IDENTIFIER:
15865 pm_refute_numbered_parameter(parser, parser->current.start, parser->current.end);
15866 parser_lex(parser);
15867 return parser->previous;
15868 case PM_CASE_OPERATOR:
15869 lex_state_set(parser, PM_LEX_STATE_ENDFN);
15870 parser_lex(parser);
15871 return parser->previous;
15872 default:
15873 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_DEF_NAME, pm_token_type_human(parser->current.type));
15874 return (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->current.start, .end = parser->current.end };
15875 }
15876}
15877
15878static void
15879parse_heredoc_dedent_string(pm_string_t *string, size_t common_whitespace) {
15880 // Get a reference to the string struct that is being held by the string
15881 // node. This is the value we're going to actually manipulate.
15882 pm_string_ensure_owned(string);
15883
15884 // Now get the bounds of the existing string. We'll use this as a
15885 // destination to move bytes into. We'll also use it for bounds checking
15886 // since we don't require that these strings be null terminated.
15887 size_t dest_length = pm_string_length(string);
15888 const uint8_t *source_cursor = (uint8_t *) string->source;
15889 const uint8_t *source_end = source_cursor + dest_length;
15890
15891 // We're going to move bytes backward in the string when we get leading
15892 // whitespace, so we'll maintain a pointer to the current position in the
15893 // string that we're writing to.
15894 size_t trimmed_whitespace = 0;
15895
15896 // While we haven't reached the amount of common whitespace that we need to
15897 // trim and we haven't reached the end of the string, we'll keep trimming
15898 // whitespace. Trimming in this context means skipping over these bytes such
15899 // that they aren't copied into the new string.
15900 while ((source_cursor < source_end) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
15901 if (*source_cursor == '\t') {
15902 trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
15903 if (trimmed_whitespace > common_whitespace) break;
15904 } else {
15905 trimmed_whitespace++;
15906 }
15907
15908 source_cursor++;
15909 dest_length--;
15910 }
15911
15912 memmove((uint8_t *) string->source, source_cursor, (size_t) (source_end - source_cursor));
15913 string->length = dest_length;
15914}
15915
15919static void
15920parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_whitespace) {
15921 // The next node should be dedented if it's the first node in the list or if
15922 // it follows a string node.
15923 bool dedent_next = true;
15924
15925 // Iterate over all nodes, and trim whitespace accordingly. We're going to
15926 // keep around two indices: a read and a write. If we end up trimming all of
15927 // the whitespace from a node, then we'll drop it from the list entirely.
15928 size_t write_index = 0;
15929
15930 pm_node_t *node;
15931 PM_NODE_LIST_FOREACH(nodes, read_index, node) {
15932 // We're not manipulating child nodes that aren't strings. In this case
15933 // we'll skip past it and indicate that the subsequent node should not
15934 // be dedented.
15935 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE)) {
15936 nodes->nodes[write_index++] = node;
15937 dedent_next = false;
15938 continue;
15939 }
15940
15941 pm_string_node_t *string_node = ((pm_string_node_t *) node);
15942 if (dedent_next) {
15943 parse_heredoc_dedent_string(&string_node->unescaped, common_whitespace);
15944 }
15945
15946 if (string_node->unescaped.length == 0) {
15947 pm_node_destroy(parser, node);
15948 } else {
15949 nodes->nodes[write_index++] = node;
15950 }
15951
15952 // We always dedent the next node if it follows a string node.
15953 dedent_next = true;
15954 }
15955
15956 nodes->size = write_index;
15957}
15958
15962static pm_token_t
15963parse_strings_empty_content(const uint8_t *location) {
15964 return (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = location, .end = location };
15965}
15966
15970static inline pm_node_t *
15971parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint16_t depth) {
15972 assert(parser->current.type == PM_TOKEN_STRING_BEGIN);
15973 bool concating = false;
15974
15975 while (match1(parser, PM_TOKEN_STRING_BEGIN)) {
15976 pm_node_t *node = NULL;
15977
15978 // Here we have found a string literal. We'll parse it and add it to
15979 // the list of strings.
15980 const pm_lex_mode_t *lex_mode = parser->lex_modes.current;
15981 assert(lex_mode->mode == PM_LEX_STRING);
15982 bool lex_interpolation = lex_mode->as.string.interpolation;
15983 bool label_allowed = lex_mode->as.string.label_allowed && accepts_label;
15984
15985 pm_token_t opening = parser->current;
15986 parser_lex(parser);
15987
15988 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
15989 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
15990 // If we get here, then we have an end immediately after a
15991 // start. In that case we'll create an empty content token and
15992 // return an uninterpolated string.
15993 pm_token_t content = parse_strings_empty_content(parser->previous.start);
15994 pm_string_node_t *string = pm_string_node_create(parser, &opening, &content, &parser->previous);
15995
15996 pm_string_shared_init(&string->unescaped, content.start, content.end);
15997 node = UP(string);
15998 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
15999 // If we get here, then we have an end of a label immediately
16000 // after a start. In that case we'll create an empty symbol
16001 // node.
16002 pm_token_t content = parse_strings_empty_content(parser->previous.start);
16003 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &content, &parser->previous);
16004
16005 pm_string_shared_init(&symbol->unescaped, content.start, content.end);
16006 node = UP(symbol);
16007
16008 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16009 } else if (!lex_interpolation) {
16010 // If we don't accept interpolation then we expect the string to
16011 // start with a single string content node.
16012 pm_string_t unescaped;
16013 pm_token_t content;
16014
16015 if (match1(parser, PM_TOKEN_EOF)) {
16016 unescaped = PM_STRING_EMPTY;
16017 content = not_provided(parser);
16018 } else {
16019 unescaped = parser->current_string;
16020 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_EXPECT_STRING_CONTENT);
16021 content = parser->previous;
16022 }
16023
16024 // It is unfortunately possible to have multiple string content
16025 // nodes in a row in the case that there's heredoc content in
16026 // the middle of the string, like this cursed example:
16027 //
16028 // <<-END+'b
16029 // a
16030 // END
16031 // c'+'d'
16032 //
16033 // In that case we need to switch to an interpolated string to
16034 // be able to contain all of the parts.
16035 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16036 pm_node_list_t parts = { 0 };
16037
16038 pm_token_t delimiters = not_provided(parser);
16039 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &delimiters, &content, &delimiters, &unescaped));
16040 pm_node_list_append(&parts, part);
16041
16042 do {
16043 part = UP(pm_string_node_create_current_string(parser, &delimiters, &parser->current, &delimiters));
16044 pm_node_list_append(&parts, part);
16045 parser_lex(parser);
16046 } while (match1(parser, PM_TOKEN_STRING_CONTENT));
16047
16048 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16049 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16050
16051 pm_node_list_free(&parts);
16052 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16053 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)));
16054 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16055 } else if (match1(parser, PM_TOKEN_EOF)) {
16056 pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF);
16057 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16058 } else if (accept1(parser, PM_TOKEN_STRING_END)) {
16059 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16060 } else {
16061 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_type_human(parser->previous.type));
16062 parser->previous.start = parser->previous.end;
16063 parser->previous.type = PM_TOKEN_MISSING;
16064 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16065 }
16066 } else if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16067 // In this case we've hit string content so we know the string
16068 // at least has something in it. We'll need to check if the
16069 // following token is the end (in which case we can return a
16070 // plain string) or if it's not then it has interpolation.
16071 pm_token_t content = parser->current;
16072 pm_string_t unescaped = parser->current_string;
16073 parser_lex(parser);
16074
16075 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16076 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16077 pm_node_flag_set(node, parse_unescaped_encoding(parser));
16078
16079 // Kind of odd behavior, but basically if we have an
16080 // unterminated string and it ends in a newline, we back up one
16081 // character so that the error message is on the last line of
16082 // content in the string.
16083 if (!accept1(parser, PM_TOKEN_STRING_END)) {
16084 const uint8_t *location = parser->previous.end;
16085 if (location > parser->start && location[-1] == '\n') location--;
16086 pm_parser_err(parser, location, location, PM_ERR_STRING_LITERAL_EOF);
16087
16088 parser->previous.start = parser->previous.end;
16089 parser->previous.type = PM_TOKEN_MISSING;
16090 }
16091 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16092 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, &content, &unescaped, true)));
16093 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16094 } else {
16095 // If we get here, then we have interpolation so we'll need
16096 // to create a string or symbol node with interpolation.
16097 pm_node_list_t parts = { 0 };
16098 pm_token_t string_opening = not_provided(parser);
16099 pm_token_t string_closing = not_provided(parser);
16100
16101 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &string_opening, &parser->previous, &string_closing, &unescaped));
16102 pm_node_flag_set(part, parse_unescaped_encoding(parser));
16103 pm_node_list_append(&parts, part);
16104
16105 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
16106 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16107 pm_node_list_append(&parts, part);
16108 }
16109 }
16110
16111 if (accept1(parser, PM_TOKEN_LABEL_END)) {
16112 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
16113 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16114 } else if (match1(parser, PM_TOKEN_EOF)) {
16115 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
16116 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
16117 } else {
16118 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
16119 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16120 }
16121
16122 pm_node_list_free(&parts);
16123 }
16124 } else {
16125 // If we get here, then the first part of the string is not plain
16126 // string content, in which case we need to parse the string as an
16127 // interpolated string.
16128 pm_node_list_t parts = { 0 };
16129 pm_node_t *part;
16130
16131 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
16132 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16133 pm_node_list_append(&parts, part);
16134 }
16135 }
16136
16137 if (accept1(parser, PM_TOKEN_LABEL_END)) {
16138 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
16139 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16140 } else if (match1(parser, PM_TOKEN_EOF)) {
16141 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
16142 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
16143 } else {
16144 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
16145 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16146 }
16147
16148 pm_node_list_free(&parts);
16149 }
16150
16151 if (current == NULL) {
16152 // If the node we just parsed is a symbol node, then we can't
16153 // concatenate it with anything else, so we can now return that
16154 // node.
16155 if (PM_NODE_TYPE_P(node, PM_SYMBOL_NODE) || PM_NODE_TYPE_P(node, PM_INTERPOLATED_SYMBOL_NODE)) {
16156 return node;
16157 }
16158
16159 // If we don't already have a node, then it's fine and we can just
16160 // set the result to be the node we just parsed.
16161 current = node;
16162 } else {
16163 // Otherwise we need to check the type of the node we just parsed.
16164 // If it cannot be concatenated with the previous node, then we'll
16165 // need to add a syntax error.
16166 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE) && !PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
16167 pm_parser_err_node(parser, node, PM_ERR_STRING_CONCATENATION);
16168 }
16169
16170 // If we haven't already created our container for concatenation,
16171 // we'll do that now.
16172 if (!concating) {
16173 if (!PM_NODE_TYPE_P(current, PM_STRING_NODE) && !PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
16174 pm_parser_err_node(parser, current, PM_ERR_STRING_CONCATENATION);
16175 }
16176
16177 concating = true;
16178 pm_token_t bounds = not_provided(parser);
16179
16180 pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, &bounds, NULL, &bounds);
16181 pm_interpolated_string_node_append(container, current);
16182 current = UP(container);
16183 }
16184
16185 pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, node);
16186 }
16187 }
16188
16189 return current;
16190}
16191
16192#define PM_PARSE_PATTERN_SINGLE 0
16193#define PM_PARSE_PATTERN_TOP 1
16194#define PM_PARSE_PATTERN_MULTI 2
16195
16196static pm_node_t *
16197parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
16198
16204static void
16205parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_constant_id_t capture, const pm_location_t *location) {
16206 // Skip this capture if it starts with an underscore.
16207 if (peek_at(parser, location->start) == '_') return;
16208
16209 if (pm_constant_id_list_includes(captures, capture)) {
16210 pm_parser_err(parser, location->start, location->end, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
16211 } else {
16212 pm_constant_id_list_append(captures, capture);
16213 }
16214}
16215
16219static pm_node_t *
16220parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *node, uint16_t depth) {
16221 // Now, if there are any :: operators that follow, parse them as constant
16222 // path nodes.
16223 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
16224 pm_token_t delimiter = parser->previous;
16225 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
16226 node = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
16227 }
16228
16229 // If there is a [ or ( that follows, then this is part of a larger pattern
16230 // expression. We'll parse the inner pattern here, then modify the returned
16231 // inner pattern with our constant path attached.
16232 if (!match2(parser, PM_TOKEN_BRACKET_LEFT, PM_TOKEN_PARENTHESIS_LEFT)) {
16233 return node;
16234 }
16235
16236 pm_token_t opening;
16237 pm_token_t closing;
16238 pm_node_t *inner = NULL;
16239
16240 if (accept1(parser, PM_TOKEN_BRACKET_LEFT)) {
16241 opening = parser->previous;
16242 accept1(parser, PM_TOKEN_NEWLINE);
16243
16244 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
16245 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
16246 accept1(parser, PM_TOKEN_NEWLINE);
16247 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
16248 }
16249
16250 closing = parser->previous;
16251 } else {
16252 parser_lex(parser);
16253 opening = parser->previous;
16254 accept1(parser, PM_TOKEN_NEWLINE);
16255
16256 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
16257 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
16258 accept1(parser, PM_TOKEN_NEWLINE);
16259 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
16260 }
16261
16262 closing = parser->previous;
16263 }
16264
16265 if (!inner) {
16266 // If there was no inner pattern, then we have something like Foo() or
16267 // Foo[]. In that case we'll create an array pattern with no requireds.
16268 return UP(pm_array_pattern_node_constant_create(parser, node, &opening, &closing));
16269 }
16270
16271 // Now that we have the inner pattern, check to see if it's an array, find,
16272 // or hash pattern. If it is, then we'll attach our constant path to it if
16273 // it doesn't already have a constant. If it's not one of those node types
16274 // or it does have a constant, then we'll create an array pattern.
16275 switch (PM_NODE_TYPE(inner)) {
16276 case PM_ARRAY_PATTERN_NODE: {
16277 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
16278
16279 if (pattern_node->constant == NULL && pattern_node->opening_loc.start == NULL) {
16280 pattern_node->base.location.start = node->location.start;
16281 pattern_node->base.location.end = closing.end;
16282
16283 pattern_node->constant = node;
16284 pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
16285 pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
16286
16287 return UP(pattern_node);
16288 }
16289
16290 break;
16291 }
16292 case PM_FIND_PATTERN_NODE: {
16293 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
16294
16295 if (pattern_node->constant == NULL && pattern_node->opening_loc.start == NULL) {
16296 pattern_node->base.location.start = node->location.start;
16297 pattern_node->base.location.end = closing.end;
16298
16299 pattern_node->constant = node;
16300 pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
16301 pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
16302
16303 return UP(pattern_node);
16304 }
16305
16306 break;
16307 }
16308 case PM_HASH_PATTERN_NODE: {
16309 pm_hash_pattern_node_t *pattern_node = (pm_hash_pattern_node_t *) inner;
16310
16311 if (pattern_node->constant == NULL && pattern_node->opening_loc.start == NULL) {
16312 pattern_node->base.location.start = node->location.start;
16313 pattern_node->base.location.end = closing.end;
16314
16315 pattern_node->constant = node;
16316 pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
16317 pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
16318
16319 return UP(pattern_node);
16320 }
16321
16322 break;
16323 }
16324 default:
16325 break;
16326 }
16327
16328 // If we got here, then we didn't return one of the inner patterns by
16329 // attaching its constant. In this case we'll create an array pattern and
16330 // attach our constant to it.
16331 pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing);
16332 pm_array_pattern_node_requireds_append(pattern_node, inner);
16333 return UP(pattern_node);
16334}
16335
16339static pm_splat_node_t *
16340parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
16341 assert(parser->previous.type == PM_TOKEN_USTAR);
16342 pm_token_t operator = parser->previous;
16343 pm_node_t *name = NULL;
16344
16345 // Rest patterns don't necessarily have a name associated with them. So we
16346 // will check for that here. If they do, then we'll add it to the local
16347 // table since this pattern will cause it to become a local variable.
16348 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
16349 pm_token_t identifier = parser->previous;
16350 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &identifier);
16351
16352 int depth;
16353 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
16354 pm_parser_local_add(parser, constant_id, identifier.start, identifier.end, 0);
16355 }
16356
16357 parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&identifier));
16358 name = UP(pm_local_variable_target_node_create(
16359 parser,
16360 &PM_LOCATION_TOKEN_VALUE(&identifier),
16361 constant_id,
16362 (uint32_t) (depth == -1 ? 0 : depth)
16363 ));
16364 }
16365
16366 // Finally we can return the created node.
16367 return pm_splat_node_create(parser, &operator, name);
16368}
16369
16373static pm_node_t *
16374parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
16375 assert(parser->current.type == PM_TOKEN_USTAR_STAR);
16376 parser_lex(parser);
16377
16378 pm_token_t operator = parser->previous;
16379 pm_node_t *value = NULL;
16380
16381 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
16382 return UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
16383 }
16384
16385 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
16386 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
16387
16388 int depth;
16389 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
16390 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
16391 }
16392
16393 parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous));
16394 value = UP(pm_local_variable_target_node_create(
16395 parser,
16396 &PM_LOCATION_TOKEN_VALUE(&parser->previous),
16397 constant_id,
16398 (uint32_t) (depth == -1 ? 0 : depth)
16399 ));
16400 }
16401
16402 return UP(pm_assoc_splat_node_create(parser, value, &operator));
16403}
16404
16409static bool
16410pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
16411 ptrdiff_t length = end - start;
16412 if (length == 0) return false;
16413
16414 // First ensure that it starts with a valid identifier starting character.
16415 size_t width = char_is_identifier_start(parser, start, end - start);
16416 if (width == 0) return false;
16417
16418 // Next, ensure that it's not an uppercase character.
16419 if (parser->encoding_changed) {
16420 if (parser->encoding->isupper_char(start, length)) return false;
16421 } else {
16422 if (pm_encoding_utf_8_isupper_char(start, length)) return false;
16423 }
16424
16425 // Next, iterate through all of the bytes of the string to ensure that they
16426 // are all valid identifier characters.
16427 const uint8_t *cursor = start + width;
16428 while ((width = char_is_identifier(parser, cursor, end - cursor))) cursor += width;
16429 return cursor == end;
16430}
16431
16436static pm_node_t *
16437parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_symbol_node_t *key) {
16438 const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc;
16439
16440 pm_constant_id_t constant_id = pm_parser_constant_id_location(parser, value_loc->start, value_loc->end);
16441 int depth = -1;
16442
16443 if (pm_slice_is_valid_local(parser, value_loc->start, value_loc->end)) {
16444 depth = pm_parser_local_depth_constant_id(parser, constant_id);
16445 } else {
16446 pm_parser_err(parser, key->base.location.start, key->base.location.end, PM_ERR_PATTERN_HASH_KEY_LOCALS);
16447
16448 if ((value_loc->end > value_loc->start) && ((value_loc->end[-1] == '!') || (value_loc->end[-1] == '?'))) {
16449 PM_PARSER_ERR_LOCATION_FORMAT(parser, value_loc, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (value_loc->end - value_loc->start), (const char *) value_loc->start);
16450 }
16451 }
16452
16453 if (depth == -1) {
16454 pm_parser_local_add(parser, constant_id, value_loc->start, value_loc->end, 0);
16455 }
16456
16457 parse_pattern_capture(parser, captures, constant_id, value_loc);
16458 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
16459 parser,
16460 value_loc,
16461 constant_id,
16462 (uint32_t) (depth == -1 ? 0 : depth)
16463 );
16464
16465 return UP(pm_implicit_node_create(parser, UP(target)));
16466}
16467
16472static void
16473parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) {
16474 if (pm_static_literals_add(&parser->newline_list, parser->start_line, keys, node, true) != NULL) {
16475 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE);
16476 }
16477}
16478
16482static pm_hash_pattern_node_t *
16483parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, uint16_t depth) {
16484 pm_node_list_t assocs = { 0 };
16485 pm_static_literals_t keys = { 0 };
16486 pm_node_t *rest = NULL;
16487
16488 switch (PM_NODE_TYPE(first_node)) {
16489 case PM_ASSOC_SPLAT_NODE:
16490 case PM_NO_KEYWORDS_PARAMETER_NODE:
16491 rest = first_node;
16492 break;
16493 case PM_SYMBOL_NODE: {
16494 if (pm_symbol_node_label_p(first_node)) {
16495 parse_pattern_hash_key(parser, &keys, first_node);
16496 pm_node_t *value;
16497
16498 if (match8(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) {
16499 // Otherwise, we will create an implicit local variable
16500 // target for the value.
16501 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
16502 } else {
16503 // Here we have a value for the first assoc in the list, so
16504 // we will parse it now.
16505 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
16506 }
16507
16508 pm_token_t operator = not_provided(parser);
16509 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, &operator, value));
16510
16511 pm_node_list_append(&assocs, assoc);
16512 break;
16513 }
16514 }
16516 default: {
16517 // If we get anything else, then this is an error. For this we'll
16518 // create a missing node for the value and create an assoc node for
16519 // the first node in the list.
16520 pm_diagnostic_id_t diag_id = PM_NODE_TYPE_P(first_node, PM_INTERPOLATED_SYMBOL_NODE) ? PM_ERR_PATTERN_HASH_KEY_INTERPOLATED : PM_ERR_PATTERN_HASH_KEY_LABEL;
16521 pm_parser_err_node(parser, first_node, diag_id);
16522
16523 pm_token_t operator = not_provided(parser);
16524 pm_node_t *value = UP(pm_missing_node_create(parser, first_node->location.start, first_node->location.end));
16525 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, &operator, value));
16526
16527 pm_node_list_append(&assocs, assoc);
16528 break;
16529 }
16530 }
16531
16532 // If there are any other assocs, then we'll parse them now.
16533 while (accept1(parser, PM_TOKEN_COMMA)) {
16534 // Here we need to break to support trailing commas.
16535 if (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF)) {
16536 // Trailing commas are not allowed to follow a rest pattern.
16537 if (rest != NULL) {
16538 pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
16539 }
16540
16541 break;
16542 }
16543
16544 if (match1(parser, PM_TOKEN_USTAR_STAR)) {
16545 pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures);
16546
16547 if (rest == NULL) {
16548 rest = assoc;
16549 } else {
16550 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
16551 pm_node_list_append(&assocs, assoc);
16552 }
16553 } else {
16554 pm_node_t *key;
16555
16556 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
16557 key = parse_strings(parser, NULL, true, (uint16_t) (depth + 1));
16558
16559 if (PM_NODE_TYPE_P(key, PM_INTERPOLATED_SYMBOL_NODE)) {
16560 pm_parser_err_node(parser, key, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
16561 } else if (!pm_symbol_node_label_p(key)) {
16562 pm_parser_err_node(parser, key, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
16563 }
16564 } else {
16565 expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
16566 key = UP(pm_symbol_node_label_create(parser, &parser->previous));
16567 }
16568
16569 parse_pattern_hash_key(parser, &keys, key);
16570 pm_node_t *value = NULL;
16571
16572 if (match7(parser, PM_TOKEN_COMMA, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
16573 if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) {
16574 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key);
16575 } else {
16576 value = UP(pm_missing_node_create(parser, key->location.end, key->location.end));
16577 }
16578 } else {
16579 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
16580 }
16581
16582 pm_token_t operator = not_provided(parser);
16583 pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, &operator, value));
16584
16585 if (rest != NULL) {
16586 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
16587 }
16588
16589 pm_node_list_append(&assocs, assoc);
16590 }
16591 }
16592
16593 pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest);
16594 xfree(assocs.nodes);
16595
16596 pm_static_literals_free(&keys);
16597 return node;
16598}
16599
16603static pm_node_t *
16604parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_diagnostic_id_t diag_id, uint16_t depth) {
16605 switch (parser->current.type) {
16606 case PM_TOKEN_IDENTIFIER:
16607 case PM_TOKEN_METHOD_NAME: {
16608 parser_lex(parser);
16609 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
16610
16611 int depth;
16612 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
16613 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
16614 }
16615
16616 parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous));
16617 return UP(pm_local_variable_target_node_create(
16618 parser,
16619 &PM_LOCATION_TOKEN_VALUE(&parser->previous),
16620 constant_id,
16621 (uint32_t) (depth == -1 ? 0 : depth)
16622 ));
16623 }
16624 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
16625 pm_token_t opening = parser->current;
16626 parser_lex(parser);
16627
16628 if (accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
16629 // If we have an empty array pattern, then we'll just return a new
16630 // array pattern node.
16631 return UP(pm_array_pattern_node_empty_create(parser, &opening, &parser->previous));
16632 }
16633
16634 // Otherwise, we'll parse the inner pattern, then deal with it depending
16635 // on the type it returns.
16636 pm_node_t *inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
16637
16638 accept1(parser, PM_TOKEN_NEWLINE);
16639 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
16640 pm_token_t closing = parser->previous;
16641
16642 switch (PM_NODE_TYPE(inner)) {
16643 case PM_ARRAY_PATTERN_NODE: {
16644 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
16645 if (pattern_node->opening_loc.start == NULL) {
16646 pattern_node->base.location.start = opening.start;
16647 pattern_node->base.location.end = closing.end;
16648
16649 pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
16650 pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
16651
16652 return UP(pattern_node);
16653 }
16654
16655 break;
16656 }
16657 case PM_FIND_PATTERN_NODE: {
16658 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
16659 if (pattern_node->opening_loc.start == NULL) {
16660 pattern_node->base.location.start = opening.start;
16661 pattern_node->base.location.end = closing.end;
16662
16663 pattern_node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
16664 pattern_node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
16665
16666 return UP(pattern_node);
16667 }
16668
16669 break;
16670 }
16671 default:
16672 break;
16673 }
16674
16675 pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing);
16676 pm_array_pattern_node_requireds_append(node, inner);
16677 return UP(node);
16678 }
16679 case PM_TOKEN_BRACE_LEFT: {
16680 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
16681 parser->pattern_matching_newlines = false;
16682
16683 pm_hash_pattern_node_t *node;
16684 pm_token_t opening = parser->current;
16685 parser_lex(parser);
16686
16687 if (accept1(parser, PM_TOKEN_BRACE_RIGHT)) {
16688 // If we have an empty hash pattern, then we'll just return a new hash
16689 // pattern node.
16690 node = pm_hash_pattern_node_empty_create(parser, &opening, &parser->previous);
16691 } else {
16692 pm_node_t *first_node;
16693
16694 switch (parser->current.type) {
16695 case PM_TOKEN_LABEL:
16696 parser_lex(parser);
16697 first_node = UP(pm_symbol_node_label_create(parser, &parser->previous));
16698 break;
16699 case PM_TOKEN_USTAR_STAR:
16700 first_node = parse_pattern_keyword_rest(parser, captures);
16701 break;
16702 case PM_TOKEN_STRING_BEGIN:
16703 first_node = parse_expression(parser, PM_BINDING_POWER_MAX, false, true, PM_ERR_PATTERN_HASH_KEY_LABEL, (uint16_t) (depth + 1));
16704 break;
16705 default: {
16706 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_type_human(parser->current.type));
16707 parser_lex(parser);
16708
16709 first_node = UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end));
16710 break;
16711 }
16712 }
16713
16714 node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1));
16715
16716 accept1(parser, PM_TOKEN_NEWLINE);
16717 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE, &opening);
16718 pm_token_t closing = parser->previous;
16719
16720 node->base.location.start = opening.start;
16721 node->base.location.end = closing.end;
16722
16723 node->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
16724 node->closing_loc = PM_LOCATION_TOKEN_VALUE(&closing);
16725 }
16726
16727 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
16728 return UP(node);
16729 }
16730 case PM_TOKEN_UDOT_DOT:
16731 case PM_TOKEN_UDOT_DOT_DOT: {
16732 pm_token_t operator = parser->current;
16733 parser_lex(parser);
16734
16735 // Since we have a unary range operator, we need to parse the subsequent
16736 // expression as the right side of the range.
16737 switch (parser->current.type) {
16738 case PM_CASE_PRIMITIVE: {
16739 pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, false, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1));
16740 return UP(pm_range_node_create(parser, NULL, &operator, right));
16741 }
16742 default: {
16743 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE);
16744 pm_node_t *right = UP(pm_missing_node_create(parser, operator.start, operator.end));
16745 return UP(pm_range_node_create(parser, NULL, &operator, right));
16746 }
16747 }
16748 }
16749 case PM_CASE_PRIMITIVE: {
16750 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_MAX, false, true, diag_id, (uint16_t) (depth + 1));
16751
16752 // If we found a label, we need to immediately return to the caller.
16753 if (pm_symbol_node_label_p(node)) return node;
16754
16755 // Call nodes (arithmetic operations) are not allowed in patterns
16756 if (PM_NODE_TYPE(node) == PM_CALL_NODE) {
16757 pm_parser_err_node(parser, node, diag_id);
16758 pm_missing_node_t *missing_node = pm_missing_node_create(parser, node->location.start, node->location.end);
16759
16760 pm_node_unreference(parser, node);
16761 pm_node_destroy(parser, node);
16762 return UP(missing_node);
16763 }
16764
16765 // Now that we have a primitive, we need to check if it's part of a range.
16766 if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
16767 pm_token_t operator = parser->previous;
16768
16769 // Now that we have the operator, we need to check if this is followed
16770 // by another expression. If it is, then we will create a full range
16771 // node. Otherwise, we'll create an endless range.
16772 switch (parser->current.type) {
16773 case PM_CASE_PRIMITIVE: {
16774 pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, false, false, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1));
16775 return UP(pm_range_node_create(parser, node, &operator, right));
16776 }
16777 default:
16778 return UP(pm_range_node_create(parser, node, &operator, NULL));
16779 }
16780 }
16781
16782 return node;
16783 }
16784 case PM_TOKEN_CARET: {
16785 parser_lex(parser);
16786 pm_token_t operator = parser->previous;
16787
16788 // At this point we have a pin operator. We need to check the subsequent
16789 // expression to determine if it's a variable or an expression.
16790 switch (parser->current.type) {
16791 case PM_TOKEN_IDENTIFIER: {
16792 parser_lex(parser);
16793 pm_node_t *variable = UP(parse_variable(parser));
16794
16795 if (variable == NULL) {
16796 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->previous, PM_ERR_NO_LOCAL_VARIABLE);
16797 variable = UP(pm_local_variable_read_node_missing_create(parser, &parser->previous, 0));
16798 }
16799
16800 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
16801 }
16802 case PM_TOKEN_INSTANCE_VARIABLE: {
16803 parser_lex(parser);
16804 pm_node_t *variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
16805
16806 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
16807 }
16808 case PM_TOKEN_CLASS_VARIABLE: {
16809 parser_lex(parser);
16810 pm_node_t *variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
16811
16812 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
16813 }
16814 case PM_TOKEN_GLOBAL_VARIABLE: {
16815 parser_lex(parser);
16816 pm_node_t *variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
16817
16818 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
16819 }
16820 case PM_TOKEN_NUMBERED_REFERENCE: {
16821 parser_lex(parser);
16822 pm_node_t *variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16823
16824 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
16825 }
16826 case PM_TOKEN_BACK_REFERENCE: {
16827 parser_lex(parser);
16828 pm_node_t *variable = UP(pm_back_reference_read_node_create(parser, &parser->previous));
16829
16830 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
16831 }
16832 case PM_TOKEN_PARENTHESIS_LEFT: {
16833 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
16834 parser->pattern_matching_newlines = false;
16835
16836 pm_token_t lparen = parser->current;
16837 parser_lex(parser);
16838
16839 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_STATEMENT, true, false, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN, (uint16_t) (depth + 1));
16840 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
16841
16842 accept1(parser, PM_TOKEN_NEWLINE);
16843 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &lparen);
16844 return UP(pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous));
16845 }
16846 default: {
16847 // If we get here, then we have a pin operator followed by something
16848 // not understood. We'll create a missing node and return that.
16849 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN);
16850 pm_node_t *variable = UP(pm_missing_node_create(parser, operator.start, operator.end));
16851 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
16852 }
16853 }
16854 }
16855 case PM_TOKEN_UCOLON_COLON: {
16856 pm_token_t delimiter = parser->current;
16857 parser_lex(parser);
16858
16859 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
16860 pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous);
16861
16862 return parse_pattern_constant_path(parser, captures, UP(node), (uint16_t) (depth + 1));
16863 }
16864 case PM_TOKEN_CONSTANT: {
16865 pm_token_t constant = parser->current;
16866 parser_lex(parser);
16867
16868 pm_node_t *node = UP(pm_constant_read_node_create(parser, &constant));
16869 return parse_pattern_constant_path(parser, captures, node, (uint16_t) (depth + 1));
16870 }
16871 default:
16872 pm_parser_err_current(parser, diag_id);
16873 return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end));
16874 }
16875}
16876
16877static bool
16878parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
16879 switch (PM_NODE_TYPE(node)) {
16880 case PM_LOCAL_VARIABLE_TARGET_NODE:
16881 pm_parser_err((pm_parser_t *) data, node->location.start, node->location.end, PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
16882 return false;
16883 default:
16884 return true;
16885 }
16886}
16887
16892static void
16893parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) {
16894 pm_visit_node(node, parse_pattern_alternation_error_each, parser);
16895}
16896
16901static pm_node_t *
16902parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, pm_diagnostic_id_t diag_id, uint16_t depth) {
16903 pm_node_t *node = first_node;
16904 bool alternation = false;
16905
16906 while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) {
16907 if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) {
16908 parse_pattern_alternation_error(parser, node);
16909 }
16910
16911 switch (parser->current.type) {
16912 case PM_TOKEN_IDENTIFIER:
16913 case PM_TOKEN_BRACKET_LEFT_ARRAY:
16914 case PM_TOKEN_BRACE_LEFT:
16915 case PM_TOKEN_CARET:
16916 case PM_TOKEN_CONSTANT:
16917 case PM_TOKEN_UCOLON_COLON:
16918 case PM_TOKEN_UDOT_DOT:
16919 case PM_TOKEN_UDOT_DOT_DOT:
16920 case PM_CASE_PRIMITIVE: {
16921 if (!alternation) {
16922 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
16923 } else {
16924 pm_token_t operator = parser->previous;
16925 pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1));
16926
16927 if (captures->size) parse_pattern_alternation_error(parser, right);
16928 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
16929 }
16930
16931 break;
16932 }
16933 case PM_TOKEN_PARENTHESIS_LEFT:
16934 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
16935 pm_token_t operator = parser->previous;
16936 pm_token_t opening = parser->current;
16937 parser_lex(parser);
16938
16939 pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
16940 accept1(parser, PM_TOKEN_NEWLINE);
16941 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
16942 pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0));
16943
16944 if (!alternation) {
16945 node = right;
16946 } else {
16947 if (captures->size) parse_pattern_alternation_error(parser, right);
16948 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
16949 }
16950
16951 break;
16952 }
16953 default: {
16954 pm_parser_err_current(parser, diag_id);
16955 pm_node_t *right = UP(pm_missing_node_create(parser, parser->current.start, parser->current.end));
16956
16957 if (!alternation) {
16958 node = right;
16959 } else {
16960 if (captures->size) parse_pattern_alternation_error(parser, right);
16961 node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous));
16962 }
16963
16964 break;
16965 }
16966 }
16967 }
16968
16969 // If we have an =>, then we are assigning this pattern to a variable.
16970 // In this case we should create an assignment node.
16971 while (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
16972 pm_token_t operator = parser->previous;
16973 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET);
16974
16975 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
16976 int depth;
16977
16978 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
16979 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
16980 }
16981
16982 parse_pattern_capture(parser, captures, constant_id, &PM_LOCATION_TOKEN_VALUE(&parser->previous));
16983 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
16984 parser,
16985 &PM_LOCATION_TOKEN_VALUE(&parser->previous),
16986 constant_id,
16987 (uint32_t) (depth == -1 ? 0 : depth)
16988 );
16989
16990 node = UP(pm_capture_pattern_node_create(parser, node, target, &operator));
16991 }
16992
16993 return node;
16994}
16995
16999static pm_node_t *
17000parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
17001 pm_node_t *node = NULL;
17002
17003 bool leading_rest = false;
17004 bool trailing_rest = false;
17005
17006 switch (parser->current.type) {
17007 case PM_TOKEN_LABEL: {
17008 parser_lex(parser);
17009 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17010 node = UP(parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)));
17011
17012 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17013 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17014 }
17015
17016 return node;
17017 }
17018 case PM_TOKEN_USTAR_STAR: {
17019 node = parse_pattern_keyword_rest(parser, captures);
17020 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17021
17022 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17023 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17024 }
17025
17026 return node;
17027 }
17028 case PM_TOKEN_STRING_BEGIN: {
17029 // We need special handling for string beginnings because they could
17030 // be dynamic symbols leading to hash patterns.
17031 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17032
17033 if (pm_symbol_node_label_p(node)) {
17034 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17035
17036 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17037 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17038 }
17039
17040 return node;
17041 }
17042
17043 node = parse_pattern_primitives(parser, captures, node, diag_id, (uint16_t) (depth + 1));
17044 break;
17045 }
17046 case PM_TOKEN_USTAR: {
17047 if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) {
17048 parser_lex(parser);
17049 node = UP(parse_pattern_rest(parser, captures));
17050 leading_rest = true;
17051 break;
17052 }
17053 }
17055 default:
17056 node = parse_pattern_primitives(parser, captures, NULL, diag_id, (uint16_t) (depth + 1));
17057 break;
17058 }
17059
17060 // If we got a dynamic label symbol, then we need to treat it like the
17061 // beginning of a hash pattern.
17062 if (pm_symbol_node_label_p(node)) {
17063 return UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17064 }
17065
17066 if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) {
17067 // If we have a comma, then we are now parsing either an array pattern
17068 // or a find pattern. We need to parse all of the patterns, put them
17069 // into a big list, and then determine which type of node we have.
17070 pm_node_list_t nodes = { 0 };
17071 pm_node_list_append(&nodes, node);
17072
17073 // Gather up all of the patterns into the list.
17074 while (accept1(parser, PM_TOKEN_COMMA)) {
17075 // Break early here in case we have a trailing comma.
17076 if (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_AND, PM_TOKEN_KEYWORD_OR)) {
17077 node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
17078 pm_node_list_append(&nodes, node);
17079 trailing_rest = true;
17080 break;
17081 }
17082
17083 if (accept1(parser, PM_TOKEN_USTAR)) {
17084 node = UP(parse_pattern_rest(parser, captures));
17085
17086 // If we have already parsed a splat pattern, then this is an
17087 // error. We will continue to parse the rest of the patterns,
17088 // but we will indicate it as an error.
17089 if (trailing_rest) {
17090 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
17091 }
17092
17093 trailing_rest = true;
17094 } else {
17095 node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
17096 }
17097
17098 pm_node_list_append(&nodes, node);
17099 }
17100
17101 // If the first pattern and the last pattern are rest patterns, then we
17102 // will call this a find pattern, regardless of how many rest patterns
17103 // are in between because we know we already added the appropriate
17104 // errors. Otherwise we will create an array pattern.
17105 if (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) {
17106 node = UP(pm_find_pattern_node_create(parser, &nodes));
17107
17108 if (nodes.size == 2) {
17109 pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER);
17110 }
17111 } else {
17112 node = UP(pm_array_pattern_node_node_list_create(parser, &nodes));
17113
17114 if (leading_rest && trailing_rest) {
17115 pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS);
17116 }
17117 }
17118
17119 xfree(nodes.nodes);
17120 } else if (leading_rest) {
17121 // Otherwise, if we parsed a single splat pattern, then we know we have
17122 // an array pattern, so we can go ahead and create that node.
17123 node = UP(pm_array_pattern_node_rest_create(parser, node));
17124 }
17125
17126 return node;
17127}
17128
17134static inline void
17135parse_negative_numeric(pm_node_t *node) {
17136 switch (PM_NODE_TYPE(node)) {
17137 case PM_INTEGER_NODE: {
17138 pm_integer_node_t *cast = (pm_integer_node_t *) node;
17139 cast->base.location.start--;
17140 cast->value.negative = true;
17141 break;
17142 }
17143 case PM_FLOAT_NODE: {
17144 pm_float_node_t *cast = (pm_float_node_t *) node;
17145 cast->base.location.start--;
17146 cast->value = -cast->value;
17147 break;
17148 }
17149 case PM_RATIONAL_NODE: {
17150 pm_rational_node_t *cast = (pm_rational_node_t *) node;
17151 cast->base.location.start--;
17152 cast->numerator.negative = true;
17153 break;
17154 }
17155 case PM_IMAGINARY_NODE:
17156 node->location.start--;
17157 parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric);
17158 break;
17159 default:
17160 assert(false && "unreachable");
17161 break;
17162 }
17163}
17164
17170static void
17171pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
17172 switch (diag_id) {
17173 case PM_ERR_HASH_KEY: {
17174 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, diag_id, pm_token_type_human(parser->previous.type));
17175 break;
17176 }
17177 case PM_ERR_HASH_VALUE:
17178 case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: {
17179 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, pm_token_type_human(parser->current.type));
17180 break;
17181 }
17182 case PM_ERR_UNARY_RECEIVER: {
17183 const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_type_human(parser->current.type));
17184 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, diag_id, human, parser->previous.start[0]);
17185 break;
17186 }
17187 case PM_ERR_UNARY_DISALLOWED:
17188 case PM_ERR_EXPECT_ARGUMENT: {
17189 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, diag_id, pm_token_type_human(parser->current.type));
17190 break;
17191 }
17192 default:
17193 pm_parser_err_previous(parser, diag_id);
17194 break;
17195 }
17196}
17197
17201static void
17202parse_retry(pm_parser_t *parser, const pm_node_t *node) {
17203#define CONTEXT_NONE 0
17204#define CONTEXT_THROUGH_ENSURE 1
17205#define CONTEXT_THROUGH_ELSE 2
17206
17207 pm_context_node_t *context_node = parser->current_context;
17208 int context = CONTEXT_NONE;
17209
17210 while (context_node != NULL) {
17211 switch (context_node->context) {
17219 case PM_CONTEXT_DEFINED:
17221 // These are the good cases. We're allowed to have a retry here.
17222 return;
17223 case PM_CONTEXT_CLASS:
17224 case PM_CONTEXT_DEF:
17226 case PM_CONTEXT_MAIN:
17227 case PM_CONTEXT_MODULE:
17228 case PM_CONTEXT_PREEXE:
17229 case PM_CONTEXT_SCLASS:
17230 // These are the bad cases. We're not allowed to have a retry in
17231 // these contexts.
17232 if (context == CONTEXT_NONE) {
17233 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE);
17234 } else if (context == CONTEXT_THROUGH_ENSURE) {
17235 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE);
17236 } else if (context == CONTEXT_THROUGH_ELSE) {
17237 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE);
17238 }
17239 return;
17247 // These are also bad cases, but with a more specific error
17248 // message indicating the else.
17249 context = CONTEXT_THROUGH_ELSE;
17250 break;
17258 // These are also bad cases, but with a more specific error
17259 // message indicating the ensure.
17260 context = CONTEXT_THROUGH_ENSURE;
17261 break;
17262 case PM_CONTEXT_NONE:
17263 // This case should never happen.
17264 assert(false && "unreachable");
17265 break;
17266 case PM_CONTEXT_BEGIN:
17270 case PM_CONTEXT_CASE_IN:
17273 case PM_CONTEXT_ELSE:
17274 case PM_CONTEXT_ELSIF:
17275 case PM_CONTEXT_EMBEXPR:
17277 case PM_CONTEXT_FOR:
17278 case PM_CONTEXT_IF:
17283 case PM_CONTEXT_PARENS:
17284 case PM_CONTEXT_POSTEXE:
17286 case PM_CONTEXT_TERNARY:
17287 case PM_CONTEXT_UNLESS:
17288 case PM_CONTEXT_UNTIL:
17289 case PM_CONTEXT_WHILE:
17290 // In these contexts we should continue walking up the list of
17291 // contexts.
17292 break;
17293 }
17294
17295 context_node = context_node->prev;
17296 }
17297
17298#undef CONTEXT_NONE
17299#undef CONTEXT_ENSURE
17300#undef CONTEXT_ELSE
17301}
17302
17306static void
17307parse_yield(pm_parser_t *parser, const pm_node_t *node) {
17308 pm_context_node_t *context_node = parser->current_context;
17309
17310 while (context_node != NULL) {
17311 switch (context_node->context) {
17312 case PM_CONTEXT_DEF:
17314 case PM_CONTEXT_DEFINED:
17318 // These are the good cases. We're allowed to have a block exit
17319 // in these contexts.
17320 return;
17321 case PM_CONTEXT_CLASS:
17325 case PM_CONTEXT_MAIN:
17326 case PM_CONTEXT_MODULE:
17330 case PM_CONTEXT_SCLASS:
17334 // These are the bad cases. We're not allowed to have a retry in
17335 // these contexts.
17336 pm_parser_err_node(parser, node, PM_ERR_INVALID_YIELD);
17337 return;
17338 case PM_CONTEXT_NONE:
17339 // This case should never happen.
17340 assert(false && "unreachable");
17341 break;
17342 case PM_CONTEXT_BEGIN:
17352 case PM_CONTEXT_CASE_IN:
17355 case PM_CONTEXT_ELSE:
17356 case PM_CONTEXT_ELSIF:
17357 case PM_CONTEXT_EMBEXPR:
17359 case PM_CONTEXT_FOR:
17360 case PM_CONTEXT_IF:
17368 case PM_CONTEXT_PARENS:
17369 case PM_CONTEXT_POSTEXE:
17371 case PM_CONTEXT_PREEXE:
17373 case PM_CONTEXT_TERNARY:
17374 case PM_CONTEXT_UNLESS:
17375 case PM_CONTEXT_UNTIL:
17376 case PM_CONTEXT_WHILE:
17377 // In these contexts we should continue walking up the list of
17378 // contexts.
17379 break;
17380 }
17381
17382 context_node = context_node->prev;
17383 }
17384}
17385
17390typedef struct {
17393
17395 const uint8_t *start;
17396
17398 const uint8_t *end;
17399
17408
17413static void
17414parse_regular_expression_error(const uint8_t *start, const uint8_t *end, const char *message, void *data) {
17416 pm_location_t location;
17417
17418 if (callback_data->shared) {
17419 location = (pm_location_t) { .start = start, .end = end };
17420 } else {
17421 location = (pm_location_t) { .start = callback_data->start, .end = callback_data->end };
17422 }
17423
17424 PM_PARSER_ERR_FORMAT(callback_data->parser, location.start, location.end, PM_ERR_REGEXP_PARSE_ERROR, message);
17425}
17426
17430static void
17431parse_regular_expression_errors(pm_parser_t *parser, pm_regular_expression_node_t *node) {
17432 const pm_string_t *unescaped = &node->unescaped;
17434 .parser = parser,
17435 .start = node->base.location.start,
17436 .end = node->base.location.end,
17437 .shared = unescaped->type == PM_STRING_SHARED
17438 };
17439
17440 pm_regexp_parse(parser, pm_string_source(unescaped), pm_string_length(unescaped), PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED), NULL, NULL, parse_regular_expression_error, &error_data);
17441}
17442
17446static inline pm_node_t *
17447parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) {
17448 switch (parser->current.type) {
17449 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
17450 parser_lex(parser);
17451
17452 pm_array_node_t *array = pm_array_node_create(parser, &parser->previous);
17453 pm_accepts_block_stack_push(parser, true);
17454 bool parsed_bare_hash = false;
17455
17456 while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
17457 bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
17458
17459 // Handle the case where we don't have a comma and we have a
17460 // newline followed by a right bracket.
17461 if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17462 break;
17463 }
17464
17465 // Ensure that we have a comma between elements in the array.
17466 if (array->elements.size > 0) {
17467 if (accept1(parser, PM_TOKEN_COMMA)) {
17468 // If there was a comma but we also accepts a newline,
17469 // then this is a syntax error.
17470 if (accepted_newline) {
17471 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
17472 }
17473 } else {
17474 // If there was no comma, then we need to add a syntax
17475 // error.
17476 const uint8_t *location = parser->previous.end;
17477 PM_PARSER_ERR_FORMAT(parser, location, location, PM_ERR_ARRAY_SEPARATOR, pm_token_type_human(parser->current.type));
17478
17479 parser->previous.start = location;
17480 parser->previous.type = PM_TOKEN_MISSING;
17481 }
17482 }
17483
17484 // If we have a right bracket immediately following a comma,
17485 // this is allowed since it's a trailing comma. In this case we
17486 // can break out of the loop.
17487 if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break;
17488
17489 pm_node_t *element;
17490
17491 if (accept1(parser, PM_TOKEN_USTAR)) {
17492 pm_token_t operator = parser->previous;
17493 pm_node_t *expression = NULL;
17494
17495 if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) {
17496 pm_parser_scope_forwarding_positionals_check(parser, &operator);
17497 } else {
17498 expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
17499 }
17500
17501 element = UP(pm_splat_node_create(parser, &operator, expression));
17502 } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) {
17503 if (parsed_bare_hash) {
17504 pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH);
17505 }
17506
17507 element = UP(pm_keyword_hash_node_create(parser));
17508 pm_static_literals_t hash_keys = { 0 };
17509
17510 if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_EOF, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) {
17511 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
17512 }
17513
17514 pm_static_literals_free(&hash_keys);
17515 parsed_bare_hash = true;
17516 } else {
17517 element = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, true, PM_ERR_ARRAY_EXPRESSION, (uint16_t) (depth + 1));
17518
17519 if (pm_symbol_node_label_p(element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
17520 if (parsed_bare_hash) {
17521 pm_parser_err_previous(parser, PM_ERR_EXPRESSION_BARE_HASH);
17522 }
17523
17524 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
17525 pm_static_literals_t hash_keys = { 0 };
17526 pm_hash_key_static_literals_add(parser, &hash_keys, element);
17527
17528 pm_token_t operator;
17529 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
17530 operator = parser->previous;
17531 } else {
17532 operator = not_provided(parser);
17533 }
17534
17535 pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1));
17536 pm_node_t *assoc = UP(pm_assoc_node_create(parser, element, &operator, value));
17537 pm_keyword_hash_node_elements_append(hash, assoc);
17538
17539 element = UP(hash);
17540 if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17541 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
17542 }
17543
17544 pm_static_literals_free(&hash_keys);
17545 parsed_bare_hash = true;
17546 }
17547 }
17548
17549 pm_array_node_elements_append(array, element);
17550 if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break;
17551 }
17552
17553 accept1(parser, PM_TOKEN_NEWLINE);
17554
17555 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17556 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_ARRAY_TERM, pm_token_type_human(parser->current.type));
17557 parser->previous.start = parser->previous.end;
17558 parser->previous.type = PM_TOKEN_MISSING;
17559 }
17560
17561 pm_array_node_close_set(array, &parser->previous);
17562 pm_accepts_block_stack_pop(parser);
17563
17564 return UP(array);
17565 }
17566 case PM_TOKEN_PARENTHESIS_LEFT:
17567 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
17568 pm_token_t opening = parser->current;
17569 pm_node_flags_t flags = 0;
17570
17571 pm_node_list_t current_block_exits = { 0 };
17572 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
17573
17574 parser_lex(parser);
17575 while (true) {
17576 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
17577 flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
17578 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
17579 break;
17580 }
17581 }
17582
17583 // If this is the end of the file or we match a right parenthesis, then
17584 // we have an empty parentheses node, and we can immediately return.
17585 if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
17586 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
17587
17588 pop_block_exits(parser, previous_block_exits);
17589 pm_node_list_free(&current_block_exits);
17590
17591 return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, flags));
17592 }
17593
17594 // Otherwise, we're going to parse the first statement in the list
17595 // of statements within the parentheses.
17596 pm_accepts_block_stack_push(parser, true);
17597 context_push(parser, PM_CONTEXT_PARENS);
17598 pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, false, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
17599 context_pop(parser);
17600
17601 // Determine if this statement is followed by a terminator. In the
17602 // case of a single statement, this is fine. But in the case of
17603 // multiple statements it's required.
17604 bool terminator_found = false;
17605
17606 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
17607 terminator_found = true;
17608 flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
17609 } else if (accept1(parser, PM_TOKEN_NEWLINE)) {
17610 terminator_found = true;
17611 }
17612
17613 if (terminator_found) {
17614 while (true) {
17615 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
17616 flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
17617 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
17618 break;
17619 }
17620 }
17621 }
17622
17623 // If we hit a right parenthesis, then we're done parsing the
17624 // parentheses node, and we can check which kind of node we should
17625 // return.
17626 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
17627 if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
17628 lex_state_set(parser, PM_LEX_STATE_ENDARG);
17629 }
17630
17631 parser_lex(parser);
17632 pm_accepts_block_stack_pop(parser);
17633
17634 pop_block_exits(parser, previous_block_exits);
17635 pm_node_list_free(&current_block_exits);
17636
17637 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
17638 // If we have a single statement and are ending on a right
17639 // parenthesis, then we need to check if this is possibly a
17640 // multiple target node.
17641 pm_multi_target_node_t *multi_target;
17642
17643 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) && ((pm_multi_target_node_t *) statement)->lparen_loc.start == NULL) {
17644 multi_target = (pm_multi_target_node_t *) statement;
17645 } else {
17646 multi_target = pm_multi_target_node_create(parser);
17647 pm_multi_target_node_targets_append(parser, multi_target, statement);
17648 }
17649
17650 pm_location_t lparen_loc = PM_LOCATION_TOKEN_VALUE(&opening);
17651 pm_location_t rparen_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
17652
17653 multi_target->lparen_loc = lparen_loc;
17654 multi_target->rparen_loc = rparen_loc;
17655 multi_target->base.location.start = lparen_loc.start;
17656 multi_target->base.location.end = rparen_loc.end;
17657
17658 pm_node_t *result;
17659 if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) {
17660 result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
17661 accept1(parser, PM_TOKEN_NEWLINE);
17662 } else {
17663 result = UP(multi_target);
17664 }
17665
17666 if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
17667 // All set, this is explicitly allowed by the parent
17668 // context.
17669 } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) {
17670 // All set, we're inside a for loop and we're parsing
17671 // multiple targets.
17672 } else if (binding_power != PM_BINDING_POWER_STATEMENT) {
17673 // Multi targets are not allowed when it's not a
17674 // statement level.
17675 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
17676 } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
17677 // Multi targets must be followed by an equal sign in
17678 // order to be valid (or a right parenthesis if they are
17679 // nested).
17680 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
17681 }
17682
17683 return result;
17684 }
17685
17686 // If we have a single statement and are ending on a right parenthesis
17687 // and we didn't return a multiple assignment node, then we can return a
17688 // regular parentheses node now.
17689 pm_statements_node_t *statements = pm_statements_node_create(parser);
17690 pm_statements_node_body_append(parser, statements, statement, true);
17691
17692 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, flags));
17693 }
17694
17695 // If we have more than one statement in the set of parentheses,
17696 // then we are going to parse all of them as a list of statements.
17697 // We'll do that here.
17698 context_push(parser, PM_CONTEXT_PARENS);
17699 flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
17700
17701 pm_statements_node_t *statements = pm_statements_node_create(parser);
17702 pm_statements_node_body_append(parser, statements, statement, true);
17703
17704 // If we didn't find a terminator and we didn't find a right
17705 // parenthesis, then this is a syntax error.
17706 if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) {
17707 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type));
17708 }
17709
17710 // Parse each statement within the parentheses.
17711 while (true) {
17712 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, true, false, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
17713 pm_statements_node_body_append(parser, statements, node, true);
17714
17715 // If we're recovering from a syntax error, then we need to stop
17716 // parsing the statements now.
17717 if (parser->recovering) {
17718 // If this is the level of context where the recovery has
17719 // happened, then we can mark the parser as done recovering.
17720 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false;
17721 break;
17722 }
17723
17724 // If we couldn't parse an expression at all, then we need to
17725 // bail out of the loop.
17726 if (PM_NODE_TYPE_P(node, PM_MISSING_NODE)) break;
17727
17728 // If we successfully parsed a statement, then we are going to
17729 // need terminator to delimit them.
17730 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
17731 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
17732 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break;
17733 } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
17734 break;
17735 } else if (!match1(parser, PM_TOKEN_EOF)) {
17736 // If we're at the end of the file, then we're going to add
17737 // an error after this for the ) anyway.
17738 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(parser->current.type));
17739 }
17740 }
17741
17742 context_pop(parser);
17743 pm_accepts_block_stack_pop(parser);
17744 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
17745
17746 // When we're parsing multi targets, we allow them to be followed by
17747 // a right parenthesis if they are at the statement level. This is
17748 // only possible if they are the final statement in a parentheses.
17749 // We need to explicitly reject that here.
17750 {
17751 pm_node_t *statement = statements->body.nodes[statements->body.size - 1];
17752
17753 if (PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
17754 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
17755 pm_multi_target_node_targets_append(parser, multi_target, statement);
17756
17757 statement = UP(multi_target);
17758 statements->body.nodes[statements->body.size - 1] = statement;
17759 }
17760
17761 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) {
17762 const uint8_t *offset = statement->location.end;
17763 pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset };
17764 pm_node_t *value = UP(pm_missing_node_create(parser, offset, offset));
17765
17766 statement = UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value));
17767 statements->body.nodes[statements->body.size - 1] = statement;
17768
17769 pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED);
17770 }
17771 }
17772
17773 pop_block_exits(parser, previous_block_exits);
17774 pm_node_list_free(&current_block_exits);
17775
17776 pm_void_statements_check(parser, statements, true);
17777 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, flags));
17778 }
17779 case PM_TOKEN_BRACE_LEFT: {
17780 // If we were passed a current_hash_keys via the parser, then that
17781 // means we're already parsing a hash and we want to share the set
17782 // of hash keys with this inner hash we're about to parse for the
17783 // sake of warnings. We'll set it to NULL after we grab it to make
17784 // sure subsequent expressions don't use it. Effectively this is a
17785 // way of getting around passing it to every call to
17786 // parse_expression.
17787 pm_static_literals_t *current_hash_keys = parser->current_hash_keys;
17788 parser->current_hash_keys = NULL;
17789
17790 pm_accepts_block_stack_push(parser, true);
17791 parser_lex(parser);
17792
17793 pm_token_t opening = parser->previous;
17794 pm_hash_node_t *node = pm_hash_node_create(parser, &opening);
17795
17796 if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
17797 if (current_hash_keys != NULL) {
17798 parse_assocs(parser, current_hash_keys, UP(node), (uint16_t) (depth + 1));
17799 } else {
17800 pm_static_literals_t hash_keys = { 0 };
17801 parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1));
17802 pm_static_literals_free(&hash_keys);
17803 }
17804
17805 accept1(parser, PM_TOKEN_NEWLINE);
17806 }
17807
17808 pm_accepts_block_stack_pop(parser);
17809 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening);
17810 pm_hash_node_closing_loc_set(node, &parser->previous);
17811
17812 return UP(node);
17813 }
17814 case PM_TOKEN_CHARACTER_LITERAL: {
17815 pm_token_t closing = not_provided(parser);
17816 pm_node_t *node = UP(pm_string_node_create_current_string(
17817 parser,
17818 &(pm_token_t) {
17819 .type = PM_TOKEN_STRING_BEGIN,
17820 .start = parser->current.start,
17821 .end = parser->current.start + 1
17822 },
17823 &(pm_token_t) {
17824 .type = PM_TOKEN_STRING_CONTENT,
17825 .start = parser->current.start + 1,
17826 .end = parser->current.end
17827 },
17828 &closing
17829 ));
17830
17831 pm_node_flag_set(node, parse_unescaped_encoding(parser));
17832
17833 // Skip past the character literal here, since now we have handled
17834 // parser->explicit_encoding correctly.
17835 parser_lex(parser);
17836
17837 // Characters can be followed by strings in which case they are
17838 // automatically concatenated.
17839 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
17840 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
17841 }
17842
17843 return node;
17844 }
17845 case PM_TOKEN_CLASS_VARIABLE: {
17846 parser_lex(parser);
17847 pm_node_t *node = UP(pm_class_variable_read_node_create(parser, &parser->previous));
17848
17849 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
17850 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
17851 }
17852
17853 return node;
17854 }
17855 case PM_TOKEN_CONSTANT: {
17856 parser_lex(parser);
17857 pm_token_t constant = parser->previous;
17858
17859 // If a constant is immediately followed by parentheses, then this is in
17860 // fact a method call, not a constant read.
17861 if (
17862 match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
17863 (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) ||
17864 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
17865 match1(parser, PM_TOKEN_BRACE_LEFT)
17866 ) {
17867 pm_arguments_t arguments = { 0 };
17868 parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1));
17869 return UP(pm_call_node_fcall_create(parser, &constant, &arguments));
17870 }
17871
17872 pm_node_t *node = UP(pm_constant_read_node_create(parser, &parser->previous));
17873
17874 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
17875 // If we get here, then we have a comma immediately following a
17876 // constant, so we're going to parse this as a multiple assignment.
17877 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
17878 }
17879
17880 return node;
17881 }
17882 case PM_TOKEN_UCOLON_COLON: {
17883 parser_lex(parser);
17884 pm_token_t delimiter = parser->previous;
17885
17886 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
17887 pm_node_t *node = UP(pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous));
17888
17889 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
17890 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
17891 }
17892
17893 return node;
17894 }
17895 case PM_TOKEN_UDOT_DOT:
17896 case PM_TOKEN_UDOT_DOT_DOT: {
17897 pm_token_t operator = parser->current;
17898 parser_lex(parser);
17899
17900 pm_node_t *right = parse_expression(parser, pm_binding_powers[operator.type].left, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
17901
17902 // Unary .. and ... are special because these are non-associative
17903 // operators that can also be unary operators. In this case we need
17904 // to explicitly reject code that has a .. or ... that follows this
17905 // expression.
17906 if (match2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
17907 pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
17908 }
17909
17910 return UP(pm_range_node_create(parser, NULL, &operator, right));
17911 }
17912 case PM_TOKEN_FLOAT:
17913 parser_lex(parser);
17914 return UP(pm_float_node_create(parser, &parser->previous));
17915 case PM_TOKEN_FLOAT_IMAGINARY:
17916 parser_lex(parser);
17917 return UP(pm_float_node_imaginary_create(parser, &parser->previous));
17918 case PM_TOKEN_FLOAT_RATIONAL:
17919 parser_lex(parser);
17920 return UP(pm_float_node_rational_create(parser, &parser->previous));
17921 case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY:
17922 parser_lex(parser);
17923 return UP(pm_float_node_rational_imaginary_create(parser, &parser->previous));
17924 case PM_TOKEN_NUMBERED_REFERENCE: {
17925 parser_lex(parser);
17926 pm_node_t *node = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
17927
17928 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
17929 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
17930 }
17931
17932 return node;
17933 }
17934 case PM_TOKEN_GLOBAL_VARIABLE: {
17935 parser_lex(parser);
17936 pm_node_t *node = UP(pm_global_variable_read_node_create(parser, &parser->previous));
17937
17938 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
17939 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
17940 }
17941
17942 return node;
17943 }
17944 case PM_TOKEN_BACK_REFERENCE: {
17945 parser_lex(parser);
17946 pm_node_t *node = UP(pm_back_reference_read_node_create(parser, &parser->previous));
17947
17948 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
17949 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
17950 }
17951
17952 return node;
17953 }
17954 case PM_TOKEN_IDENTIFIER:
17955 case PM_TOKEN_METHOD_NAME: {
17956 parser_lex(parser);
17957 pm_token_t identifier = parser->previous;
17958 pm_node_t *node = parse_variable_call(parser);
17959
17960 if (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
17961 // If parse_variable_call returned with a call node, then we
17962 // know the identifier is not in the local table. In that case
17963 // we need to check if there are arguments following the
17964 // identifier.
17965 pm_call_node_t *call = (pm_call_node_t *) node;
17966 pm_arguments_t arguments = { 0 };
17967
17968 if (parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1))) {
17969 // Since we found arguments, we need to turn off the
17970 // variable call bit in the flags.
17971 pm_node_flag_unset(UP(call), PM_CALL_NODE_FLAGS_VARIABLE_CALL);
17972
17973 call->opening_loc = arguments.opening_loc;
17974 call->arguments = arguments.arguments;
17975 call->closing_loc = arguments.closing_loc;
17976 call->block = arguments.block;
17977
17978 const uint8_t *end = pm_arguments_end(&arguments);
17979 if (!end) {
17980 end = call->message_loc.end;
17981 }
17982 call->base.location.end = end;
17983 }
17984 } else {
17985 // Otherwise, we know the identifier is in the local table. This
17986 // can still be a method call if it is followed by arguments or
17987 // a block, so we need to check for that here.
17988 if (
17989 (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) ||
17990 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
17991 match1(parser, PM_TOKEN_BRACE_LEFT)
17992 ) {
17993 pm_arguments_t arguments = { 0 };
17994 parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1));
17995 pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments);
17996
17997 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
17998 // If we're about to convert an 'it' implicit local
17999 // variable read into a method call, we need to remove
18000 // it from the list of implicit local variables.
18001 pm_node_unreference(parser, node);
18002 } else {
18003 // Otherwise, we're about to convert a regular local
18004 // variable read into a method call, in which case we
18005 // need to indicate that this was not a read for the
18006 // purposes of warnings.
18007 assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE));
18008
18009 if (pm_token_is_numbered_parameter(identifier.start, identifier.end)) {
18010 pm_node_unreference(parser, node);
18011 } else {
18012 pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node;
18013 pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
18014 }
18015 }
18016
18017 pm_node_destroy(parser, node);
18018 return UP(fcall);
18019 }
18020 }
18021
18022 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
18023 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
18024 }
18025
18026 return node;
18027 }
18028 case PM_TOKEN_HEREDOC_START: {
18029 // Here we have found a heredoc. We'll parse it and add it to the
18030 // list of strings.
18031 assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC);
18032 pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base;
18033
18034 size_t common_whitespace = (size_t) -1;
18035 parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace;
18036
18037 parser_lex(parser);
18038 pm_token_t opening = parser->previous;
18039
18040 pm_node_t *node;
18041 pm_node_t *part;
18042
18043 if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
18044 // If we get here, then we have an empty heredoc. We'll create
18045 // an empty content token and return an empty string node.
18046 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
18047 pm_token_t content = parse_strings_empty_content(parser->previous.start);
18048
18049 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
18050 node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
18051 } else {
18052 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
18053 }
18054
18055 node->location.end = opening.end;
18056 } else if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) == NULL) {
18057 // If we get here, then we tried to find something in the
18058 // heredoc but couldn't actually parse anything, so we'll just
18059 // return a missing node.
18060 //
18061 // parse_string_part handles its own errors, so there is no need
18062 // for us to add one here.
18063 node = UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end));
18064 } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
18065 // If we get here, then the part that we parsed was plain string
18066 // content and we're at the end of the heredoc, so we can return
18067 // just a string node with the heredoc opening and closing as
18068 // its opening and closing.
18069 pm_node_flag_set(part, parse_unescaped_encoding(parser));
18070 pm_string_node_t *cast = (pm_string_node_t *) part;
18071
18072 cast->opening_loc = PM_LOCATION_TOKEN_VALUE(&opening);
18073 cast->closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->current);
18074 cast->base.location = cast->opening_loc;
18075
18076 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
18077 assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
18078 cast->base.type = PM_X_STRING_NODE;
18079 }
18080
18081 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
18082 parse_heredoc_dedent_string(&cast->unescaped, common_whitespace);
18083 }
18084
18085 node = UP(cast);
18086 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
18087 } else {
18088 // If we get here, then we have multiple parts in the heredoc,
18089 // so we'll need to create an interpolated string node to hold
18090 // them all.
18091 pm_node_list_t parts = { 0 };
18092 pm_node_list_append(&parts, part);
18093
18094 while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
18095 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
18096 pm_node_list_append(&parts, part);
18097 }
18098 }
18099
18100 // Now that we have all of the parts, create the correct type of
18101 // interpolated node.
18102 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
18103 pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
18104 cast->parts = parts;
18105
18106 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
18107 pm_interpolated_xstring_node_closing_set(cast, &parser->previous);
18108
18109 cast->base.location = cast->opening_loc;
18110 node = UP(cast);
18111 } else {
18112 pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
18113 pm_node_list_free(&parts);
18114
18115 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
18116 pm_interpolated_string_node_closing_set(cast, &parser->previous);
18117
18118 cast->base.location = cast->opening_loc;
18119 node = UP(cast);
18120 }
18121
18122 // If this is a heredoc that is indented with a ~, then we need
18123 // to dedent each line by the common leading whitespace.
18124 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
18125 pm_node_list_t *nodes;
18126 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
18127 nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
18128 } else {
18129 nodes = &((pm_interpolated_string_node_t *) node)->parts;
18130 }
18131
18132 parse_heredoc_dedent(parser, nodes, common_whitespace);
18133 }
18134 }
18135
18136 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
18137 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
18138 }
18139
18140 return node;
18141 }
18142 case PM_TOKEN_INSTANCE_VARIABLE: {
18143 parser_lex(parser);
18144 pm_node_t *node = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
18145
18146 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
18147 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
18148 }
18149
18150 return node;
18151 }
18152 case PM_TOKEN_INTEGER: {
18153 pm_node_flags_t base = parser->integer_base;
18154 parser_lex(parser);
18155 return UP(pm_integer_node_create(parser, base, &parser->previous));
18156 }
18157 case PM_TOKEN_INTEGER_IMAGINARY: {
18158 pm_node_flags_t base = parser->integer_base;
18159 parser_lex(parser);
18160 return UP(pm_integer_node_imaginary_create(parser, base, &parser->previous));
18161 }
18162 case PM_TOKEN_INTEGER_RATIONAL: {
18163 pm_node_flags_t base = parser->integer_base;
18164 parser_lex(parser);
18165 return UP(pm_integer_node_rational_create(parser, base, &parser->previous));
18166 }
18167 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: {
18168 pm_node_flags_t base = parser->integer_base;
18169 parser_lex(parser);
18170 return UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous));
18171 }
18172 case PM_TOKEN_KEYWORD___ENCODING__:
18173 parser_lex(parser);
18174 return UP(pm_source_encoding_node_create(parser, &parser->previous));
18175 case PM_TOKEN_KEYWORD___FILE__:
18176 parser_lex(parser);
18177 return UP(pm_source_file_node_create(parser, &parser->previous));
18178 case PM_TOKEN_KEYWORD___LINE__:
18179 parser_lex(parser);
18180 return UP(pm_source_line_node_create(parser, &parser->previous));
18181 case PM_TOKEN_KEYWORD_ALIAS: {
18182 if (binding_power != PM_BINDING_POWER_STATEMENT) {
18183 pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS);
18184 }
18185
18186 parser_lex(parser);
18187 pm_token_t keyword = parser->previous;
18188
18189 pm_node_t *new_name = parse_alias_argument(parser, true, (uint16_t) (depth + 1));
18190 pm_node_t *old_name = parse_alias_argument(parser, false, (uint16_t) (depth + 1));
18191
18192 switch (PM_NODE_TYPE(new_name)) {
18193 case PM_BACK_REFERENCE_READ_NODE:
18194 case PM_NUMBERED_REFERENCE_READ_NODE:
18195 case PM_GLOBAL_VARIABLE_READ_NODE: {
18196 if (PM_NODE_TYPE_P(old_name, PM_BACK_REFERENCE_READ_NODE) || PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE) || PM_NODE_TYPE_P(old_name, PM_GLOBAL_VARIABLE_READ_NODE)) {
18197 if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) {
18198 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE);
18199 }
18200 } else {
18201 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
18202 }
18203
18204 return UP(pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name));
18205 }
18206 case PM_SYMBOL_NODE:
18207 case PM_INTERPOLATED_SYMBOL_NODE: {
18208 if (!PM_NODE_TYPE_P(old_name, PM_SYMBOL_NODE) && !PM_NODE_TYPE_P(old_name, PM_INTERPOLATED_SYMBOL_NODE)) {
18209 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
18210 }
18211 }
18213 default:
18214 return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name));
18215 }
18216 }
18217 case PM_TOKEN_KEYWORD_CASE: {
18218 size_t opening_newline_index = token_newline_index(parser);
18219 parser_lex(parser);
18220
18221 pm_token_t case_keyword = parser->previous;
18222 pm_node_t *predicate = NULL;
18223
18224 pm_node_list_t current_block_exits = { 0 };
18225 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18226
18227 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18228 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18229 predicate = NULL;
18230 } else if (match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_END)) {
18231 predicate = NULL;
18232 } else if (!token_begins_expression_p(parser->current.type)) {
18233 predicate = NULL;
18234 } else {
18235 predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_CASE_EXPRESSION_AFTER_CASE, (uint16_t) (depth + 1));
18236 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18237 }
18238
18239 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
18240 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18241 parser_lex(parser);
18242
18243 pop_block_exits(parser, previous_block_exits);
18244 pm_node_list_free(&current_block_exits);
18245
18246 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18247 return UP(pm_case_node_create(parser, &case_keyword, predicate, &parser->previous));
18248 }
18249
18250 // At this point we can create a case node, though we don't yet know
18251 // if it is a case-in or case-when node.
18252 pm_token_t end_keyword = not_provided(parser);
18253 pm_node_t *node;
18254
18255 if (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18256 pm_case_node_t *case_node = pm_case_node_create(parser, &case_keyword, predicate, &end_keyword);
18257 pm_static_literals_t literals = { 0 };
18258
18259 // At this point we've seen a when keyword, so we know this is a
18260 // case-when node. We will continue to parse the when nodes
18261 // until we hit the end of the list.
18262 while (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18263 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18264 parser_lex(parser);
18265
18266 pm_token_t when_keyword = parser->previous;
18267 pm_when_node_t *when_node = pm_when_node_create(parser, &when_keyword);
18268
18269 do {
18270 if (accept1(parser, PM_TOKEN_USTAR)) {
18271 pm_token_t operator = parser->previous;
18272 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
18273
18274 pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression);
18275 pm_when_node_conditions_append(when_node, UP(splat_node));
18276
18277 if (PM_NODE_TYPE_P(expression, PM_MISSING_NODE)) break;
18278 } else {
18279 pm_node_t *condition = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_CASE_EXPRESSION_AFTER_WHEN, (uint16_t) (depth + 1));
18280 pm_when_node_conditions_append(when_node, condition);
18281
18282 // If we found a missing node, then this is a syntax
18283 // error and we should stop looping.
18284 if (PM_NODE_TYPE_P(condition, PM_MISSING_NODE)) break;
18285
18286 // If this is a string node, then we need to mark it
18287 // as frozen because when clause strings are frozen.
18288 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
18289 pm_node_flag_set(condition, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
18290 } else if (PM_NODE_TYPE_P(condition, PM_SOURCE_FILE_NODE)) {
18291 pm_node_flag_set(condition, PM_NODE_FLAG_STATIC_LITERAL);
18292 }
18293
18294 pm_when_clause_static_literals_add(parser, &literals, condition);
18295 }
18296 } while (accept1(parser, PM_TOKEN_COMMA));
18297
18298 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18299 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18300 pm_when_node_then_keyword_loc_set(when_node, &parser->previous);
18301 }
18302 } else {
18303 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER);
18304 pm_when_node_then_keyword_loc_set(when_node, &parser->previous);
18305 }
18306
18307 if (!match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18308 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_CASE_WHEN, (uint16_t) (depth + 1));
18309 if (statements != NULL) {
18310 pm_when_node_statements_set(when_node, statements);
18311 }
18312 }
18313
18314 pm_case_node_condition_append(case_node, UP(when_node));
18315 }
18316
18317 // If we didn't parse any conditions (in or when) then we need
18318 // to indicate that we have an error.
18319 if (case_node->conditions.size == 0) {
18320 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18321 }
18322
18323 pm_static_literals_free(&literals);
18324 node = UP(case_node);
18325 } else {
18326 pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate, &end_keyword);
18327
18328 // If this is a case-match node (i.e., it is a pattern matching
18329 // case statement) then we must have a predicate.
18330 if (predicate == NULL) {
18331 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MATCH_MISSING_PREDICATE);
18332 }
18333
18334 // At this point we expect that we're parsing a case-in node. We
18335 // will continue to parse the in nodes until we hit the end of
18336 // the list.
18337 while (match1(parser, PM_TOKEN_KEYWORD_IN)) {
18338 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18339
18340 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
18341 parser->pattern_matching_newlines = true;
18342
18343 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
18344 parser->command_start = false;
18345 parser_lex(parser);
18346
18347 pm_token_t in_keyword = parser->previous;
18348
18349 pm_constant_id_list_t captures = { 0 };
18350 pm_node_t *pattern = parse_pattern(parser, &captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_IN, (uint16_t) (depth + 1));
18351
18352 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
18353 pm_constant_id_list_free(&captures);
18354
18355 // Since we're in the top-level of the case-in node we need
18356 // to check for guard clauses in the form of `if` or
18357 // `unless` statements.
18358 if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) {
18359 pm_token_t keyword = parser->previous;
18360 pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1));
18361 pattern = UP(pm_if_node_modifier_create(parser, pattern, &keyword, predicate));
18362 } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) {
18363 pm_token_t keyword = parser->previous;
18364 pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1));
18365 pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate));
18366 }
18367
18368 // Now we need to check for the terminator of the in node's
18369 // pattern. It can be a newline or semicolon optionally
18370 // followed by a `then` keyword.
18371 pm_token_t then_keyword;
18372 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18373 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18374 then_keyword = parser->previous;
18375 } else {
18376 then_keyword = not_provided(parser);
18377 }
18378 } else {
18379 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_IN_DELIMITER);
18380 then_keyword = parser->previous;
18381 }
18382
18383 // Now we can actually parse the statements associated with
18384 // the in node.
18385 pm_statements_node_t *statements;
18386 if (match3(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18387 statements = NULL;
18388 } else {
18389 statements = parse_statements(parser, PM_CONTEXT_CASE_IN, (uint16_t) (depth + 1));
18390 }
18391
18392 // Now that we have the full pattern and statements, we can
18393 // create the node and attach it to the case node.
18394 pm_node_t *condition = UP(pm_in_node_create(parser, pattern, statements, &in_keyword, &then_keyword));
18395 pm_case_match_node_condition_append(case_node, condition);
18396 }
18397
18398 // If we didn't parse any conditions (in or when) then we need
18399 // to indicate that we have an error.
18400 if (case_node->conditions.size == 0) {
18401 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18402 }
18403
18404 node = UP(case_node);
18405 }
18406
18407 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18408 if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) {
18409 pm_token_t else_keyword = parser->previous;
18410 pm_else_node_t *else_node;
18411
18412 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
18413 else_node = pm_else_node_create(parser, &else_keyword, parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1)), &parser->current);
18414 } else {
18415 else_node = pm_else_node_create(parser, &else_keyword, NULL, &parser->current);
18416 }
18417
18418 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18419 pm_case_node_else_clause_set((pm_case_node_t *) node, else_node);
18420 } else {
18421 pm_case_match_node_else_clause_set((pm_case_match_node_t *) node, else_node);
18422 }
18423 }
18424
18425 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18426 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword);
18427
18428 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18429 pm_case_node_end_keyword_loc_set((pm_case_node_t *) node, &parser->previous);
18430 } else {
18431 pm_case_match_node_end_keyword_loc_set((pm_case_match_node_t *) node, &parser->previous);
18432 }
18433
18434 pop_block_exits(parser, previous_block_exits);
18435 pm_node_list_free(&current_block_exits);
18436
18437 return node;
18438 }
18439 case PM_TOKEN_KEYWORD_BEGIN: {
18440 size_t opening_newline_index = token_newline_index(parser);
18441 parser_lex(parser);
18442
18443 pm_token_t begin_keyword = parser->previous;
18444 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18445
18446 pm_node_list_t current_block_exits = { 0 };
18447 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18448 pm_statements_node_t *begin_statements = NULL;
18449
18450 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18451 pm_accepts_block_stack_push(parser, true);
18452 begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN, (uint16_t) (depth + 1));
18453 pm_accepts_block_stack_pop(parser);
18454 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18455 }
18456
18457 pm_begin_node_t *begin_node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
18458 parse_rescues(parser, opening_newline_index, &begin_keyword, begin_node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1));
18459 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword);
18460
18461 begin_node->base.location.end = parser->previous.end;
18462 pm_begin_node_end_keyword_set(begin_node, &parser->previous);
18463
18464 pop_block_exits(parser, previous_block_exits);
18465 pm_node_list_free(&current_block_exits);
18466
18467 return UP(begin_node);
18468 }
18469 case PM_TOKEN_KEYWORD_BEGIN_UPCASE: {
18470 pm_node_list_t current_block_exits = { 0 };
18471 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18472
18473 if (binding_power != PM_BINDING_POWER_STATEMENT) {
18474 pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
18475 }
18476
18477 parser_lex(parser);
18478 pm_token_t keyword = parser->previous;
18479
18480 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_BEGIN_UPCASE_BRACE);
18481 pm_token_t opening = parser->previous;
18482 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE, (uint16_t) (depth + 1));
18483
18484 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening);
18485 pm_context_t context = parser->current_context->context;
18486 if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
18487 pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
18488 }
18489
18490 flush_block_exits(parser, previous_block_exits);
18491 pm_node_list_free(&current_block_exits);
18492
18493 return UP(pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous));
18494 }
18495 case PM_TOKEN_KEYWORD_BREAK:
18496 case PM_TOKEN_KEYWORD_NEXT:
18497 case PM_TOKEN_KEYWORD_RETURN: {
18498 parser_lex(parser);
18499
18500 pm_token_t keyword = parser->previous;
18501 pm_arguments_t arguments = { 0 };
18502
18503 if (
18504 token_begins_expression_p(parser->current.type) ||
18505 match2(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)
18506 ) {
18507 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
18508
18509 if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
18510 pm_token_t next = parser->current;
18511 parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, (uint16_t) (depth + 1));
18512
18513 // Reject `foo && return bar`.
18514 if (!accepts_command_call && arguments.arguments != NULL) {
18515 PM_PARSER_ERR_TOKEN_FORMAT(parser, next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(next.type));
18516 }
18517 }
18518 }
18519
18520 switch (keyword.type) {
18521 case PM_TOKEN_KEYWORD_BREAK: {
18522 pm_node_t *node = UP(pm_break_node_create(parser, &keyword, arguments.arguments));
18523 if (!parser->partial_script) parse_block_exit(parser, node);
18524 return node;
18525 }
18526 case PM_TOKEN_KEYWORD_NEXT: {
18527 pm_node_t *node = UP(pm_next_node_create(parser, &keyword, arguments.arguments));
18528 if (!parser->partial_script) parse_block_exit(parser, node);
18529 return node;
18530 }
18531 case PM_TOKEN_KEYWORD_RETURN: {
18532 pm_node_t *node = UP(pm_return_node_create(parser, &keyword, arguments.arguments));
18533 parse_return(parser, node);
18534 return node;
18535 }
18536 default:
18537 assert(false && "unreachable");
18538 return UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end));
18539 }
18540 }
18541 case PM_TOKEN_KEYWORD_SUPER: {
18542 parser_lex(parser);
18543
18544 pm_token_t keyword = parser->previous;
18545 pm_arguments_t arguments = { 0 };
18546 parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1));
18547
18548 if (
18549 arguments.opening_loc.start == NULL &&
18550 arguments.arguments == NULL &&
18551 ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE))
18552 ) {
18553 return UP(pm_forwarding_super_node_create(parser, &keyword, &arguments));
18554 }
18555
18556 return UP(pm_super_node_create(parser, &keyword, &arguments));
18557 }
18558 case PM_TOKEN_KEYWORD_YIELD: {
18559 parser_lex(parser);
18560
18561 pm_token_t keyword = parser->previous;
18562 pm_arguments_t arguments = { 0 };
18563 parse_arguments_list(parser, &arguments, false, accepts_command_call, (uint16_t) (depth + 1));
18564
18565 // It's possible that we've parsed a block argument through our
18566 // call to parse_arguments_list. If we found one, we should mark it
18567 // as invalid and destroy it, as we don't have a place for it on the
18568 // yield node.
18569 if (arguments.block != NULL) {
18570 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
18571 pm_node_unreference(parser, arguments.block);
18572 pm_node_destroy(parser, arguments.block);
18573 arguments.block = NULL;
18574 }
18575
18576 pm_node_t *node = UP(pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc));
18577 if (!parser->parsing_eval && !parser->partial_script) parse_yield(parser, node);
18578
18579 return node;
18580 }
18581 case PM_TOKEN_KEYWORD_CLASS: {
18582 size_t opening_newline_index = token_newline_index(parser);
18583 parser_lex(parser);
18584
18585 pm_token_t class_keyword = parser->previous;
18586 pm_do_loop_stack_push(parser, false);
18587
18588 pm_node_list_t current_block_exits = { 0 };
18589 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18590
18591 if (accept1(parser, PM_TOKEN_LESS_LESS)) {
18592 pm_token_t operator = parser->previous;
18593 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS, (uint16_t) (depth + 1));
18594
18595 pm_parser_scope_push(parser, true);
18596 if (!match2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18597 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_type_human(parser->current.type));
18598 }
18599
18600 pm_node_t *statements = NULL;
18601 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18602 pm_accepts_block_stack_push(parser, true);
18603 statements = UP(parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1)));
18604 pm_accepts_block_stack_pop(parser);
18605 }
18606
18607 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18608 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18609 statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_SCLASS, (uint16_t) (depth + 1)));
18610 } else {
18611 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18612 }
18613
18614 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18615
18616 pm_constant_id_list_t locals;
18617 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18618
18619 pm_parser_scope_pop(parser);
18620 pm_do_loop_stack_pop(parser);
18621
18622 flush_block_exits(parser, previous_block_exits);
18623 pm_node_list_free(&current_block_exits);
18624
18625 return UP(pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous));
18626 }
18627
18628 pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_CLASS_NAME, (uint16_t) (depth + 1));
18629 pm_token_t name = parser->previous;
18630 if (name.type != PM_TOKEN_CONSTANT) {
18631 pm_parser_err_token(parser, &name, PM_ERR_CLASS_NAME);
18632 }
18633
18634 pm_token_t inheritance_operator;
18635 pm_node_t *superclass;
18636
18637 if (match1(parser, PM_TOKEN_LESS)) {
18638 inheritance_operator = parser->current;
18639 lex_state_set(parser, PM_LEX_STATE_BEG);
18640
18641 parser->command_start = true;
18642 parser_lex(parser);
18643
18644 superclass = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_CLASS_SUPERCLASS, (uint16_t) (depth + 1));
18645 } else {
18646 inheritance_operator = not_provided(parser);
18647 superclass = NULL;
18648 }
18649
18650 pm_parser_scope_push(parser, true);
18651
18652 if (inheritance_operator.type != PM_TOKEN_NOT_PROVIDED) {
18653 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CLASS_UNEXPECTED_END);
18654 } else {
18655 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18656 }
18657 pm_node_t *statements = NULL;
18658
18659 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18660 pm_accepts_block_stack_push(parser, true);
18661 statements = UP(parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1)));
18662 pm_accepts_block_stack_pop(parser);
18663 }
18664
18665 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18666 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18667 statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &class_keyword, class_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_CLASS, (uint16_t) (depth + 1)));
18668 } else {
18669 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18670 }
18671
18672 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18673
18674 if (context_def_p(parser)) {
18675 pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD);
18676 }
18677
18678 pm_constant_id_list_t locals;
18679 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18680
18681 pm_parser_scope_pop(parser);
18682 pm_do_loop_stack_pop(parser);
18683
18684 if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) {
18685 pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME);
18686 }
18687
18688 pop_block_exits(parser, previous_block_exits);
18689 pm_node_list_free(&current_block_exits);
18690
18691 return UP(pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous));
18692 }
18693 case PM_TOKEN_KEYWORD_DEF: {
18694 pm_node_list_t current_block_exits = { 0 };
18695 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18696
18697 pm_token_t def_keyword = parser->current;
18698 size_t opening_newline_index = token_newline_index(parser);
18699
18700 pm_node_t *receiver = NULL;
18701 pm_token_t operator = not_provided(parser);
18702 pm_token_t name;
18703
18704 // This context is necessary for lexing `...` in a bare params
18705 // correctly. It must be pushed before lexing the first param, so it
18706 // is here.
18707 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18708 parser_lex(parser);
18709
18710 // This will be false if the method name is not a valid identifier
18711 // but could be followed by an operator.
18712 bool valid_name = true;
18713
18714 switch (parser->current.type) {
18715 case PM_CASE_OPERATOR:
18716 pm_parser_scope_push(parser, true);
18717 lex_state_set(parser, PM_LEX_STATE_ENDFN);
18718 parser_lex(parser);
18719
18720 name = parser->previous;
18721 break;
18722 case PM_TOKEN_IDENTIFIER: {
18723 parser_lex(parser);
18724
18725 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18726 receiver = parse_variable_call(parser);
18727
18728 pm_parser_scope_push(parser, true);
18729 lex_state_set(parser, PM_LEX_STATE_FNAME);
18730 parser_lex(parser);
18731
18732 operator = parser->previous;
18733 name = parse_method_definition_name(parser);
18734 } else {
18735 pm_refute_numbered_parameter(parser, parser->previous.start, parser->previous.end);
18736 pm_parser_scope_push(parser, true);
18737
18738 name = parser->previous;
18739 }
18740
18741 break;
18742 }
18743 case PM_TOKEN_INSTANCE_VARIABLE:
18744 case PM_TOKEN_CLASS_VARIABLE:
18745 case PM_TOKEN_GLOBAL_VARIABLE:
18746 valid_name = false;
18748 case PM_TOKEN_CONSTANT:
18749 case PM_TOKEN_KEYWORD_NIL:
18750 case PM_TOKEN_KEYWORD_SELF:
18751 case PM_TOKEN_KEYWORD_TRUE:
18752 case PM_TOKEN_KEYWORD_FALSE:
18753 case PM_TOKEN_KEYWORD___FILE__:
18754 case PM_TOKEN_KEYWORD___LINE__:
18755 case PM_TOKEN_KEYWORD___ENCODING__: {
18756 pm_parser_scope_push(parser, true);
18757 parser_lex(parser);
18758
18759 pm_token_t identifier = parser->previous;
18760
18761 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18762 lex_state_set(parser, PM_LEX_STATE_FNAME);
18763 parser_lex(parser);
18764 operator = parser->previous;
18765
18766 switch (identifier.type) {
18767 case PM_TOKEN_CONSTANT:
18768 receiver = UP(pm_constant_read_node_create(parser, &identifier));
18769 break;
18770 case PM_TOKEN_INSTANCE_VARIABLE:
18771 receiver = UP(pm_instance_variable_read_node_create(parser, &identifier));
18772 break;
18773 case PM_TOKEN_CLASS_VARIABLE:
18774 receiver = UP(pm_class_variable_read_node_create(parser, &identifier));
18775 break;
18776 case PM_TOKEN_GLOBAL_VARIABLE:
18777 receiver = UP(pm_global_variable_read_node_create(parser, &identifier));
18778 break;
18779 case PM_TOKEN_KEYWORD_NIL:
18780 receiver = UP(pm_nil_node_create(parser, &identifier));
18781 break;
18782 case PM_TOKEN_KEYWORD_SELF:
18783 receiver = UP(pm_self_node_create(parser, &identifier));
18784 break;
18785 case PM_TOKEN_KEYWORD_TRUE:
18786 receiver = UP(pm_true_node_create(parser, &identifier));
18787 break;
18788 case PM_TOKEN_KEYWORD_FALSE:
18789 receiver = UP(pm_false_node_create(parser, &identifier));
18790 break;
18791 case PM_TOKEN_KEYWORD___FILE__:
18792 receiver = UP(pm_source_file_node_create(parser, &identifier));
18793 break;
18794 case PM_TOKEN_KEYWORD___LINE__:
18795 receiver = UP(pm_source_line_node_create(parser, &identifier));
18796 break;
18797 case PM_TOKEN_KEYWORD___ENCODING__:
18798 receiver = UP(pm_source_encoding_node_create(parser, &identifier));
18799 break;
18800 default:
18801 break;
18802 }
18803
18804 name = parse_method_definition_name(parser);
18805 } else {
18806 if (!valid_name) {
18807 PM_PARSER_ERR_TOKEN_FORMAT(parser, identifier, PM_ERR_DEF_NAME, pm_token_type_human(identifier.type));
18808 }
18809
18810 name = identifier;
18811 }
18812 break;
18813 }
18814 case PM_TOKEN_PARENTHESIS_LEFT: {
18815 // The current context is `PM_CONTEXT_DEF_PARAMS`, however
18816 // the inner expression of this parenthesis should not be
18817 // processed under this context. Thus, the context is popped
18818 // here.
18819 context_pop(parser);
18820 parser_lex(parser);
18821
18822 pm_token_t lparen = parser->previous;
18823 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEF_RECEIVER, (uint16_t) (depth + 1));
18824
18825 accept1(parser, PM_TOKEN_NEWLINE);
18826 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
18827 pm_token_t rparen = parser->previous;
18828
18829 lex_state_set(parser, PM_LEX_STATE_FNAME);
18830 expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM);
18831
18832 operator = parser->previous;
18833 receiver = UP(pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0));
18834
18835 // To push `PM_CONTEXT_DEF_PARAMS` again is for the same
18836 // reason as described the above.
18837 pm_parser_scope_push(parser, true);
18838 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18839 name = parse_method_definition_name(parser);
18840 break;
18841 }
18842 default:
18843 pm_parser_scope_push(parser, true);
18844 name = parse_method_definition_name(parser);
18845 break;
18846 }
18847
18848 pm_token_t lparen;
18849 pm_token_t rparen;
18850 pm_parameters_node_t *params;
18851
18852 bool accept_endless_def = true;
18853 switch (parser->current.type) {
18854 case PM_TOKEN_PARENTHESIS_LEFT: {
18855 parser_lex(parser);
18856 lparen = parser->previous;
18857
18858 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18859 params = NULL;
18860 } else {
18861 params = parse_parameters(
18862 parser,
18863 PM_BINDING_POWER_DEFINED,
18864 true,
18865 false,
18866 true,
18867 true,
18868 false,
18869 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18870 (uint16_t) (depth + 1)
18871 );
18872 }
18873
18874 lex_state_set(parser, PM_LEX_STATE_BEG);
18875 parser->command_start = true;
18876
18877 context_pop(parser);
18878 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18879 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_type_human(parser->current.type));
18880 parser->previous.start = parser->previous.end;
18881 parser->previous.type = PM_TOKEN_MISSING;
18882 }
18883
18884 rparen = parser->previous;
18885 break;
18886 }
18887 case PM_CASE_PARAMETER: {
18888 // If we're about to lex a label, we need to add the label
18889 // state to make sure the next newline is ignored.
18890 if (parser->current.type == PM_TOKEN_LABEL) {
18891 lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL);
18892 }
18893
18894 lparen = not_provided(parser);
18895 rparen = not_provided(parser);
18896 params = parse_parameters(
18897 parser,
18898 PM_BINDING_POWER_DEFINED,
18899 false,
18900 false,
18901 true,
18902 true,
18903 false,
18904 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18905 (uint16_t) (depth + 1)
18906 );
18907
18908 // Reject `def * = 1` and similar. We have to specifically check
18909 // for them because they create ambiguity with optional arguments.
18910 accept_endless_def = false;
18911
18912 context_pop(parser);
18913 break;
18914 }
18915 default: {
18916 lparen = not_provided(parser);
18917 rparen = not_provided(parser);
18918 params = NULL;
18919
18920 context_pop(parser);
18921 break;
18922 }
18923 }
18924
18925 pm_node_t *statements = NULL;
18926 pm_token_t equal;
18927 pm_token_t end_keyword;
18928
18929 if (accept1(parser, PM_TOKEN_EQUAL)) {
18930 if (token_is_setter_name(&name)) {
18931 pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER);
18932 }
18933 if (!accept_endless_def) {
18934 pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
18935 }
18936 if (
18939 ) {
18940 PM_PARSER_ERR_FORMAT(parser, def_keyword.start, parser->previous.end, PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE, "endless method definition");
18941 }
18942 equal = parser->previous;
18943
18944 context_push(parser, PM_CONTEXT_DEF);
18945 pm_do_loop_stack_push(parser, false);
18946 statements = UP(pm_statements_node_create(parser));
18947
18948 bool allow_command_call;
18949 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
18950 allow_command_call = accepts_command_call;
18951 } else {
18952 // Allow `def foo = puts "Hello"` but not `private def foo = puts "Hello"`
18953 allow_command_call = binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION;
18954 }
18955
18956 pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_command_call, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1));
18957
18958 if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
18959 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
18960
18961 pm_token_t rescue_keyword = parser->previous;
18962 pm_node_t *value = parse_expression(parser, PM_BINDING_POWER_MATCH + 1, false, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
18963 context_pop(parser);
18964
18965 statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value));
18966 }
18967
18968 pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false);
18969 pm_do_loop_stack_pop(parser);
18970 context_pop(parser);
18971 end_keyword = not_provided(parser);
18972 } else {
18973 equal = not_provided(parser);
18974
18975 if (lparen.type == PM_TOKEN_NOT_PROVIDED) {
18976 lex_state_set(parser, PM_LEX_STATE_BEG);
18977 parser->command_start = true;
18978 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_DEF_PARAMS_TERM);
18979 } else {
18980 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18981 }
18982
18983 pm_accepts_block_stack_push(parser, true);
18984 pm_do_loop_stack_push(parser, false);
18985
18986 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18987 pm_accepts_block_stack_push(parser, true);
18988 statements = UP(parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1)));
18989 pm_accepts_block_stack_pop(parser);
18990 }
18991
18992 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18993 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18994 statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &def_keyword, def_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_DEF, (uint16_t) (depth + 1)));
18995 } else {
18996 parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false);
18997 }
18998
18999 pm_accepts_block_stack_pop(parser);
19000 pm_do_loop_stack_pop(parser);
19001
19002 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword);
19003 end_keyword = parser->previous;
19004 }
19005
19006 pm_constant_id_list_t locals;
19007 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
19008 pm_parser_scope_pop(parser);
19009
19015 pm_constant_id_t name_id = pm_parser_constant_id_location(parser, name.start, parse_operator_symbol_name(&name));
19016
19017 flush_block_exits(parser, previous_block_exits);
19018 pm_node_list_free(&current_block_exits);
19019
19020 return UP(pm_def_node_create(
19021 parser,
19022 name_id,
19023 &name,
19024 receiver,
19025 params,
19026 statements,
19027 &locals,
19028 &def_keyword,
19029 &operator,
19030 &lparen,
19031 &rparen,
19032 &equal,
19033 &end_keyword
19034 ));
19035 }
19036 case PM_TOKEN_KEYWORD_DEFINED: {
19037 parser_lex(parser);
19038 pm_token_t keyword = parser->previous;
19039
19040 pm_token_t lparen;
19041 pm_token_t rparen;
19042 pm_node_t *expression;
19043
19044 context_push(parser, PM_CONTEXT_DEFINED);
19045 bool newline = accept1(parser, PM_TOKEN_NEWLINE);
19046
19047 if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
19048 lparen = parser->previous;
19049
19050 if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19051 expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
19052 lparen = not_provided(parser);
19053 rparen = not_provided(parser);
19054 } else {
19055 expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19056
19057 if (parser->recovering) {
19058 rparen = not_provided(parser);
19059 } else {
19060 accept1(parser, PM_TOKEN_NEWLINE);
19061 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19062 rparen = parser->previous;
19063 }
19064 }
19065 } else {
19066 lparen = not_provided(parser);
19067 rparen = not_provided(parser);
19068 expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19069 }
19070
19071 context_pop(parser);
19072 return UP(pm_defined_node_create(
19073 parser,
19074 &lparen,
19075 expression,
19076 &rparen,
19077 &keyword
19078 ));
19079 }
19080 case PM_TOKEN_KEYWORD_END_UPCASE: {
19081 if (binding_power != PM_BINDING_POWER_STATEMENT) {
19082 pm_parser_err_current(parser, PM_ERR_STATEMENT_POSTEXE_END);
19083 }
19084
19085 parser_lex(parser);
19086 pm_token_t keyword = parser->previous;
19087
19088 if (context_def_p(parser)) {
19089 pm_parser_warn_token(parser, &keyword, PM_WARN_END_IN_METHOD);
19090 }
19091
19092 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_END_UPCASE_BRACE);
19093 pm_token_t opening = parser->previous;
19094 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE, (uint16_t) (depth + 1));
19095
19096 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening);
19097 return UP(pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous));
19098 }
19099 case PM_TOKEN_KEYWORD_FALSE:
19100 parser_lex(parser);
19101 return UP(pm_false_node_create(parser, &parser->previous));
19102 case PM_TOKEN_KEYWORD_FOR: {
19103 size_t opening_newline_index = token_newline_index(parser);
19104 parser_lex(parser);
19105
19106 pm_token_t for_keyword = parser->previous;
19107 pm_node_t *index;
19108
19109 context_push(parser, PM_CONTEXT_FOR_INDEX);
19110
19111 // First, parse out the first index expression.
19112 if (accept1(parser, PM_TOKEN_USTAR)) {
19113 pm_token_t star_operator = parser->previous;
19114 pm_node_t *name = NULL;
19115
19116 if (token_begins_expression_p(parser->current.type)) {
19117 name = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
19118 }
19119
19120 index = UP(pm_splat_node_create(parser, &star_operator, name));
19121 } else if (token_begins_expression_p(parser->current.type)) {
19122 index = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
19123 } else {
19124 pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX);
19125 index = UP(pm_missing_node_create(parser, for_keyword.start, for_keyword.end));
19126 }
19127
19128 // Now, if there are multiple index expressions, parse them out.
19129 if (match1(parser, PM_TOKEN_COMMA)) {
19130 index = parse_targets(parser, index, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19131 } else {
19132 index = parse_target(parser, index, false, false);
19133 }
19134
19135 context_pop(parser);
19136 pm_do_loop_stack_push(parser, true);
19137
19138 expect1(parser, PM_TOKEN_KEYWORD_IN, PM_ERR_FOR_IN);
19139 pm_token_t in_keyword = parser->previous;
19140
19141 pm_node_t *collection = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_FOR_COLLECTION, (uint16_t) (depth + 1));
19142 pm_do_loop_stack_pop(parser);
19143
19144 pm_token_t do_keyword;
19145 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
19146 do_keyword = parser->previous;
19147 } else {
19148 do_keyword = not_provided(parser);
19149 if (!match2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)) {
19150 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_type_human(parser->current.type));
19151 }
19152 }
19153
19154 pm_statements_node_t *statements = NULL;
19155 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
19156 statements = parse_statements(parser, PM_CONTEXT_FOR, (uint16_t) (depth + 1));
19157 }
19158
19159 parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false);
19160 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword);
19161
19162 return UP(pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, &do_keyword, &parser->previous));
19163 }
19164 case PM_TOKEN_KEYWORD_IF:
19165 if (parser_end_of_line_p(parser)) {
19166 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_KEYWORD_EOL);
19167 }
19168
19169 size_t opening_newline_index = token_newline_index(parser);
19170 bool if_after_else = parser->previous.type == PM_TOKEN_KEYWORD_ELSE;
19171 parser_lex(parser);
19172
19173 return parse_conditional(parser, PM_CONTEXT_IF, opening_newline_index, if_after_else, (uint16_t) (depth + 1));
19174 case PM_TOKEN_KEYWORD_UNDEF: {
19175 if (binding_power != PM_BINDING_POWER_STATEMENT) {
19176 pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF);
19177 }
19178
19179 parser_lex(parser);
19180 pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous);
19181 pm_node_t *name = parse_undef_argument(parser, (uint16_t) (depth + 1));
19182
19183 if (PM_NODE_TYPE_P(name, PM_MISSING_NODE)) {
19184 pm_node_destroy(parser, name);
19185 } else {
19186 pm_undef_node_append(undef, name);
19187
19188 while (match1(parser, PM_TOKEN_COMMA)) {
19189 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
19190 parser_lex(parser);
19191 name = parse_undef_argument(parser, (uint16_t) (depth + 1));
19192
19193 if (PM_NODE_TYPE_P(name, PM_MISSING_NODE)) {
19194 pm_node_destroy(parser, name);
19195 break;
19196 }
19197
19198 pm_undef_node_append(undef, name);
19199 }
19200 }
19201
19202 return UP(undef);
19203 }
19204 case PM_TOKEN_KEYWORD_NOT: {
19205 parser_lex(parser);
19206
19207 pm_token_t message = parser->previous;
19208 pm_arguments_t arguments = { 0 };
19209 pm_node_t *receiver = NULL;
19210
19211 // If we do not accept a command call, then we also do not accept a
19212 // not without parentheses. In this case we need to reject this
19213 // syntax.
19214 if (!accepts_command_call && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
19215 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) {
19216 pm_parser_err(parser, parser->previous.end, parser->previous.end + 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN);
19217 } else {
19218 accept1(parser, PM_TOKEN_NEWLINE);
19219 pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER);
19220 }
19221
19222 return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end));
19223 }
19224
19225 accept1(parser, PM_TOKEN_NEWLINE);
19226
19227 if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
19228 pm_token_t lparen = parser->previous;
19229
19230 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19231 receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
19232 } else {
19233 arguments.opening_loc = PM_LOCATION_TOKEN_VALUE(&lparen);
19234 receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1));
19235
19236 if (!parser->recovering) {
19237 accept1(parser, PM_TOKEN_NEWLINE);
19238 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19239 arguments.closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
19240 }
19241 }
19242 } else {
19243 receiver = parse_expression(parser, PM_BINDING_POWER_NOT, true, false, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1));
19244 }
19245
19246 return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
19247 }
19248 case PM_TOKEN_KEYWORD_UNLESS: {
19249 size_t opening_newline_index = token_newline_index(parser);
19250 parser_lex(parser);
19251
19252 return parse_conditional(parser, PM_CONTEXT_UNLESS, opening_newline_index, false, (uint16_t) (depth + 1));
19253 }
19254 case PM_TOKEN_KEYWORD_MODULE: {
19255 pm_node_list_t current_block_exits = { 0 };
19256 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19257
19258 size_t opening_newline_index = token_newline_index(parser);
19259 parser_lex(parser);
19260 pm_token_t module_keyword = parser->previous;
19261
19262 pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_MODULE_NAME, (uint16_t) (depth + 1));
19263 pm_token_t name;
19264
19265 // If we can recover from a syntax error that occurred while parsing
19266 // the name of the module, then we'll handle that here.
19267 if (PM_NODE_TYPE_P(constant_path, PM_MISSING_NODE)) {
19268 pop_block_exits(parser, previous_block_exits);
19269 pm_node_list_free(&current_block_exits);
19270
19271 pm_token_t missing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
19272 return UP(pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing));
19273 }
19274
19275 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
19276 pm_token_t double_colon = parser->previous;
19277
19278 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
19279 constant_path = UP(pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous));
19280 }
19281
19282 // Here we retrieve the name of the module. If it wasn't a constant,
19283 // then it's possible that `module foo` was passed, which is a
19284 // syntax error. We handle that here as well.
19285 name = parser->previous;
19286 if (name.type != PM_TOKEN_CONSTANT) {
19287 pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME);
19288 }
19289
19290 pm_parser_scope_push(parser, true);
19291 accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE);
19292 pm_node_t *statements = NULL;
19293
19294 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
19295 pm_accepts_block_stack_push(parser, true);
19296 statements = UP(parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1)));
19297 pm_accepts_block_stack_pop(parser);
19298 }
19299
19300 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
19301 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
19302 statements = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &module_keyword, module_keyword.start, (pm_statements_node_t *) statements, PM_RESCUES_MODULE, (uint16_t) (depth + 1)));
19303 } else {
19304 parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false);
19305 }
19306
19307 pm_constant_id_list_t locals;
19308 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
19309
19310 pm_parser_scope_pop(parser);
19311 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword);
19312
19313 if (context_def_p(parser)) {
19314 pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
19315 }
19316
19317 pop_block_exits(parser, previous_block_exits);
19318 pm_node_list_free(&current_block_exits);
19319
19320 return UP(pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous));
19321 }
19322 case PM_TOKEN_KEYWORD_NIL:
19323 parser_lex(parser);
19324 return UP(pm_nil_node_create(parser, &parser->previous));
19325 case PM_TOKEN_KEYWORD_REDO: {
19326 parser_lex(parser);
19327
19328 pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous));
19329 if (!parser->partial_script) parse_block_exit(parser, node);
19330
19331 return node;
19332 }
19333 case PM_TOKEN_KEYWORD_RETRY: {
19334 parser_lex(parser);
19335
19336 pm_node_t *node = UP(pm_retry_node_create(parser, &parser->previous));
19337 parse_retry(parser, node);
19338
19339 return node;
19340 }
19341 case PM_TOKEN_KEYWORD_SELF:
19342 parser_lex(parser);
19343 return UP(pm_self_node_create(parser, &parser->previous));
19344 case PM_TOKEN_KEYWORD_TRUE:
19345 parser_lex(parser);
19346 return UP(pm_true_node_create(parser, &parser->previous));
19347 case PM_TOKEN_KEYWORD_UNTIL: {
19348 size_t opening_newline_index = token_newline_index(parser);
19349
19350 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
19351 pm_do_loop_stack_push(parser, true);
19352
19353 parser_lex(parser);
19354 pm_token_t keyword = parser->previous;
19355 pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1));
19356
19357 pm_do_loop_stack_pop(parser);
19358 context_pop(parser);
19359
19360 pm_token_t do_keyword;
19361 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
19362 do_keyword = parser->previous;
19363 } else {
19364 do_keyword = not_provided(parser);
19365 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
19366 }
19367
19368 pm_statements_node_t *statements = NULL;
19369 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
19370 pm_accepts_block_stack_push(parser, true);
19371 statements = parse_statements(parser, PM_CONTEXT_UNTIL, (uint16_t) (depth + 1));
19372 pm_accepts_block_stack_pop(parser);
19373 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19374 }
19375
19376 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
19377 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword);
19378
19379 return UP(pm_until_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0));
19380 }
19381 case PM_TOKEN_KEYWORD_WHILE: {
19382 size_t opening_newline_index = token_newline_index(parser);
19383
19384 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
19385 pm_do_loop_stack_push(parser, true);
19386
19387 parser_lex(parser);
19388 pm_token_t keyword = parser->previous;
19389 pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1));
19390
19391 pm_do_loop_stack_pop(parser);
19392 context_pop(parser);
19393
19394 pm_token_t do_keyword;
19395 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
19396 do_keyword = parser->previous;
19397 } else {
19398 do_keyword = not_provided(parser);
19399 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
19400 }
19401
19402 pm_statements_node_t *statements = NULL;
19403 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
19404 pm_accepts_block_stack_push(parser, true);
19405 statements = parse_statements(parser, PM_CONTEXT_WHILE, (uint16_t) (depth + 1));
19406 pm_accepts_block_stack_pop(parser);
19407 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19408 }
19409
19410 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
19411 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword);
19412
19413 return UP(pm_while_node_create(parser, &keyword, &do_keyword, &parser->previous, predicate, statements, 0));
19414 }
19415 case PM_TOKEN_PERCENT_LOWER_I: {
19416 parser_lex(parser);
19417 pm_token_t opening = parser->previous;
19418 pm_array_node_t *array = pm_array_node_create(parser, &opening);
19419 pm_node_t *current = NULL;
19420
19421 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
19422 accept1(parser, PM_TOKEN_WORDS_SEP);
19423 if (match1(parser, PM_TOKEN_STRING_END)) break;
19424
19425 // Interpolation is not possible but nested heredocs can still lead to
19426 // consecutive (disjoint) string tokens when the final newline is escaped.
19427 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
19428 pm_token_t opening = not_provided(parser);
19429 pm_token_t closing = not_provided(parser);
19430
19431 // Record the string node, moving to interpolation if needed.
19432 if (current == NULL) {
19433 current = UP(pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing));
19434 parser_lex(parser);
19435 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
19436 pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing));
19437 parser_lex(parser);
19438 pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, string);
19439 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19440 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
19441 pm_token_t bounds = not_provided(parser);
19442
19443 pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = cast->value_loc.start, .end = cast->value_loc.end };
19444 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped));
19445 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, &opening, &parser->previous, &closing));
19446 parser_lex(parser);
19447
19448 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing);
19449 pm_interpolated_symbol_node_append(interpolated, first_string);
19450 pm_interpolated_symbol_node_append(interpolated, second_string);
19451
19452 xfree(current);
19453 current = UP(interpolated);
19454 } else {
19455 assert(false && "unreachable");
19456 }
19457 }
19458
19459 if (current) {
19460 pm_array_node_elements_append(array, current);
19461 current = NULL;
19462 } else {
19463 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT);
19464 }
19465 }
19466
19467 pm_token_t closing = parser->current;
19468 if (match1(parser, PM_TOKEN_EOF)) {
19469 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_LOWER_TERM);
19470 closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
19471 } else {
19472 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM);
19473 }
19474 pm_array_node_close_set(array, &closing);
19475
19476 return UP(array);
19477 }
19478 case PM_TOKEN_PERCENT_UPPER_I: {
19479 parser_lex(parser);
19480 pm_token_t opening = parser->previous;
19481 pm_array_node_t *array = pm_array_node_create(parser, &opening);
19482
19483 // This is the current node that we are parsing that will be added to the
19484 // list of elements.
19485 pm_node_t *current = NULL;
19486
19487 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
19488 switch (parser->current.type) {
19489 case PM_TOKEN_WORDS_SEP: {
19490 if (current == NULL) {
19491 // If we hit a separator before we have any content, then we don't
19492 // need to do anything.
19493 } else {
19494 // If we hit a separator after we've hit content, then we need to
19495 // append that content to the list and reset the current node.
19496 pm_array_node_elements_append(array, current);
19497 current = NULL;
19498 }
19499
19500 parser_lex(parser);
19501 break;
19502 }
19503 case PM_TOKEN_STRING_CONTENT: {
19504 pm_token_t opening = not_provided(parser);
19505 pm_token_t closing = not_provided(parser);
19506
19507 if (current == NULL) {
19508 // If we hit content and the current node is NULL, then this is
19509 // the first string content we've seen. In that case we're going
19510 // to create a new string node and set that to the current.
19511 current = UP(pm_symbol_node_create_current_string(parser, &opening, &parser->current, &closing));
19512 parser_lex(parser);
19513 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
19514 // If we hit string content and the current node is an
19515 // interpolated string, then we need to append the string content
19516 // to the list of child nodes.
19517 pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing));
19518 parser_lex(parser);
19519
19520 pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, string);
19521 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19522 // If we hit string content and the current node is a symbol node,
19523 // then we need to convert the current node into an interpolated
19524 // string and add the string content to the list of child nodes.
19525 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
19526 pm_token_t bounds = not_provided(parser);
19527
19528 pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = cast->value_loc.start, .end = cast->value_loc.end };
19529 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, &bounds, &content, &bounds, &cast->unescaped));
19530 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, &opening, &parser->previous, &closing));
19531 parser_lex(parser);
19532
19533 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing);
19534 pm_interpolated_symbol_node_append(interpolated, first_string);
19535 pm_interpolated_symbol_node_append(interpolated, second_string);
19536
19537 xfree(current);
19538 current = UP(interpolated);
19539 } else {
19540 assert(false && "unreachable");
19541 }
19542
19543 break;
19544 }
19545 case PM_TOKEN_EMBVAR: {
19546 bool start_location_set = false;
19547 if (current == NULL) {
19548 // If we hit an embedded variable and the current node is NULL,
19549 // then this is the start of a new string. We'll set the current
19550 // node to a new interpolated string.
19551 pm_token_t opening = not_provided(parser);
19552 pm_token_t closing = not_provided(parser);
19553 current = UP(pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing));
19554 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19555 // If we hit an embedded variable and the current node is a string
19556 // node, then we'll convert the current into an interpolated
19557 // string and add the string node to the list of parts.
19558 pm_token_t opening = not_provided(parser);
19559 pm_token_t closing = not_provided(parser);
19560 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing);
19561
19562 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
19563 pm_interpolated_symbol_node_append(interpolated, current);
19564 interpolated->base.location.start = current->location.start;
19565 start_location_set = true;
19566 current = UP(interpolated);
19567 } else {
19568 // If we hit an embedded variable and the current node is an
19569 // interpolated string, then we'll just add the embedded variable.
19570 }
19571
19572 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19573 pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part);
19574 if (!start_location_set) {
19575 current->location.start = part->location.start;
19576 }
19577 break;
19578 }
19579 case PM_TOKEN_EMBEXPR_BEGIN: {
19580 bool start_location_set = false;
19581 if (current == NULL) {
19582 // If we hit an embedded expression and the current node is NULL,
19583 // then this is the start of a new string. We'll set the current
19584 // node to a new interpolated string.
19585 pm_token_t opening = not_provided(parser);
19586 pm_token_t closing = not_provided(parser);
19587 current = UP(pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing));
19588 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19589 // If we hit an embedded expression and the current node is a
19590 // string node, then we'll convert the current into an
19591 // interpolated string and add the string node to the list of
19592 // parts.
19593 pm_token_t opening = not_provided(parser);
19594 pm_token_t closing = not_provided(parser);
19595 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, &opening, NULL, &closing);
19596
19597 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
19598 pm_interpolated_symbol_node_append(interpolated, current);
19599 interpolated->base.location.start = current->location.start;
19600 start_location_set = true;
19601 current = UP(interpolated);
19602 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
19603 // If we hit an embedded expression and the current node is an
19604 // interpolated string, then we'll just continue on.
19605 } else {
19606 assert(false && "unreachable");
19607 }
19608
19609 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19610 pm_interpolated_symbol_node_append((pm_interpolated_symbol_node_t *) current, part);
19611 if (!start_location_set) {
19612 current->location.start = part->location.start;
19613 }
19614 break;
19615 }
19616 default:
19617 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_UPPER_ELEMENT);
19618 parser_lex(parser);
19619 break;
19620 }
19621 }
19622
19623 // If we have a current node, then we need to append it to the list.
19624 if (current) {
19625 pm_array_node_elements_append(array, current);
19626 }
19627
19628 pm_token_t closing = parser->current;
19629 if (match1(parser, PM_TOKEN_EOF)) {
19630 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_UPPER_TERM);
19631 closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
19632 } else {
19633 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM);
19634 }
19635 pm_array_node_close_set(array, &closing);
19636
19637 return UP(array);
19638 }
19639 case PM_TOKEN_PERCENT_LOWER_W: {
19640 parser_lex(parser);
19641 pm_token_t opening = parser->previous;
19642 pm_array_node_t *array = pm_array_node_create(parser, &opening);
19643 pm_node_t *current = NULL;
19644
19645 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
19646 accept1(parser, PM_TOKEN_WORDS_SEP);
19647 if (match1(parser, PM_TOKEN_STRING_END)) break;
19648
19649 // Interpolation is not possible but nested heredocs can still lead to
19650 // consecutive (disjoint) string tokens when the final newline is escaped.
19651 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
19652 pm_token_t opening = not_provided(parser);
19653 pm_token_t closing = not_provided(parser);
19654
19655 pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing));
19656
19657 // Record the string node, moving to interpolation if needed.
19658 if (current == NULL) {
19659 current = string;
19660 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
19661 pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, string);
19662 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19663 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing);
19664 pm_interpolated_string_node_append(interpolated, current);
19665 pm_interpolated_string_node_append(interpolated, string);
19666 current = UP(interpolated);
19667 } else {
19668 assert(false && "unreachable");
19669 }
19670 parser_lex(parser);
19671 }
19672
19673 if (current) {
19674 pm_array_node_elements_append(array, current);
19675 current = NULL;
19676 } else {
19677 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT);
19678 }
19679 }
19680
19681 pm_token_t closing = parser->current;
19682 if (match1(parser, PM_TOKEN_EOF)) {
19683 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_LOWER_TERM);
19684 closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
19685 } else {
19686 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM);
19687 }
19688
19689 pm_array_node_close_set(array, &closing);
19690 return UP(array);
19691 }
19692 case PM_TOKEN_PERCENT_UPPER_W: {
19693 parser_lex(parser);
19694 pm_token_t opening = parser->previous;
19695 pm_array_node_t *array = pm_array_node_create(parser, &opening);
19696
19697 // This is the current node that we are parsing that will be added
19698 // to the list of elements.
19699 pm_node_t *current = NULL;
19700
19701 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
19702 switch (parser->current.type) {
19703 case PM_TOKEN_WORDS_SEP: {
19704 // Reset the explicit encoding if we hit a separator
19705 // since each element can have its own encoding.
19706 parser->explicit_encoding = NULL;
19707
19708 if (current == NULL) {
19709 // If we hit a separator before we have any content,
19710 // then we don't need to do anything.
19711 } else {
19712 // If we hit a separator after we've hit content,
19713 // then we need to append that content to the list
19714 // and reset the current node.
19715 pm_array_node_elements_append(array, current);
19716 current = NULL;
19717 }
19718
19719 parser_lex(parser);
19720 break;
19721 }
19722 case PM_TOKEN_STRING_CONTENT: {
19723 pm_token_t opening = not_provided(parser);
19724 pm_token_t closing = not_provided(parser);
19725
19726 pm_node_t *string = UP(pm_string_node_create_current_string(parser, &opening, &parser->current, &closing));
19727 pm_node_flag_set(string, parse_unescaped_encoding(parser));
19728 parser_lex(parser);
19729
19730 if (current == NULL) {
19731 // If we hit content and the current node is NULL,
19732 // then this is the first string content we've seen.
19733 // In that case we're going to create a new string
19734 // node and set that to the current.
19735 current = string;
19736 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
19737 // If we hit string content and the current node is
19738 // an interpolated string, then we need to append
19739 // the string content to the list of child nodes.
19740 pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, string);
19741 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19742 // If we hit string content and the current node is
19743 // a string node, then we need to convert the
19744 // current node into an interpolated string and add
19745 // the string content to the list of child nodes.
19746 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing);
19747 pm_interpolated_string_node_append(interpolated, current);
19748 pm_interpolated_string_node_append(interpolated, string);
19749 current = UP(interpolated);
19750 } else {
19751 assert(false && "unreachable");
19752 }
19753
19754 break;
19755 }
19756 case PM_TOKEN_EMBVAR: {
19757 if (current == NULL) {
19758 // If we hit an embedded variable and the current
19759 // node is NULL, then this is the start of a new
19760 // string. We'll set the current node to a new
19761 // interpolated string.
19762 pm_token_t opening = not_provided(parser);
19763 pm_token_t closing = not_provided(parser);
19764 current = UP(pm_interpolated_string_node_create(parser, &opening, NULL, &closing));
19765 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19766 // If we hit an embedded variable and the current
19767 // node is a string node, then we'll convert the
19768 // current into an interpolated string and add the
19769 // string node to the list of parts.
19770 pm_token_t opening = not_provided(parser);
19771 pm_token_t closing = not_provided(parser);
19772 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing);
19773 pm_interpolated_string_node_append(interpolated, current);
19774 current = UP(interpolated);
19775 } else {
19776 // If we hit an embedded variable and the current
19777 // node is an interpolated string, then we'll just
19778 // add the embedded variable.
19779 }
19780
19781 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19782 pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part);
19783 break;
19784 }
19785 case PM_TOKEN_EMBEXPR_BEGIN: {
19786 if (current == NULL) {
19787 // If we hit an embedded expression and the current
19788 // node is NULL, then this is the start of a new
19789 // string. We'll set the current node to a new
19790 // interpolated string.
19791 pm_token_t opening = not_provided(parser);
19792 pm_token_t closing = not_provided(parser);
19793 current = UP(pm_interpolated_string_node_create(parser, &opening, NULL, &closing));
19794 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19795 // If we hit an embedded expression and the current
19796 // node is a string node, then we'll convert the
19797 // current into an interpolated string and add the
19798 // string node to the list of parts.
19799 pm_token_t opening = not_provided(parser);
19800 pm_token_t closing = not_provided(parser);
19801 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, &opening, NULL, &closing);
19802 pm_interpolated_string_node_append(interpolated, current);
19803 current = UP(interpolated);
19804 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
19805 // If we hit an embedded expression and the current
19806 // node is an interpolated string, then we'll just
19807 // continue on.
19808 } else {
19809 assert(false && "unreachable");
19810 }
19811
19812 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19813 pm_interpolated_string_node_append((pm_interpolated_string_node_t *) current, part);
19814 break;
19815 }
19816 default:
19817 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_UPPER_ELEMENT);
19818 parser_lex(parser);
19819 break;
19820 }
19821 }
19822
19823 // If we have a current node, then we need to append it to the list.
19824 if (current) {
19825 pm_array_node_elements_append(array, current);
19826 }
19827
19828 pm_token_t closing = parser->current;
19829 if (match1(parser, PM_TOKEN_EOF)) {
19830 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_UPPER_TERM);
19831 closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
19832 } else {
19833 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM);
19834 }
19835
19836 pm_array_node_close_set(array, &closing);
19837 return UP(array);
19838 }
19839 case PM_TOKEN_REGEXP_BEGIN: {
19840 pm_token_t opening = parser->current;
19841 parser_lex(parser);
19842
19843 if (match1(parser, PM_TOKEN_REGEXP_END)) {
19844 // If we get here, then we have an end immediately after a start. In
19845 // that case we'll create an empty content token and return an
19846 // uninterpolated regular expression.
19847 pm_token_t content = (pm_token_t) {
19848 .type = PM_TOKEN_STRING_CONTENT,
19849 .start = parser->previous.end,
19850 .end = parser->previous.end
19851 };
19852
19853 parser_lex(parser);
19854
19855 pm_node_t *node = UP(pm_regular_expression_node_create(parser, &opening, &content, &parser->previous));
19856 pm_node_flag_set(node, PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING);
19857
19858 return node;
19859 }
19860
19861 pm_interpolated_regular_expression_node_t *interpolated;
19862
19863 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
19864 // In this case we've hit string content so we know the regular
19865 // expression at least has something in it. We'll need to check if the
19866 // following token is the end (in which case we can return a plain
19867 // regular expression) or if it's not then it has interpolation.
19868 pm_string_t unescaped = parser->current_string;
19869 pm_token_t content = parser->current;
19870 bool ascii_only = parser->current_regular_expression_ascii_only;
19871 parser_lex(parser);
19872
19873 // If we hit an end, then we can create a regular expression
19874 // node without interpolation, which can be represented more
19875 // succinctly and more easily compiled.
19876 if (accept1(parser, PM_TOKEN_REGEXP_END)) {
19877 pm_regular_expression_node_t *node = (pm_regular_expression_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
19878
19879 // If we're not immediately followed by a =~, then we want
19880 // to parse all of the errors at this point. If it is
19881 // followed by a =~, then it will get parsed higher up while
19882 // parsing the named captures as well.
19883 if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) {
19884 parse_regular_expression_errors(parser, node);
19885 }
19886
19887 pm_node_flag_set(UP(node), parse_and_validate_regular_expression_encoding(parser, &unescaped, ascii_only, FL(node)));
19888 return UP(node);
19889 }
19890
19891 // If we get here, then we have interpolation so we'll need to create
19892 // a regular expression node with interpolation.
19893 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
19894
19895 pm_token_t opening = not_provided(parser);
19896 pm_token_t closing = not_provided(parser);
19897 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped));
19898
19899 if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
19900 // This is extremely strange, but the first string part of a
19901 // regular expression will always be tagged as binary if we
19902 // are in a US-ASCII file, no matter its contents.
19903 pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING);
19904 }
19905
19906 pm_interpolated_regular_expression_node_append(interpolated, part);
19907 } else {
19908 // If the first part of the body of the regular expression is not a
19909 // string content, then we have interpolation and we need to create an
19910 // interpolated regular expression node.
19911 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
19912 }
19913
19914 // Now that we're here and we have interpolation, we'll parse all of the
19915 // parts into the list.
19916 pm_node_t *part;
19917 while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) {
19918 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19919 pm_interpolated_regular_expression_node_append(interpolated, part);
19920 }
19921 }
19922
19923 pm_token_t closing = parser->current;
19924 if (match1(parser, PM_TOKEN_EOF)) {
19925 pm_parser_err_token(parser, &opening, PM_ERR_REGEXP_TERM);
19926 closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
19927 } else {
19928 expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM);
19929 }
19930
19931 pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing);
19932 return UP(interpolated);
19933 }
19934 case PM_TOKEN_BACKTICK:
19935 case PM_TOKEN_PERCENT_LOWER_X: {
19936 parser_lex(parser);
19937 pm_token_t opening = parser->previous;
19938
19939 // When we get here, we don't know if this string is going to have
19940 // interpolation or not, even though it is allowed. Still, we want to be
19941 // able to return a string node without interpolation if we can since
19942 // it'll be faster.
19943 if (match1(parser, PM_TOKEN_STRING_END)) {
19944 // If we get here, then we have an end immediately after a start. In
19945 // that case we'll create an empty content token and return an
19946 // uninterpolated string.
19947 pm_token_t content = (pm_token_t) {
19948 .type = PM_TOKEN_STRING_CONTENT,
19949 .start = parser->previous.end,
19950 .end = parser->previous.end
19951 };
19952
19953 parser_lex(parser);
19954 return UP(pm_xstring_node_create(parser, &opening, &content, &parser->previous));
19955 }
19956
19957 pm_interpolated_x_string_node_t *node;
19958
19959 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
19960 // In this case we've hit string content so we know the string
19961 // at least has something in it. We'll need to check if the
19962 // following token is the end (in which case we can return a
19963 // plain string) or if it's not then it has interpolation.
19964 pm_string_t unescaped = parser->current_string;
19965 pm_token_t content = parser->current;
19966 parser_lex(parser);
19967
19968 if (match1(parser, PM_TOKEN_STRING_END)) {
19969 pm_node_t *node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
19970 pm_node_flag_set(node, parse_unescaped_encoding(parser));
19971 parser_lex(parser);
19972 return node;
19973 }
19974
19975 // If we get here, then we have interpolation so we'll need to
19976 // create a string node with interpolation.
19977 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19978
19979 pm_token_t opening = not_provided(parser);
19980 pm_token_t closing = not_provided(parser);
19981
19982 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, &opening, &parser->previous, &closing, &unescaped));
19983 pm_node_flag_set(part, parse_unescaped_encoding(parser));
19984
19985 pm_interpolated_xstring_node_append(node, part);
19986 } else {
19987 // If the first part of the body of the string is not a string
19988 // content, then we have interpolation and we need to create an
19989 // interpolated string node.
19990 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19991 }
19992
19993 pm_node_t *part;
19994 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
19995 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19996 pm_interpolated_xstring_node_append(node, part);
19997 }
19998 }
19999
20000 pm_token_t closing = parser->current;
20001 if (match1(parser, PM_TOKEN_EOF)) {
20002 pm_parser_err_token(parser, &opening, PM_ERR_XSTRING_TERM);
20003 closing = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
20004 } else {
20005 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM);
20006 }
20007 pm_interpolated_xstring_node_closing_set(node, &closing);
20008
20009 return UP(node);
20010 }
20011 case PM_TOKEN_USTAR: {
20012 parser_lex(parser);
20013
20014 // * operators at the beginning of expressions are only valid in the
20015 // context of a multiple assignment. We enforce that here. We'll
20016 // still lex past it though and create a missing node place.
20017 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20018 pm_parser_err_prefix(parser, diag_id);
20019 return UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end));
20020 }
20021
20022 pm_token_t operator = parser->previous;
20023 pm_node_t *name = NULL;
20024
20025 if (token_begins_expression_p(parser->current.type)) {
20026 name = parse_expression(parser, PM_BINDING_POWER_INDEX, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
20027 }
20028
20029 pm_node_t *splat = UP(pm_splat_node_create(parser, &operator, name));
20030
20031 if (match1(parser, PM_TOKEN_COMMA)) {
20032 return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20033 } else {
20034 return parse_target_validate(parser, splat, true);
20035 }
20036 }
20037 case PM_TOKEN_BANG: {
20038 if (binding_power > PM_BINDING_POWER_UNARY) {
20039 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20040 }
20041
20042 parser_lex(parser);
20043
20044 pm_token_t operator = parser->previous;
20045 pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, binding_power < PM_BINDING_POWER_MATCH, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20046 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!");
20047
20048 pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
20049 return UP(node);
20050 }
20051 case PM_TOKEN_TILDE: {
20052 if (binding_power > PM_BINDING_POWER_UNARY) {
20053 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20054 }
20055 parser_lex(parser);
20056
20057 pm_token_t operator = parser->previous;
20058 pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20059 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~");
20060
20061 return UP(node);
20062 }
20063 case PM_TOKEN_UMINUS: {
20064 if (binding_power > PM_BINDING_POWER_UNARY) {
20065 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20066 }
20067 parser_lex(parser);
20068
20069 pm_token_t operator = parser->previous;
20070 pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20071 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "-@");
20072
20073 return UP(node);
20074 }
20075 case PM_TOKEN_UMINUS_NUM: {
20076 parser_lex(parser);
20077
20078 pm_token_t operator = parser->previous;
20079 pm_node_t *node = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20080
20081 if (accept1(parser, PM_TOKEN_STAR_STAR)) {
20082 pm_token_t exponent_operator = parser->previous;
20083 pm_node_t *exponent = parse_expression(parser, pm_binding_powers[exponent_operator.type].right, false, false, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
20084 node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0));
20085 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20086 } else {
20087 switch (PM_NODE_TYPE(node)) {
20088 case PM_INTEGER_NODE:
20089 case PM_FLOAT_NODE:
20090 case PM_RATIONAL_NODE:
20091 case PM_IMAGINARY_NODE:
20092 parse_negative_numeric(node);
20093 break;
20094 default:
20095 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20096 break;
20097 }
20098 }
20099
20100 return node;
20101 }
20102 case PM_TOKEN_MINUS_GREATER: {
20103 int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting;
20105
20106 size_t opening_newline_index = token_newline_index(parser);
20107 pm_accepts_block_stack_push(parser, true);
20108 parser_lex(parser);
20109
20110 pm_token_t operator = parser->previous;
20111 pm_parser_scope_push(parser, false);
20112
20113 pm_block_parameters_node_t *block_parameters;
20114
20115 switch (parser->current.type) {
20116 case PM_TOKEN_PARENTHESIS_LEFT: {
20117 pm_token_t opening = parser->current;
20118 parser_lex(parser);
20119
20120 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20121 block_parameters = pm_block_parameters_node_create(parser, NULL, &opening);
20122 } else {
20123 block_parameters = parse_block_parameters(parser, false, &opening, true, true, (uint16_t) (depth + 1));
20124 }
20125
20126 accept1(parser, PM_TOKEN_NEWLINE);
20127 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20128
20129 pm_block_parameters_node_closing_set(block_parameters, &parser->previous);
20130 break;
20131 }
20132 case PM_CASE_PARAMETER: {
20133 pm_accepts_block_stack_push(parser, false);
20134 pm_token_t opening = not_provided(parser);
20135 block_parameters = parse_block_parameters(parser, false, &opening, true, false, (uint16_t) (depth + 1));
20136 pm_accepts_block_stack_pop(parser);
20137 break;
20138 }
20139 default: {
20140 block_parameters = NULL;
20141 break;
20142 }
20143 }
20144
20145 pm_token_t opening;
20146 pm_node_t *body = NULL;
20147 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
20148
20149 if (accept1(parser, PM_TOKEN_LAMBDA_BEGIN)) {
20150 opening = parser->previous;
20151
20152 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
20153 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1)));
20154 }
20155
20156 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
20157 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
20158 } else {
20159 expect1(parser, PM_TOKEN_KEYWORD_DO, PM_ERR_LAMBDA_OPEN);
20160 opening = parser->previous;
20161
20162 if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
20163 pm_accepts_block_stack_push(parser, true);
20164 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1)));
20165 pm_accepts_block_stack_pop(parser);
20166 }
20167
20168 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
20169 assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE));
20170 body = UP(parse_rescues_implicit_begin(parser, opening_newline_index, &operator, opening.start, (pm_statements_node_t *) body, PM_RESCUES_LAMBDA, (uint16_t) (depth + 1)));
20171 } else {
20172 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
20173 }
20174
20175 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator);
20176 }
20177
20178 pm_constant_id_list_t locals;
20179 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
20180 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &operator, &parser->previous);
20181
20182 pm_parser_scope_pop(parser);
20183 pm_accepts_block_stack_pop(parser);
20184
20185 return UP(pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body));
20186 }
20187 case PM_TOKEN_UPLUS: {
20188 if (binding_power > PM_BINDING_POWER_UNARY) {
20189 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20190 }
20191 parser_lex(parser);
20192
20193 pm_token_t operator = parser->previous;
20194 pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, false, false, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20195 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "+@");
20196
20197 return UP(node);
20198 }
20199 case PM_TOKEN_STRING_BEGIN:
20200 return parse_strings(parser, NULL, accepts_label, (uint16_t) (depth + 1));
20201 case PM_TOKEN_SYMBOL_BEGIN: {
20202 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
20203 parser_lex(parser);
20204
20205 return parse_symbol(parser, &lex_mode, PM_LEX_STATE_END, (uint16_t) (depth + 1));
20206 }
20207 default: {
20208 pm_context_t recoverable = context_recoverable(parser, &parser->current);
20209
20210 if (recoverable != PM_CONTEXT_NONE) {
20211 parser->recovering = true;
20212
20213 // If the given error is not the generic one, then we'll add it
20214 // here because it will provide more context in addition to the
20215 // recoverable error that we will also add.
20216 if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) {
20217 pm_parser_err_prefix(parser, diag_id);
20218 }
20219
20220 // If we get here, then we are assuming this token is closing a
20221 // parent context, so we'll indicate that to the user so that
20222 // they know how we behaved.
20223 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_type_human(parser->current.type), context_human(recoverable));
20224 } else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) {
20225 // We're going to make a special case here, because "cannot
20226 // parse expression" is pretty generic, and we know here that we
20227 // have an unexpected token.
20228 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_type_human(parser->current.type));
20229 } else {
20230 pm_parser_err_prefix(parser, diag_id);
20231 }
20232
20233 return UP(pm_missing_node_create(parser, parser->previous.start, parser->previous.end));
20234 }
20235 }
20236}
20237
20247static pm_node_t *
20248parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id, uint16_t depth) {
20249 pm_node_t *value = parse_value_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MATCH, false, diag_id, (uint16_t) (depth + 1));
20250
20251 // Contradicting binding powers, the right-hand-side value of the assignment
20252 // allows the `rescue` modifier.
20253 if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
20254 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
20255
20256 pm_token_t rescue = parser->current;
20257 parser_lex(parser);
20258
20259 pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MATCH + 1, false, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
20260 context_pop(parser);
20261
20262 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
20263 }
20264
20265 return value;
20266}
20267
20272static void
20273parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) {
20274 switch (PM_NODE_TYPE(node)) {
20275 case PM_BEGIN_NODE: {
20276 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
20277 if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements);
20278 break;
20279 }
20280 case PM_LOCAL_VARIABLE_WRITE_NODE: {
20281 const pm_local_variable_write_node_t *cast = (const pm_local_variable_write_node_t *) node;
20282 pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
20283 break;
20284 }
20285 case PM_PARENTHESES_NODE: {
20286 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
20287 if (cast->body != NULL) parse_assignment_value_local(parser, cast->body);
20288 break;
20289 }
20290 case PM_STATEMENTS_NODE: {
20291 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
20292 const pm_node_t *statement;
20293
20294 PM_NODE_LIST_FOREACH(&cast->body, index, statement) {
20295 parse_assignment_value_local(parser, statement);
20296 }
20297 break;
20298 }
20299 default:
20300 break;
20301 }
20302}
20303
20316static pm_node_t *
20317parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, pm_diagnostic_id_t diag_id, uint16_t depth) {
20318 bool permitted = true;
20319 if (previous_binding_power != PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_USTAR)) permitted = false;
20320
20321 pm_node_t *value = parse_starred_expression(parser, binding_power, previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? accepts_command_call : previous_binding_power < PM_BINDING_POWER_MODIFIER, diag_id, (uint16_t) (depth + 1));
20322 if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
20323
20324 parse_assignment_value_local(parser, value);
20325 bool single_value = true;
20326
20327 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) {
20328 single_value = false;
20329
20330 pm_token_t opening = not_provided(parser);
20331 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20332
20333 pm_array_node_elements_append(array, value);
20334 value = UP(array);
20335
20336 while (accept1(parser, PM_TOKEN_COMMA)) {
20337 pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1));
20338
20339 pm_array_node_elements_append(array, element);
20340 if (PM_NODE_TYPE_P(element, PM_MISSING_NODE)) break;
20341
20342 parse_assignment_value_local(parser, element);
20343 }
20344 }
20345
20346 // Contradicting binding powers, the right-hand-side value of the assignment
20347 // allows the `rescue` modifier.
20348 if ((single_value || (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1))) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
20349 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
20350
20351 pm_token_t rescue = parser->current;
20352 parser_lex(parser);
20353
20354 bool accepts_command_call_inner = false;
20355
20356 // RHS can accept command call iff the value is a call with arguments
20357 // but without parenthesis.
20358 if (PM_NODE_TYPE_P(value, PM_CALL_NODE)) {
20359 pm_call_node_t *call_node = (pm_call_node_t *) value;
20360 if ((call_node->arguments != NULL) && (call_node->opening_loc.start == NULL)) {
20361 accepts_command_call_inner = true;
20362 }
20363 }
20364
20365 pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MATCH + 1, accepts_command_call_inner, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
20366 context_pop(parser);
20367
20368 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
20369 }
20370
20371 return value;
20372}
20373
20381static void
20382parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) {
20383 if (call_node->arguments != NULL) {
20384 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS);
20385 pm_node_unreference(parser, UP(call_node->arguments));
20386 pm_node_destroy(parser, UP(call_node->arguments));
20387 call_node->arguments = NULL;
20388 }
20389
20390 if (call_node->block != NULL) {
20391 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK);
20392 pm_node_unreference(parser, UP(call_node->block));
20393 pm_node_destroy(parser, UP(call_node->block));
20394 call_node->block = NULL;
20395 }
20396}
20397
20402typedef struct {
20405
20407 pm_call_node_t *call;
20408
20410 pm_match_write_node_t *match;
20411
20414
20422
20423static inline const uint8_t *
20424pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
20425 cursor++;
20426
20427 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
20428 uint8_t value = escape_hexadecimal_digit(*cursor);
20429 cursor++;
20430
20431 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
20432 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(*cursor));
20433 cursor++;
20434 }
20435
20436 pm_buffer_append_byte(unescaped, value);
20437 } else {
20438 pm_buffer_append_string(unescaped, "\\x", 2);
20439 }
20440
20441 return cursor;
20442}
20443
20444static inline const uint8_t *
20445pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
20446 uint8_t value = (uint8_t) (*cursor - '0');
20447 cursor++;
20448
20449 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
20450 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
20451 cursor++;
20452
20453 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
20454 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
20455 cursor++;
20456 }
20457 }
20458
20459 pm_buffer_append_byte(unescaped, value);
20460 return cursor;
20461}
20462
20463static inline const uint8_t *
20464pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end, const pm_location_t *error_location) {
20465 const uint8_t *start = cursor - 1;
20466 cursor++;
20467
20468 if (cursor >= end) {
20469 pm_buffer_append_string(unescaped, "\\u", 2);
20470 return cursor;
20471 }
20472
20473 if (*cursor != '{') {
20474 size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4));
20475 uint32_t value = escape_unicode(parser, cursor, length, error_location);
20476
20477 if (!pm_buffer_append_unicode_codepoint(unescaped, value)) {
20478 pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start));
20479 }
20480
20481 return cursor + length;
20482 }
20483
20484 cursor++;
20485 for (;;) {
20486 while (cursor < end && *cursor == ' ') cursor++;
20487
20488 if (cursor >= end) break;
20489 if (*cursor == '}') {
20490 cursor++;
20491 break;
20492 }
20493
20494 size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor);
20495 if (length == 0) {
20496 break;
20497 }
20498 uint32_t value = escape_unicode(parser, cursor, length, error_location);
20499
20500 (void) pm_buffer_append_unicode_codepoint(unescaped, value);
20501 cursor += length;
20502 }
20503
20504 return cursor;
20505}
20506
20507static void
20508pm_named_capture_escape(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *source, const size_t length, const uint8_t *cursor, const pm_location_t *error_location) {
20509 const uint8_t *end = source + length;
20510 pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source));
20511
20512 for (;;) {
20513 if (++cursor >= end) {
20514 pm_buffer_append_byte(unescaped, '\\');
20515 return;
20516 }
20517
20518 switch (*cursor) {
20519 case 'x':
20520 cursor = pm_named_capture_escape_hex(unescaped, cursor, end);
20521 break;
20522 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
20523 cursor = pm_named_capture_escape_octal(unescaped, cursor, end);
20524 break;
20525 case 'u':
20526 cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location);
20527 break;
20528 default:
20529 pm_buffer_append_byte(unescaped, '\\');
20530 break;
20531 }
20532
20533 const uint8_t *next_cursor = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, parser->encoding);
20534 if (next_cursor == NULL) break;
20535
20536 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (next_cursor - cursor));
20537 cursor = next_cursor;
20538 }
20539
20540 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (end - cursor));
20541}
20542
20547static void
20548parse_regular_expression_named_capture(const pm_string_t *capture, void *data) {
20550
20551 pm_parser_t *parser = callback_data->parser;
20552 pm_call_node_t *call = callback_data->call;
20553 pm_constant_id_list_t *names = &callback_data->names;
20554
20555 const uint8_t *source = pm_string_source(capture);
20556 size_t length = pm_string_length(capture);
20557 pm_buffer_t unescaped = { 0 };
20558
20559 // First, we need to handle escapes within the name of the capture group.
20560 // This is because regular expressions have three different representations
20561 // in prism. The first is the plain source code. The second is the
20562 // representation that will be sent to the regular expression engine, which
20563 // is the value of the "unescaped" field. This is poorly named, because it
20564 // actually still contains escapes, just a subset of them that the regular
20565 // expression engine knows how to handle. The third representation is fully
20566 // unescaped, which is what we need.
20567 const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding);
20568 if (PRISM_UNLIKELY(cursor != NULL)) {
20569 pm_named_capture_escape(parser, &unescaped, source, length, cursor, callback_data->shared ? NULL : &call->receiver->location);
20570 source = (const uint8_t *) pm_buffer_value(&unescaped);
20571 length = pm_buffer_length(&unescaped);
20572 }
20573
20574 pm_location_t location;
20575 pm_constant_id_t name;
20576
20577 // If the name of the capture group isn't a valid identifier, we do
20578 // not add it to the local table.
20579 if (!pm_slice_is_valid_local(parser, source, source + length)) {
20580 pm_buffer_free(&unescaped);
20581 return;
20582 }
20583
20584 if (callback_data->shared) {
20585 // If the unescaped string is a slice of the source, then we can
20586 // copy the names directly. The pointers will line up.
20587 location = (pm_location_t) { .start = source, .end = source + length };
20588 name = pm_parser_constant_id_location(parser, location.start, location.end);
20589 } else {
20590 // Otherwise, the name is a slice of the malloc-ed owned string,
20591 // in which case we need to copy it out into a new string.
20592 location = (pm_location_t) { .start = call->receiver->location.start, .end = call->receiver->location.end };
20593
20594 void *memory = xmalloc(length);
20595 if (memory == NULL) abort();
20596
20597 memcpy(memory, source, length);
20598 name = pm_parser_constant_id_owned(parser, (uint8_t *) memory, length);
20599 }
20600
20601 // Add this name to the list of constants if it is valid, not duplicated,
20602 // and not a keyword.
20603 if (name != 0 && !pm_constant_id_list_includes(names, name)) {
20604 pm_constant_id_list_append(names, name);
20605
20606 int depth;
20607 if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
20608 // If the local is not already a local but it is a keyword, then we
20609 // do not want to add a capture for this.
20610 if (pm_local_is_keyword((const char *) source, length)) {
20611 pm_buffer_free(&unescaped);
20612 return;
20613 }
20614
20615 // If the identifier is not already a local, then we will add it to
20616 // the local table.
20617 pm_parser_local_add(parser, name, location.start, location.end, 0);
20618 }
20619
20620 // Here we lazily create the MatchWriteNode since we know we're
20621 // about to add a target.
20622 if (callback_data->match == NULL) {
20623 callback_data->match = pm_match_write_node_create(parser, call);
20624 }
20625
20626 // Next, create the local variable target and add it to the list of
20627 // targets for the match.
20628 pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &location, name, depth == -1 ? 0 : (uint32_t) depth));
20629 pm_node_list_append(&callback_data->match->targets, target);
20630 }
20631
20632 pm_buffer_free(&unescaped);
20633}
20634
20639static pm_node_t *
20640parse_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call, bool extended_mode) {
20642 .parser = parser,
20643 .call = call,
20644 .names = { 0 },
20645 .shared = content->type == PM_STRING_SHARED
20646 };
20647
20649 .parser = parser,
20650 .start = call->receiver->location.start,
20651 .end = call->receiver->location.end,
20652 .shared = content->type == PM_STRING_SHARED
20653 };
20654
20655 pm_regexp_parse(parser, pm_string_source(content), pm_string_length(content), extended_mode, parse_regular_expression_named_capture, &callback_data, parse_regular_expression_error, &error_data);
20656 pm_constant_id_list_free(&callback_data.names);
20657
20658 if (callback_data.match != NULL) {
20659 return UP(callback_data.match);
20660 } else {
20661 return UP(call);
20662 }
20663}
20664
20665static inline pm_node_t *
20666parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, bool accepts_command_call, uint16_t depth) {
20667 pm_token_t token = parser->current;
20668
20669 switch (token.type) {
20670 case PM_TOKEN_EQUAL: {
20671 switch (PM_NODE_TYPE(node)) {
20672 case PM_CALL_NODE: {
20673 // If we have no arguments to the call node and we need this
20674 // to be a target then this is either a method call or a
20675 // local variable write. This _must_ happen before the value
20676 // is parsed because it could be referenced in the value.
20677 pm_call_node_t *call_node = (pm_call_node_t *) node;
20678 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
20679 pm_parser_local_add_location(parser, call_node->message_loc.start, call_node->message_loc.end, 0);
20680 }
20681 }
20683 case PM_CASE_WRITABLE: {
20684 // When we have `it = value`, we need to add `it` as a local
20685 // variable before parsing the value, in case the value
20686 // references the variable.
20687 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
20688 pm_parser_local_add_location(parser, node->location.start, node->location.end, 0);
20689 }
20690
20691 parser_lex(parser);
20692 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) ? PM_BINDING_POWER_MULTI_ASSIGNMENT + 1 : binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
20693
20694 if (PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) && previous_binding_power != PM_BINDING_POWER_STATEMENT) {
20695 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_MULTI_WRITE);
20696 }
20697
20698 return parse_write(parser, node, &token, value);
20699 }
20700 case PM_SPLAT_NODE: {
20701 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
20702 pm_multi_target_node_targets_append(parser, multi_target, node);
20703
20704 parser_lex(parser);
20705 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_BINDING_POWER_MULTI_ASSIGNMENT + 1, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
20706 return parse_write(parser, UP(multi_target), &token, value);
20707 }
20708 case PM_SOURCE_ENCODING_NODE:
20709 case PM_FALSE_NODE:
20710 case PM_SOURCE_FILE_NODE:
20711 case PM_SOURCE_LINE_NODE:
20712 case PM_NIL_NODE:
20713 case PM_SELF_NODE:
20714 case PM_TRUE_NODE: {
20715 // In these special cases, we have specific error messages
20716 // and we will replace them with local variable writes.
20717 parser_lex(parser);
20718 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
20719 return parse_unwriteable_write(parser, node, &token, value);
20720 }
20721 default:
20722 // In this case we have an = sign, but we don't know what
20723 // it's for. We need to treat it as an error. We'll mark it
20724 // as an error and skip past it.
20725 parser_lex(parser);
20726 pm_parser_err_token(parser, &token, PM_ERR_EXPRESSION_NOT_WRITABLE);
20727 return node;
20728 }
20729 }
20730 case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL: {
20731 switch (PM_NODE_TYPE(node)) {
20732 case PM_BACK_REFERENCE_READ_NODE:
20733 case PM_NUMBERED_REFERENCE_READ_NODE:
20734 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
20736 case PM_GLOBAL_VARIABLE_READ_NODE: {
20737 parser_lex(parser);
20738
20739 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20740 pm_node_t *result = UP(pm_global_variable_and_write_node_create(parser, node, &token, value));
20741
20742 pm_node_destroy(parser, node);
20743 return result;
20744 }
20745 case PM_CLASS_VARIABLE_READ_NODE: {
20746 parser_lex(parser);
20747
20748 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20749 pm_node_t *result = UP(pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
20750
20751 pm_node_destroy(parser, node);
20752 return result;
20753 }
20754 case PM_CONSTANT_PATH_NODE: {
20755 parser_lex(parser);
20756
20757 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20758 pm_node_t *write = UP(pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
20759
20760 return parse_shareable_constant_write(parser, write);
20761 }
20762 case PM_CONSTANT_READ_NODE: {
20763 parser_lex(parser);
20764
20765 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20766 pm_node_t *write = UP(pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
20767
20768 pm_node_destroy(parser, node);
20769 return parse_shareable_constant_write(parser, write);
20770 }
20771 case PM_INSTANCE_VARIABLE_READ_NODE: {
20772 parser_lex(parser);
20773
20774 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20775 pm_node_t *result = UP(pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
20776
20777 pm_node_destroy(parser, node);
20778 return result;
20779 }
20780 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
20781 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
20782 parser_lex(parser);
20783
20784 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20785 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0));
20786
20787 pm_node_unreference(parser, node);
20788 pm_node_destroy(parser, node);
20789 return result;
20790 }
20791 case PM_LOCAL_VARIABLE_READ_NODE: {
20792 if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) {
20793 PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start);
20794 pm_node_unreference(parser, node);
20795 }
20796
20797 pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node;
20798 parser_lex(parser);
20799
20800 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20801 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, cast->name, cast->depth));
20802
20803 pm_node_destroy(parser, node);
20804 return result;
20805 }
20806 case PM_CALL_NODE: {
20807 pm_call_node_t *cast = (pm_call_node_t *) node;
20808
20809 // If we have a vcall (a method with no arguments and no
20810 // receiver that could have been a local variable) then we
20811 // will transform it into a local variable write.
20812 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
20813 pm_location_t *message_loc = &cast->message_loc;
20814 pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
20815
20816 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1);
20817 parser_lex(parser);
20818
20819 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20820 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
20821
20822 pm_node_destroy(parser, UP(cast));
20823 return result;
20824 }
20825
20826 // Move past the token here so that we have already added
20827 // the local variable by this point.
20828 parser_lex(parser);
20829
20830 // If there is no call operator and the message is "[]" then
20831 // this is an aref expression, and we can transform it into
20832 // an aset expression.
20833 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
20834 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20835 return UP(pm_index_and_write_node_create(parser, cast, &token, value));
20836 }
20837
20838 // If this node cannot be writable, then we have an error.
20839 if (pm_call_node_writable_p(parser, cast)) {
20840 parse_write_name(parser, &cast->name);
20841 } else {
20842 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
20843 }
20844
20845 parse_call_operator_write(parser, cast, &token);
20846 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
20847 return UP(pm_call_and_write_node_create(parser, cast, &token, value));
20848 }
20849 case PM_MULTI_WRITE_NODE: {
20850 parser_lex(parser);
20851 pm_parser_err_token(parser, &token, PM_ERR_AMPAMPEQ_MULTI_ASSIGN);
20852 return node;
20853 }
20854 default:
20855 parser_lex(parser);
20856
20857 // In this case we have an &&= sign, but we don't know what it's for.
20858 // We need to treat it as an error. For now, we'll mark it as an error
20859 // and just skip right past it.
20860 pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
20861 return node;
20862 }
20863 }
20864 case PM_TOKEN_PIPE_PIPE_EQUAL: {
20865 switch (PM_NODE_TYPE(node)) {
20866 case PM_BACK_REFERENCE_READ_NODE:
20867 case PM_NUMBERED_REFERENCE_READ_NODE:
20868 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
20870 case PM_GLOBAL_VARIABLE_READ_NODE: {
20871 parser_lex(parser);
20872
20873 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20874 pm_node_t *result = UP(pm_global_variable_or_write_node_create(parser, node, &token, value));
20875
20876 pm_node_destroy(parser, node);
20877 return result;
20878 }
20879 case PM_CLASS_VARIABLE_READ_NODE: {
20880 parser_lex(parser);
20881
20882 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20883 pm_node_t *result = UP(pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
20884
20885 pm_node_destroy(parser, node);
20886 return result;
20887 }
20888 case PM_CONSTANT_PATH_NODE: {
20889 parser_lex(parser);
20890
20891 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20892 pm_node_t *write = UP(pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
20893
20894 return parse_shareable_constant_write(parser, write);
20895 }
20896 case PM_CONSTANT_READ_NODE: {
20897 parser_lex(parser);
20898
20899 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20900 pm_node_t *write = UP(pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
20901
20902 pm_node_destroy(parser, node);
20903 return parse_shareable_constant_write(parser, write);
20904 }
20905 case PM_INSTANCE_VARIABLE_READ_NODE: {
20906 parser_lex(parser);
20907
20908 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20909 pm_node_t *result = UP(pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
20910
20911 pm_node_destroy(parser, node);
20912 return result;
20913 }
20914 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
20915 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
20916 parser_lex(parser);
20917
20918 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20919 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0));
20920
20921 pm_node_unreference(parser, node);
20922 pm_node_destroy(parser, node);
20923 return result;
20924 }
20925 case PM_LOCAL_VARIABLE_READ_NODE: {
20926 if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) {
20927 PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start);
20928 pm_node_unreference(parser, node);
20929 }
20930
20931 pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node;
20932 parser_lex(parser);
20933
20934 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20935 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, cast->name, cast->depth));
20936
20937 pm_node_destroy(parser, node);
20938 return result;
20939 }
20940 case PM_CALL_NODE: {
20941 pm_call_node_t *cast = (pm_call_node_t *) node;
20942
20943 // If we have a vcall (a method with no arguments and no
20944 // receiver that could have been a local variable) then we
20945 // will transform it into a local variable write.
20946 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
20947 pm_location_t *message_loc = &cast->message_loc;
20948 pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
20949
20950 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1);
20951 parser_lex(parser);
20952
20953 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20954 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
20955
20956 pm_node_destroy(parser, UP(cast));
20957 return result;
20958 }
20959
20960 // Move past the token here so that we have already added
20961 // the local variable by this point.
20962 parser_lex(parser);
20963
20964 // If there is no call operator and the message is "[]" then
20965 // this is an aref expression, and we can transform it into
20966 // an aset expression.
20967 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
20968 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20969 return UP(pm_index_or_write_node_create(parser, cast, &token, value));
20970 }
20971
20972 // If this node cannot be writable, then we have an error.
20973 if (pm_call_node_writable_p(parser, cast)) {
20974 parse_write_name(parser, &cast->name);
20975 } else {
20976 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
20977 }
20978
20979 parse_call_operator_write(parser, cast, &token);
20980 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
20981 return UP(pm_call_or_write_node_create(parser, cast, &token, value));
20982 }
20983 case PM_MULTI_WRITE_NODE: {
20984 parser_lex(parser);
20985 pm_parser_err_token(parser, &token, PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN);
20986 return node;
20987 }
20988 default:
20989 parser_lex(parser);
20990
20991 // In this case we have an ||= sign, but we don't know what it's for.
20992 // We need to treat it as an error. For now, we'll mark it as an error
20993 // and just skip right past it.
20994 pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
20995 return node;
20996 }
20997 }
20998 case PM_TOKEN_AMPERSAND_EQUAL:
20999 case PM_TOKEN_CARET_EQUAL:
21000 case PM_TOKEN_GREATER_GREATER_EQUAL:
21001 case PM_TOKEN_LESS_LESS_EQUAL:
21002 case PM_TOKEN_MINUS_EQUAL:
21003 case PM_TOKEN_PERCENT_EQUAL:
21004 case PM_TOKEN_PIPE_EQUAL:
21005 case PM_TOKEN_PLUS_EQUAL:
21006 case PM_TOKEN_SLASH_EQUAL:
21007 case PM_TOKEN_STAR_EQUAL:
21008 case PM_TOKEN_STAR_STAR_EQUAL: {
21009 switch (PM_NODE_TYPE(node)) {
21010 case PM_BACK_REFERENCE_READ_NODE:
21011 case PM_NUMBERED_REFERENCE_READ_NODE:
21012 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21014 case PM_GLOBAL_VARIABLE_READ_NODE: {
21015 parser_lex(parser);
21016
21017 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21018 pm_node_t *result = UP(pm_global_variable_operator_write_node_create(parser, node, &token, value));
21019
21020 pm_node_destroy(parser, node);
21021 return result;
21022 }
21023 case PM_CLASS_VARIABLE_READ_NODE: {
21024 parser_lex(parser);
21025
21026 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21027 pm_node_t *result = UP(pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21028
21029 pm_node_destroy(parser, node);
21030 return result;
21031 }
21032 case PM_CONSTANT_PATH_NODE: {
21033 parser_lex(parser);
21034
21035 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21036 pm_node_t *write = UP(pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21037
21038 return parse_shareable_constant_write(parser, write);
21039 }
21040 case PM_CONSTANT_READ_NODE: {
21041 parser_lex(parser);
21042
21043 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21044 pm_node_t *write = UP(pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21045
21046 pm_node_destroy(parser, node);
21047 return parse_shareable_constant_write(parser, write);
21048 }
21049 case PM_INSTANCE_VARIABLE_READ_NODE: {
21050 parser_lex(parser);
21051
21052 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21053 pm_node_t *result = UP(pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21054
21055 pm_node_destroy(parser, node);
21056 return result;
21057 }
21058 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21059 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21060 parser_lex(parser);
21061
21062 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21063 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0));
21064
21065 pm_node_unreference(parser, node);
21066 pm_node_destroy(parser, node);
21067 return result;
21068 }
21069 case PM_LOCAL_VARIABLE_READ_NODE: {
21070 if (pm_token_is_numbered_parameter(node->location.start, node->location.end)) {
21071 PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.end, PM_ERR_PARAMETER_NUMBERED_RESERVED, node->location.start);
21072 pm_node_unreference(parser, node);
21073 }
21074
21075 pm_local_variable_read_node_t *cast = (pm_local_variable_read_node_t *) node;
21076 parser_lex(parser);
21077
21078 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21079 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21080
21081 pm_node_destroy(parser, node);
21082 return result;
21083 }
21084 case PM_CALL_NODE: {
21085 parser_lex(parser);
21086 pm_call_node_t *cast = (pm_call_node_t *) node;
21087
21088 // If we have a vcall (a method with no arguments and no
21089 // receiver that could have been a local variable) then we
21090 // will transform it into a local variable write.
21091 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21092 pm_location_t *message_loc = &cast->message_loc;
21093 pm_refute_numbered_parameter(parser, message_loc->start, message_loc->end);
21094
21095 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, message_loc->start, message_loc->end, 1);
21096 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21097 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21098
21099 pm_node_destroy(parser, UP(cast));
21100 return result;
21101 }
21102
21103 // If there is no call operator and the message is "[]" then
21104 // this is an aref expression, and we can transform it into
21105 // an aset expression.
21106 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
21107 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21108 return UP(pm_index_operator_write_node_create(parser, cast, &token, value));
21109 }
21110
21111 // If this node cannot be writable, then we have an error.
21112 if (pm_call_node_writable_p(parser, cast)) {
21113 parse_write_name(parser, &cast->name);
21114 } else {
21115 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
21116 }
21117
21118 parse_call_operator_write(parser, cast, &token);
21119 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21120 return UP(pm_call_operator_write_node_create(parser, cast, &token, value));
21121 }
21122 case PM_MULTI_WRITE_NODE: {
21123 parser_lex(parser);
21124 pm_parser_err_token(parser, &token, PM_ERR_OPERATOR_MULTI_ASSIGN);
21125 return node;
21126 }
21127 default:
21128 parser_lex(parser);
21129
21130 // In this case we have an operator but we don't know what it's for.
21131 // We need to treat it as an error. For now, we'll mark it as an error
21132 // and just skip right past it.
21133 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->previous, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_type_human(parser->current.type));
21134 return node;
21135 }
21136 }
21137 case PM_TOKEN_AMPERSAND_AMPERSAND:
21138 case PM_TOKEN_KEYWORD_AND: {
21139 parser_lex(parser);
21140
21141 pm_node_t *right = parse_expression(parser, binding_power, parser->previous.type == PM_TOKEN_KEYWORD_AND, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21142 return UP(pm_and_node_create(parser, node, &token, right));
21143 }
21144 case PM_TOKEN_KEYWORD_OR:
21145 case PM_TOKEN_PIPE_PIPE: {
21146 parser_lex(parser);
21147
21148 pm_node_t *right = parse_expression(parser, binding_power, parser->previous.type == PM_TOKEN_KEYWORD_OR, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21149 return UP(pm_or_node_create(parser, node, &token, right));
21150 }
21151 case PM_TOKEN_EQUAL_TILDE: {
21152 // Note that we _must_ parse the value before adding the local
21153 // variables in order to properly mirror the behavior of Ruby. For
21154 // example,
21155 //
21156 // /(?<foo>bar)/ =~ foo
21157 //
21158 // In this case, `foo` should be a method call and not a local yet.
21159 parser_lex(parser);
21160 pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21161
21162 // By default, we're going to create a call node and then return it.
21163 pm_call_node_t *call = pm_call_node_binary_create(parser, node, &token, argument, 0);
21164 pm_node_t *result = UP(call);
21165
21166 // If the receiver of this =~ is a regular expression node, then we
21167 // need to introduce local variables for it based on its named
21168 // capture groups.
21169 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE)) {
21170 // It's possible to have an interpolated regular expression node
21171 // that only contains strings. This is because it can be split
21172 // up by a heredoc. In this case we need to concat the unescaped
21173 // strings together and then parse them as a regular expression.
21174 pm_node_list_t *parts = &((pm_interpolated_regular_expression_node_t *) node)->parts;
21175
21176 bool interpolated = false;
21177 size_t total_length = 0;
21178
21179 pm_node_t *part;
21180 PM_NODE_LIST_FOREACH(parts, index, part) {
21181 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
21182 total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped);
21183 } else {
21184 interpolated = true;
21185 break;
21186 }
21187 }
21188
21189 if (!interpolated && total_length > 0) {
21190 void *memory = xmalloc(total_length);
21191 if (!memory) abort();
21192
21193 uint8_t *cursor = memory;
21194 PM_NODE_LIST_FOREACH(parts, index, part) {
21195 pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
21196 size_t length = pm_string_length(unescaped);
21197
21198 memcpy(cursor, pm_string_source(unescaped), length);
21199 cursor += length;
21200 }
21201
21202 pm_string_t owned;
21203 pm_string_owned_init(&owned, (uint8_t *) memory, total_length);
21204
21205 result = parse_regular_expression_named_captures(parser, &owned, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
21206 pm_string_free(&owned);
21207 }
21208 } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
21209 // If we have a regular expression node, then we can just parse
21210 // the named captures directly off the unescaped string.
21211 const pm_string_t *content = &((pm_regular_expression_node_t *) node)->unescaped;
21212 result = parse_regular_expression_named_captures(parser, content, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
21213 }
21214
21215 return result;
21216 }
21217 case PM_TOKEN_UAMPERSAND:
21218 case PM_TOKEN_USTAR:
21219 case PM_TOKEN_USTAR_STAR:
21220 // The only times this will occur are when we are in an error state,
21221 // but we'll put them in here so that errors can propagate.
21222 case PM_TOKEN_BANG_EQUAL:
21223 case PM_TOKEN_BANG_TILDE:
21224 case PM_TOKEN_EQUAL_EQUAL:
21225 case PM_TOKEN_EQUAL_EQUAL_EQUAL:
21226 case PM_TOKEN_LESS_EQUAL_GREATER:
21227 case PM_TOKEN_CARET:
21228 case PM_TOKEN_PIPE:
21229 case PM_TOKEN_AMPERSAND:
21230 case PM_TOKEN_GREATER_GREATER:
21231 case PM_TOKEN_LESS_LESS:
21232 case PM_TOKEN_MINUS:
21233 case PM_TOKEN_PLUS:
21234 case PM_TOKEN_PERCENT:
21235 case PM_TOKEN_SLASH:
21236 case PM_TOKEN_STAR:
21237 case PM_TOKEN_STAR_STAR: {
21238 parser_lex(parser);
21239 pm_token_t operator = parser->previous;
21240 switch (PM_NODE_TYPE(node)) {
21241 case PM_RESCUE_MODIFIER_NODE: {
21242 pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node;
21243 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
21244 PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type));
21245 }
21246 break;
21247 }
21248 case PM_AND_NODE: {
21249 pm_and_node_t *cast = (pm_and_node_t *) node;
21250 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
21251 PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type));
21252 }
21253 break;
21254 }
21255 case PM_OR_NODE: {
21256 pm_or_node_t *cast = (pm_or_node_t *) node;
21257 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
21258 PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type));
21259 }
21260 break;
21261 }
21262 default:
21263 break;
21264 }
21265
21266 pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21267 return UP(pm_call_node_binary_create(parser, node, &token, argument, 0));
21268 }
21269 case PM_TOKEN_GREATER:
21270 case PM_TOKEN_GREATER_EQUAL:
21271 case PM_TOKEN_LESS:
21272 case PM_TOKEN_LESS_EQUAL: {
21273 if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) {
21274 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON);
21275 }
21276
21277 parser_lex(parser);
21278 pm_node_t *argument = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21279 return UP(pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON));
21280 }
21281 case PM_TOKEN_AMPERSAND_DOT:
21282 case PM_TOKEN_DOT: {
21283 parser_lex(parser);
21284 pm_token_t operator = parser->previous;
21285 pm_arguments_t arguments = { 0 };
21286
21287 // This if statement handles the foo.() syntax.
21288 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
21289 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
21290 return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments));
21291 }
21292
21293 switch (PM_NODE_TYPE(node)) {
21294 case PM_RESCUE_MODIFIER_NODE: {
21295 pm_rescue_modifier_node_t *cast = (pm_rescue_modifier_node_t *) node;
21296 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
21297 PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type));
21298 }
21299 break;
21300 }
21301 case PM_AND_NODE: {
21302 pm_and_node_t *cast = (pm_and_node_t *) node;
21303 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
21304 PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type));
21305 }
21306 break;
21307 }
21308 case PM_OR_NODE: {
21309 pm_or_node_t *cast = (pm_or_node_t *) node;
21310 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
21311 PM_PARSER_ERR_TOKEN_FORMAT(parser, operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(operator.type));
21312 }
21313 break;
21314 }
21315 default:
21316 break;
21317 }
21318
21319 pm_token_t message;
21320
21321 switch (parser->current.type) {
21322 case PM_CASE_OPERATOR:
21323 case PM_CASE_KEYWORD:
21324 case PM_TOKEN_CONSTANT:
21325 case PM_TOKEN_IDENTIFIER:
21326 case PM_TOKEN_METHOD_NAME: {
21327 parser_lex(parser);
21328 message = parser->previous;
21329 break;
21330 }
21331 default: {
21332 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_type_human(parser->current.type));
21333 message = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
21334 }
21335 }
21336
21337 parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1));
21338 pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments);
21339
21340 if (
21341 (previous_binding_power == PM_BINDING_POWER_STATEMENT) &&
21342 arguments.arguments == NULL &&
21343 arguments.opening_loc.start == NULL &&
21344 match1(parser, PM_TOKEN_COMMA)
21345 ) {
21346 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21347 } else {
21348 return UP(call);
21349 }
21350 }
21351 case PM_TOKEN_DOT_DOT:
21352 case PM_TOKEN_DOT_DOT_DOT: {
21353 parser_lex(parser);
21354
21355 pm_node_t *right = NULL;
21356 if (token_begins_expression_p(parser->current.type)) {
21357 right = parse_expression(parser, binding_power, false, false, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21358 }
21359
21360 return UP(pm_range_node_create(parser, node, &token, right));
21361 }
21362 case PM_TOKEN_KEYWORD_IF_MODIFIER: {
21363 pm_token_t keyword = parser->current;
21364 parser_lex(parser);
21365
21366 pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1));
21367 return UP(pm_if_node_modifier_create(parser, node, &keyword, predicate));
21368 }
21369 case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: {
21370 pm_token_t keyword = parser->current;
21371 parser_lex(parser);
21372
21373 pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1));
21374 return UP(pm_unless_node_modifier_create(parser, node, &keyword, predicate));
21375 }
21376 case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: {
21377 parser_lex(parser);
21378 pm_statements_node_t *statements = pm_statements_node_create(parser);
21379 pm_statements_node_body_append(parser, statements, node, true);
21380
21381 pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1));
21382 return UP(pm_until_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0));
21383 }
21384 case PM_TOKEN_KEYWORD_WHILE_MODIFIER: {
21385 parser_lex(parser);
21386 pm_statements_node_t *statements = pm_statements_node_create(parser);
21387 pm_statements_node_body_append(parser, statements, node, true);
21388
21389 pm_node_t *predicate = parse_value_expression(parser, binding_power, true, false, PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1));
21390 return UP(pm_while_node_modifier_create(parser, &token, predicate, statements, PM_NODE_TYPE_P(node, PM_BEGIN_NODE) ? PM_LOOP_FLAGS_BEGIN_MODIFIER : 0));
21391 }
21392 case PM_TOKEN_QUESTION_MARK: {
21393 context_push(parser, PM_CONTEXT_TERNARY);
21394 pm_node_list_t current_block_exits = { 0 };
21395 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
21396
21397 pm_token_t qmark = parser->current;
21398 parser_lex(parser);
21399
21400 pm_node_t *true_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_TERNARY_EXPRESSION_TRUE, (uint16_t) (depth + 1));
21401
21402 if (parser->recovering) {
21403 // If parsing the true expression of this ternary resulted in a syntax
21404 // error that we can recover from, then we're going to put missing nodes
21405 // and tokens into the remaining places. We want to be sure to do this
21406 // before the `expect` function call to make sure it doesn't
21407 // accidentally move past a ':' token that occurs after the syntax
21408 // error.
21409 pm_token_t colon = (pm_token_t) { .type = PM_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
21410 pm_node_t *false_expression = UP(pm_missing_node_create(parser, colon.start, colon.end));
21411
21412 context_pop(parser);
21413 pop_block_exits(parser, previous_block_exits);
21414 pm_node_list_free(&current_block_exits);
21415
21416 return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression));
21417 }
21418
21419 accept1(parser, PM_TOKEN_NEWLINE);
21420 expect1(parser, PM_TOKEN_COLON, PM_ERR_TERNARY_COLON);
21421
21422 pm_token_t colon = parser->previous;
21423 pm_node_t *false_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, false, false, PM_ERR_TERNARY_EXPRESSION_FALSE, (uint16_t) (depth + 1));
21424
21425 context_pop(parser);
21426 pop_block_exits(parser, previous_block_exits);
21427 pm_node_list_free(&current_block_exits);
21428
21429 return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression));
21430 }
21431 case PM_TOKEN_COLON_COLON: {
21432 parser_lex(parser);
21433 pm_token_t delimiter = parser->previous;
21434
21435 switch (parser->current.type) {
21436 case PM_TOKEN_CONSTANT: {
21437 parser_lex(parser);
21438 pm_node_t *path;
21439
21440 if (
21441 (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
21442 (accepts_command_call && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)))
21443 ) {
21444 // If we have a constant immediately following a '::' operator, then
21445 // this can either be a constant path or a method call, depending on
21446 // what follows the constant.
21447 //
21448 // If we have parentheses, then this is a method call. That would
21449 // look like Foo::Bar().
21450 pm_token_t message = parser->previous;
21451 pm_arguments_t arguments = { 0 };
21452
21453 parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1));
21454 path = UP(pm_call_node_call_create(parser, node, &delimiter, &message, &arguments));
21455 } else {
21456 // Otherwise, this is a constant path. That would look like Foo::Bar.
21457 path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
21458 }
21459
21460 // If this is followed by a comma then it is a multiple assignment.
21461 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
21462 return parse_targets_validate(parser, path, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21463 }
21464
21465 return path;
21466 }
21467 case PM_CASE_OPERATOR:
21468 case PM_CASE_KEYWORD:
21469 case PM_TOKEN_IDENTIFIER:
21470 case PM_TOKEN_METHOD_NAME: {
21471 parser_lex(parser);
21472 pm_token_t message = parser->previous;
21473
21474 // If we have an identifier following a '::' operator, then it is for
21475 // sure a method call.
21476 pm_arguments_t arguments = { 0 };
21477 parse_arguments_list(parser, &arguments, true, accepts_command_call, (uint16_t) (depth + 1));
21478 pm_call_node_t *call = pm_call_node_call_create(parser, node, &delimiter, &message, &arguments);
21479
21480 // If this is followed by a comma then it is a multiple assignment.
21481 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
21482 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21483 }
21484
21485 return UP(call);
21486 }
21487 case PM_TOKEN_PARENTHESIS_LEFT: {
21488 // If we have a parenthesis following a '::' operator, then it is the
21489 // method call shorthand. That would look like Foo::(bar).
21490 pm_arguments_t arguments = { 0 };
21491 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
21492
21493 return UP(pm_call_node_shorthand_create(parser, node, &delimiter, &arguments));
21494 }
21495 default: {
21496 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
21497 return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
21498 }
21499 }
21500 }
21501 case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: {
21502 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21503 parser_lex(parser);
21504 accept1(parser, PM_TOKEN_NEWLINE);
21505
21506 pm_node_t *value = parse_expression(parser, binding_power, true, false, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21507 context_pop(parser);
21508
21509 return UP(pm_rescue_modifier_node_create(parser, node, &token, value));
21510 }
21511 case PM_TOKEN_BRACKET_LEFT: {
21512 parser_lex(parser);
21513
21514 pm_arguments_t arguments = { 0 };
21515 arguments.opening_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
21516
21517 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
21518 pm_accepts_block_stack_push(parser, true);
21519 parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT, (uint16_t) (depth + 1));
21520 pm_accepts_block_stack_pop(parser);
21521 expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET);
21522 }
21523
21524 arguments.closing_loc = PM_LOCATION_TOKEN_VALUE(&parser->previous);
21525
21526 // If we have a comma after the closing bracket then this is a multiple
21527 // assignment and we should parse the targets.
21528 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
21529 pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments);
21530 return parse_targets_validate(parser, UP(aref), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21531 }
21532
21533 // If we're at the end of the arguments, we can now check if there is a
21534 // block node that starts with a {. If there is, then we can parse it and
21535 // add it to the arguments.
21536 pm_block_node_t *block = NULL;
21537 if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
21538 block = parse_block(parser, (uint16_t) (depth + 1));
21539 pm_arguments_validate_block(parser, &arguments, block);
21540 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
21541 block = parse_block(parser, (uint16_t) (depth + 1));
21542 }
21543
21544 if (block != NULL) {
21545 if (arguments.block != NULL) {
21546 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_AFTER_BLOCK);
21547 if (arguments.arguments == NULL) {
21548 arguments.arguments = pm_arguments_node_create(parser);
21549 }
21550 pm_arguments_node_arguments_append(arguments.arguments, arguments.block);
21551 }
21552
21553 arguments.block = UP(block);
21554 }
21555
21556 return UP(pm_call_node_aref_create(parser, node, &arguments));
21557 }
21558 case PM_TOKEN_KEYWORD_IN: {
21559 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
21560 parser->pattern_matching_newlines = true;
21561
21562 pm_token_t operator = parser->current;
21563 parser->command_start = false;
21564 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
21565 parser_lex(parser);
21566
21567 pm_constant_id_list_t captures = { 0 };
21568 pm_node_t *pattern = parse_pattern(parser, &captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_IN, (uint16_t) (depth + 1));
21569
21570 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
21571 pm_constant_id_list_free(&captures);
21572
21573 return UP(pm_match_predicate_node_create(parser, node, pattern, &operator));
21574 }
21575 case PM_TOKEN_EQUAL_GREATER: {
21576 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
21577 parser->pattern_matching_newlines = true;
21578
21579 pm_token_t operator = parser->current;
21580 parser->command_start = false;
21581 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
21582 parser_lex(parser);
21583
21584 pm_constant_id_list_t captures = { 0 };
21585 pm_node_t *pattern = parse_pattern(parser, &captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_HROCKET, (uint16_t) (depth + 1));
21586
21587 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
21588 pm_constant_id_list_free(&captures);
21589
21590 return UP(pm_match_required_node_create(parser, node, pattern, &operator));
21591 }
21592 default:
21593 assert(false && "unreachable");
21594 return NULL;
21595 }
21596}
21597
21598#undef PM_PARSE_PATTERN_SINGLE
21599#undef PM_PARSE_PATTERN_TOP
21600#undef PM_PARSE_PATTERN_MULTI
21601
21606static inline bool
21607pm_call_node_command_p(const pm_call_node_t *node) {
21608 return (
21609 (node->opening_loc.start == NULL) &&
21610 (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) &&
21611 (node->arguments != NULL || node->block != NULL)
21612 );
21613}
21614
21623static pm_node_t *
21624parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool accepts_command_call, bool accepts_label, pm_diagnostic_id_t diag_id, uint16_t depth) {
21625 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
21626 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
21627 return UP(pm_missing_node_create(parser, parser->current.start, parser->current.end));
21628 }
21629
21630 pm_node_t *node = parse_expression_prefix(parser, binding_power, accepts_command_call, accepts_label, diag_id, depth);
21631
21632 switch (PM_NODE_TYPE(node)) {
21633 case PM_MISSING_NODE:
21634 // If we found a syntax error, then the type of node returned by
21635 // parse_expression_prefix is going to be a missing node.
21636 return node;
21637 case PM_PRE_EXECUTION_NODE:
21638 case PM_POST_EXECUTION_NODE:
21639 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
21640 case PM_ALIAS_METHOD_NODE:
21641 case PM_MULTI_WRITE_NODE:
21642 case PM_UNDEF_NODE:
21643 // These expressions are statements, and cannot be followed by
21644 // operators (except modifiers).
21645 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21646 return node;
21647 }
21648 break;
21649 case PM_CALL_NODE:
21650 // If we have a call node, then we need to check if it looks like a
21651 // method call without parentheses that contains arguments. If it
21652 // does, then it has different rules for parsing infix operators,
21653 // namely that it only accepts composition (and/or) and modifiers
21654 // (if/unless/etc.).
21655 if ((pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_COMPOSITION) && pm_call_node_command_p((pm_call_node_t *) node)) {
21656 return node;
21657 }
21658 break;
21659 case PM_SYMBOL_NODE:
21660 // If we have a symbol node that is being parsed as a label, then we
21661 // need to immediately return, because there should never be an
21662 // infix operator following this node.
21663 if (pm_symbol_node_label_p(node)) {
21664 return node;
21665 }
21666 break;
21667 default:
21668 break;
21669 }
21670
21671 // Otherwise we'll look and see if the next token can be parsed as an infix
21672 // operator. If it can, then we'll parse it using parse_expression_infix.
21673 pm_binding_powers_t current_binding_powers;
21674 pm_token_type_t current_token_type;
21675
21676 while (
21677 current_token_type = parser->current.type,
21678 current_binding_powers = pm_binding_powers[current_token_type],
21679 binding_power <= current_binding_powers.left &&
21680 current_binding_powers.binary
21681 ) {
21682 node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call, (uint16_t) (depth + 1));
21683
21684 switch (PM_NODE_TYPE(node)) {
21685 case PM_MULTI_WRITE_NODE:
21686 // Multi-write nodes are statements, and cannot be followed by
21687 // operators except modifiers.
21688 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21689 return node;
21690 }
21691 break;
21692 case PM_CLASS_VARIABLE_WRITE_NODE:
21693 case PM_CONSTANT_PATH_WRITE_NODE:
21694 case PM_CONSTANT_WRITE_NODE:
21695 case PM_GLOBAL_VARIABLE_WRITE_NODE:
21696 case PM_INSTANCE_VARIABLE_WRITE_NODE:
21697 case PM_LOCAL_VARIABLE_WRITE_NODE:
21698 // These expressions are statements, by virtue of the right-hand
21699 // side of their write being an implicit array.
21700 if (PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21701 return node;
21702 }
21703 break;
21704 case PM_CALL_NODE:
21705 // These expressions are also statements, by virtue of the
21706 // right-hand side of the expression (i.e., the last argument to
21707 // the call node) being an implicit array.
21708 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY) && pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21709 return node;
21710 }
21711 break;
21712 default:
21713 break;
21714 }
21715
21716 // If the operator is nonassoc and we should not be able to parse the
21717 // upcoming infix operator, break.
21718 if (current_binding_powers.nonassoc) {
21719 // If this is a non-assoc operator and we are about to parse the
21720 // exact same operator, then we need to add an error.
21721 if (match1(parser, current_token_type)) {
21722 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type));
21723 break;
21724 }
21725
21726 // If this is an endless range, then we need to reject a couple of
21727 // additional operators because it violates the normal operator
21728 // precedence rules. Those patterns are:
21729 //
21730 // 1.. & 2
21731 // 1.. * 2
21732 //
21733 if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) {
21734 if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) {
21735 PM_PARSER_ERR_TOKEN_FORMAT(parser, parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_type_human(parser->current.type), pm_token_type_human(current_token_type));
21736 break;
21737 }
21738
21739 if (PM_BINDING_POWER_TERM <= pm_binding_powers[parser->current.type].left) {
21740 break;
21741 }
21742 } else if (current_binding_powers.left <= pm_binding_powers[parser->current.type].left) {
21743 break;
21744 }
21745 }
21746
21747 if (accepts_command_call) {
21748 // A command-style method call is only accepted on method chains.
21749 // Thus, we check whether the parsed node can continue method chains.
21750 // The method chain can continue if the parsed node is one of the following five kinds:
21751 // (1) index access: foo[1]
21752 // (2) attribute access: foo.bar
21753 // (3) method call with parenthesis: foo.bar(1)
21754 // (4) method call with a block: foo.bar do end
21755 // (5) constant path: foo::Bar
21756 switch (node->type) {
21757 case PM_CALL_NODE: {
21758 pm_call_node_t *cast = (pm_call_node_t *)node;
21759 if (
21760 // (1) foo[1]
21761 !(
21762 cast->call_operator_loc.start == NULL &&
21763 cast->message_loc.start != NULL &&
21764 cast->message_loc.start[0] == '[' &&
21765 cast->message_loc.end[-1] == ']'
21766 ) &&
21767 // (2) foo.bar
21768 !(
21769 cast->call_operator_loc.start != NULL &&
21770 cast->arguments == NULL &&
21771 cast->block == NULL &&
21772 cast->opening_loc.start == NULL
21773 ) &&
21774 // (3) foo.bar(1)
21775 !(
21776 cast->call_operator_loc.start != NULL &&
21777 cast->opening_loc.start != NULL
21778 ) &&
21779 // (4) foo.bar do end
21780 !(
21781 cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
21782 )
21783 ) {
21784 accepts_command_call = false;
21785 }
21786 break;
21787 }
21788 // (5) foo::Bar
21789 case PM_CONSTANT_PATH_NODE:
21790 break;
21791 default:
21792 accepts_command_call = false;
21793 break;
21794 }
21795 }
21796
21797 if (context_terminator(parser->current_context->context, &parser->current)) {
21798 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
21799 if (
21800 !next_binding_powers.binary ||
21801 binding_power > next_binding_powers.left ||
21802 (PM_NODE_TYPE_P(node, PM_CALL_NODE) && pm_call_node_command_p((pm_call_node_t *) node))
21803 ) {
21804 return node;
21805 }
21806 }
21807 }
21808
21809 return node;
21810}
21811
21816static pm_statements_node_t *
21817wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
21818 if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) {
21819 if (statements == NULL) {
21820 statements = pm_statements_node_create(parser);
21821 }
21822
21823 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
21824 pm_arguments_node_arguments_append(
21825 arguments,
21826 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)))
21827 );
21828
21829 pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create(
21830 parser,
21831 arguments,
21832 pm_parser_constant_id_constant(parser, "print", 5)
21833 )), true);
21834 }
21835
21836 if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) {
21837 if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) {
21838 if (statements == NULL) {
21839 statements = pm_statements_node_create(parser);
21840 }
21841
21842 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
21843 pm_arguments_node_arguments_append(
21844 arguments,
21845 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)))
21846 );
21847
21848 pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2));
21849 pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, UP(receiver), "split", arguments);
21850
21851 pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create(
21852 parser,
21853 pm_parser_constant_id_constant(parser, "$F", 2),
21854 UP(call)
21855 );
21856
21857 pm_statements_node_body_prepend(statements, UP(write));
21858 }
21859
21860 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
21861 pm_arguments_node_arguments_append(
21862 arguments,
21863 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)))
21864 );
21865
21866 if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) {
21867 pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser);
21868 pm_keyword_hash_node_elements_append(keywords, UP(pm_assoc_node_create(
21869 parser,
21870 UP(pm_symbol_node_synthesized_create(parser, "chomp")),
21871 &(pm_token_t) { .type = PM_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start },
21872 UP(pm_true_node_synthesized_create(parser))
21873 )));
21874
21875 pm_arguments_node_arguments_append(arguments, UP(keywords));
21876 pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS);
21877 }
21878
21879 pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
21880 pm_statements_node_body_append(parser, wrapped_statements, UP(pm_while_node_synthesized_create(
21881 parser,
21882 UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))),
21883 statements
21884 )), true);
21885
21886 statements = wrapped_statements;
21887 }
21888
21889 return statements;
21890}
21891
21895static pm_node_t *
21896parse_program(pm_parser_t *parser) {
21897 // If the current scope is NULL, then we want to push a new top level scope.
21898 // The current scope could exist in the event that we are parsing an eval
21899 // and the user has passed into scopes that already exist.
21900 if (parser->current_scope == NULL) {
21901 pm_parser_scope_push(parser, true);
21902 }
21903
21904 pm_node_list_t current_block_exits = { 0 };
21905 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
21906
21907 parser_lex(parser);
21908 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN, 0);
21909
21910 if (statements != NULL && !parser->parsing_eval) {
21911 // If we have statements, then the top-level statement should be
21912 // explicitly checked as well. We have to do this here because
21913 // everywhere else we check all but the last statement.
21914 assert(statements->body.size > 0);
21915 pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]);
21916 }
21917
21918 pm_constant_id_list_t locals;
21919 pm_locals_order(parser, &parser->current_scope->locals, &locals, true);
21920 pm_parser_scope_pop(parser);
21921
21922 // At the top level, see if we need to wrap the statements in a program
21923 // node with a while loop based on the options.
21924 if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
21925 statements = wrap_statements(parser, statements);
21926 } else {
21927 flush_block_exits(parser, previous_block_exits);
21928 }
21929
21930 pm_node_list_free(&current_block_exits);
21931
21932 // If this is an empty file, then we're still going to parse all of the
21933 // statements in order to gather up all of the comments and such. Here we'll
21934 // correct the location information.
21935 if (statements == NULL) {
21936 statements = pm_statements_node_create(parser);
21937 pm_statements_node_location_set(statements, parser->start, parser->start);
21938 }
21939
21940 return UP(pm_program_node_create(parser, &locals, statements));
21941}
21942
21943/******************************************************************************/
21944/* External functions */
21945/******************************************************************************/
21946
21956static const char *
21957pm_strnstr(const char *big, const char *little, size_t big_length) {
21958 size_t little_length = strlen(little);
21959
21960 for (const char *max = big + big_length - little_length; big <= max; big++) {
21961 if (*big == *little && memcmp(big, little, little_length) == 0) return big;
21962 }
21963
21964 return NULL;
21965}
21966
21967#ifdef _WIN32
21968#define pm_parser_warn_shebang_carriage_return(parser, start, length) ((void) 0)
21969#else
21975static void
21976pm_parser_warn_shebang_carriage_return(pm_parser_t *parser, const uint8_t *start, size_t length) {
21977 if (length > 2 && start[length - 2] == '\r' && start[length - 1] == '\n') {
21978 pm_parser_warn(parser, start, start + length, PM_WARN_SHEBANG_CARRIAGE_RETURN);
21979 }
21980}
21981#endif
21982
21987static void
21988pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const char *engine, size_t length) {
21989 const char *switches = pm_strnstr(engine, " -", length);
21990 if (switches == NULL) return;
21991
21992 pm_options_t next_options = *options;
21993 options->shebang_callback(
21994 &next_options,
21995 (const uint8_t *) (switches + 1),
21996 length - ((size_t) (switches - engine)) - 1,
21997 options->shebang_callback_data
21998 );
21999
22000 size_t encoding_length;
22001 if ((encoding_length = pm_string_length(&next_options.encoding)) > 0) {
22002 const uint8_t *encoding_source = pm_string_source(&next_options.encoding);
22003 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
22004 }
22005
22006 parser->command_line = next_options.command_line;
22007 parser->frozen_string_literal = next_options.frozen_string_literal;
22008}
22009
22014pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) {
22015 assert(source != NULL);
22016
22017 *parser = (pm_parser_t) {
22018 .node_id = 0,
22019 .lex_state = PM_LEX_STATE_BEG,
22020 .enclosure_nesting = 0,
22021 .lambda_enclosure_nesting = -1,
22022 .brace_nesting = 0,
22023 .do_loop_stack = 0,
22024 .accepts_block_stack = 0,
22025 .lex_modes = {
22026 .index = 0,
22027 .stack = {{ .mode = PM_LEX_DEFAULT }},
22028 .current = &parser->lex_modes.stack[0],
22029 },
22030 .start = source,
22031 .end = source + size,
22032 .previous = { .type = PM_TOKEN_EOF, .start = source, .end = source },
22033 .current = { .type = PM_TOKEN_EOF, .start = source, .end = source },
22034 .next_start = NULL,
22035 .heredoc_end = NULL,
22036 .data_loc = { .start = NULL, .end = NULL },
22037 .comment_list = { 0 },
22038 .magic_comment_list = { 0 },
22039 .warning_list = { 0 },
22040 .error_list = { 0 },
22041 .current_scope = NULL,
22042 .current_context = NULL,
22043 .encoding = PM_ENCODING_UTF_8_ENTRY,
22044 .encoding_changed_callback = NULL,
22045 .encoding_comment_start = source,
22046 .lex_callback = NULL,
22047 .filepath = { 0 },
22048 .constant_pool = { 0 },
22049 .newline_list = { 0 },
22050 .integer_base = 0,
22051 .current_string = PM_STRING_EMPTY,
22052 .start_line = 1,
22053 .explicit_encoding = NULL,
22054 .command_line = 0,
22055 .parsing_eval = false,
22056 .partial_script = false,
22057 .command_start = true,
22058 .recovering = false,
22059 .encoding_locked = false,
22060 .encoding_changed = false,
22061 .pattern_matching_newlines = false,
22062 .in_keyword_arg = false,
22063 .current_block_exits = NULL,
22064 .semantic_token_seen = false,
22065 .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET,
22066 .current_regular_expression_ascii_only = false,
22067 .warn_mismatched_indentation = true
22068 };
22069
22070 // Initialize the constant pool. We're going to completely guess as to the
22071 // number of constants that we'll need based on the size of the input. The
22072 // ratio we chose here is actually less arbitrary than you might think.
22073 //
22074 // We took ~50K Ruby files and measured the size of the file versus the
22075 // number of constants that were found in those files. Then we found the
22076 // average and standard deviation of the ratios of constants/bytesize. Then
22077 // we added 1.34 standard deviations to the average to get a ratio that
22078 // would fit 75% of the files (for a two-tailed distribution). This works
22079 // because there was about a 0.77 correlation and the distribution was
22080 // roughly normal.
22081 //
22082 // This ratio will need to change if we add more constants to the constant
22083 // pool for another node type.
22084 uint32_t constant_size = ((uint32_t) size) / 95;
22085 pm_constant_pool_init(&parser->constant_pool, constant_size < 4 ? 4 : constant_size);
22086
22087 // Initialize the newline list. Similar to the constant pool, we're going to
22088 // guess at the number of newlines that we'll need based on the size of the
22089 // input.
22090 size_t newline_size = size / 22;
22091 pm_newline_list_init(&parser->newline_list, source, newline_size < 4 ? 4 : newline_size);
22092
22093 // If options were provided to this parse, establish them here.
22094 if (options != NULL) {
22095 // filepath option
22096 parser->filepath = options->filepath;
22097
22098 // line option
22099 parser->start_line = options->line;
22100
22101 // encoding option
22102 size_t encoding_length = pm_string_length(&options->encoding);
22103 if (encoding_length > 0) {
22104 const uint8_t *encoding_source = pm_string_source(&options->encoding);
22105 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
22106 }
22107
22108 // encoding_locked option
22109 parser->encoding_locked = options->encoding_locked;
22110
22111 // frozen_string_literal option
22113
22114 // command_line option
22115 parser->command_line = options->command_line;
22116
22117 // version option
22118 parser->version = options->version;
22119
22120 // partial_script
22121 parser->partial_script = options->partial_script;
22122
22123 // scopes option
22124 parser->parsing_eval = options->scopes_count > 0;
22125 if (parser->parsing_eval) parser->warn_mismatched_indentation = false;
22126
22127 for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
22128 const pm_options_scope_t *scope = pm_options_scope_get(options, scope_index);
22129 pm_parser_scope_push(parser, scope_index == 0);
22130
22131 // Scopes given from the outside are not allowed to have numbered
22132 // parameters.
22133 parser->current_scope->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED;
22134
22135 for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
22136 const pm_string_t *local = pm_options_scope_local_get(scope, local_index);
22137
22138 const uint8_t *source = pm_string_source(local);
22139 size_t length = pm_string_length(local);
22140
22141 void *allocated = xmalloc(length);
22142 if (allocated == NULL) continue;
22143
22144 memcpy(allocated, source, length);
22145 pm_parser_local_add_owned(parser, (uint8_t *) allocated, length);
22146 }
22147 }
22148 }
22149
22150 // Now that we have established the user-provided options, check if
22151 // a version was given and parse as the latest version otherwise.
22152 if (parser->version == PM_OPTIONS_VERSION_UNSET) {
22154 }
22155
22156 pm_accepts_block_stack_push(parser, true);
22157
22158 // Skip past the UTF-8 BOM if it exists.
22159 if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) {
22160 parser->current.end += 3;
22161 parser->encoding_comment_start += 3;
22162
22163 if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
22165 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
22166 }
22167 }
22168
22169 // If the -x command line flag is set, or the first shebang of the file does
22170 // not include "ruby", then we'll search for a shebang that does include
22171 // "ruby" and start parsing from there.
22172 bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
22173
22174 // If the first two bytes of the source are a shebang, then we will do a bit
22175 // of extra processing.
22176 //
22177 // First, we'll indicate that the encoding comment is at the end of the
22178 // shebang. This means that when a shebang is present the encoding comment
22179 // can begin on the second line.
22180 //
22181 // Second, we will check if the shebang includes "ruby". If it does, then we
22182 // we will start parsing from there. We will also potentially warning the
22183 // user if there is a carriage return at the end of the shebang. We will
22184 // also potentially call the shebang callback if this is the main script to
22185 // allow the caller to parse the shebang and find any command-line options.
22186 // If the shebang does not include "ruby" and this is the main script being
22187 // parsed, then we will start searching the file for a shebang that does
22188 // contain "ruby" as if -x were passed on the command line.
22189 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
22190 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end);
22191
22192 if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') {
22193 const char *engine;
22194
22195 if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) {
22196 if (newline != NULL) {
22197 parser->encoding_comment_start = newline + 1;
22198
22199 if (options == NULL || options->main_script) {
22200 pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1);
22201 }
22202 }
22203
22204 if (options != NULL && options->main_script && options->shebang_callback != NULL) {
22205 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start)));
22206 }
22207
22208 search_shebang = false;
22209 } else if (options != NULL && options->main_script && !parser->parsing_eval) {
22210 search_shebang = true;
22211 }
22212 }
22213
22214 // Here we're going to find the first shebang that includes "ruby" and start
22215 // parsing from there.
22216 if (search_shebang) {
22217 // If a shebang that includes "ruby" is not found, then we're going to a
22218 // a load error to the list of errors on the parser.
22219 bool found_shebang = false;
22220
22221 // This is going to point to the start of each line as we check it.
22222 // We'll maintain a moving window looking at each line at they come.
22223 const uint8_t *cursor = parser->start;
22224
22225 // The newline pointer points to the end of the current line that we're
22226 // considering. If it is NULL, then we're at the end of the file.
22227 const uint8_t *newline = next_newline(cursor, parser->end - cursor);
22228
22229 while (newline != NULL) {
22230 pm_newline_list_append(&parser->newline_list, newline);
22231
22232 cursor = newline + 1;
22233 newline = next_newline(cursor, parser->end - cursor);
22234
22235 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - cursor);
22236 if (length > 2 && cursor[0] == '#' && cursor[1] == '!') {
22237 const char *engine;
22238 if ((engine = pm_strnstr((const char *) cursor, "ruby", length)) != NULL) {
22239 found_shebang = true;
22240
22241 if (newline != NULL) {
22242 pm_parser_warn_shebang_carriage_return(parser, cursor, length + 1);
22243 parser->encoding_comment_start = newline + 1;
22244 }
22245
22246 if (options != NULL && options->shebang_callback != NULL) {
22247 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) cursor)));
22248 }
22249
22250 break;
22251 }
22252 }
22253 }
22254
22255 if (found_shebang) {
22256 parser->previous = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
22257 parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
22258 } else {
22259 pm_parser_err(parser, parser->start, parser->start, PM_ERR_SCRIPT_NOT_FOUND);
22260 pm_newline_list_clear(&parser->newline_list);
22261 }
22262 }
22263
22264 // The encoding comment can start after any amount of inline whitespace, so
22265 // here we'll advance it to the first non-inline-whitespace character so
22266 // that it is ready for future comparisons.
22267 parser->encoding_comment_start += pm_strspn_inline_whitespace(parser->encoding_comment_start, parser->end - parser->encoding_comment_start);
22268}
22269
22278
22282static inline void
22283pm_comment_list_free(pm_list_t *list) {
22284 pm_list_node_t *node, *next;
22285
22286 for (node = list->head; node != NULL; node = next) {
22287 next = node->next;
22288
22289 pm_comment_t *comment = (pm_comment_t *) node;
22290 xfree(comment);
22291 }
22292}
22293
22297static inline void
22298pm_magic_comment_list_free(pm_list_t *list) {
22299 pm_list_node_t *node, *next;
22300
22301 for (node = list->head; node != NULL; node = next) {
22302 next = node->next;
22303
22306 }
22307}
22308
22314 pm_string_free(&parser->filepath);
22315 pm_diagnostic_list_free(&parser->error_list);
22316 pm_diagnostic_list_free(&parser->warning_list);
22317 pm_comment_list_free(&parser->comment_list);
22318 pm_magic_comment_list_free(&parser->magic_comment_list);
22319 pm_constant_pool_free(&parser->constant_pool);
22320 pm_newline_list_free(&parser->newline_list);
22321
22322 while (parser->current_scope != NULL) {
22323 // Normally, popping the scope doesn't free the locals since it is
22324 // assumed that ownership has transferred to the AST. However if we have
22325 // scopes while we're freeing the parser, it's likely they came from
22326 // eval scopes and we need to free them explicitly here.
22327 pm_parser_scope_pop(parser);
22328 }
22329
22330 while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
22331 lex_mode_pop(parser);
22332 }
22333}
22334
22338PRISM_EXPORTED_FUNCTION pm_node_t *
22340 return parse_program(parser);
22341}
22342
22348static bool
22349pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof) {
22350#define LINE_SIZE 4096
22351 char line[LINE_SIZE];
22352
22353 while (memset(line, '\n', LINE_SIZE), stream_fgets(line, LINE_SIZE, stream) != NULL) {
22354 size_t length = LINE_SIZE;
22355 while (length > 0 && line[length - 1] == '\n') length--;
22356
22357 if (length == LINE_SIZE) {
22358 // If we read a line that is the maximum size and it doesn't end
22359 // with a newline, then we'll just append it to the buffer and
22360 // continue reading.
22361 length--;
22362 pm_buffer_append_string(buffer, line, length);
22363 continue;
22364 }
22365
22366 // Append the line to the buffer.
22367 length--;
22368 pm_buffer_append_string(buffer, line, length);
22369
22370 // Check if the line matches the __END__ marker. If it does, then stop
22371 // reading and return false. In most circumstances, this means we should
22372 // stop reading from the stream so that the DATA constant can pick it
22373 // up.
22374 switch (length) {
22375 case 7:
22376 if (strncmp(line, "__END__", 7) == 0) return false;
22377 break;
22378 case 8:
22379 if (strncmp(line, "__END__\n", 8) == 0) return false;
22380 break;
22381 case 9:
22382 if (strncmp(line, "__END__\r\n", 9) == 0) return false;
22383 break;
22384 }
22385
22386 // All data should be read via gets. If the string returned by gets
22387 // _doesn't_ end with a newline, then we assume we hit EOF condition.
22388 if (stream_feof(stream)) {
22389 break;
22390 }
22391 }
22392
22393 return true;
22394#undef LINE_SIZE
22395}
22396
22406static bool
22407pm_parse_stream_unterminated_heredoc_p(pm_parser_t *parser) {
22408 pm_diagnostic_t *diagnostic = (pm_diagnostic_t *) parser->error_list.head;
22409
22410 for (; diagnostic != NULL; diagnostic = (pm_diagnostic_t *) diagnostic->node.next) {
22411 if (diagnostic->diag_id == PM_ERR_HEREDOC_TERM) {
22412 return true;
22413 }
22414 }
22415
22416 return false;
22417}
22418
22425PRISM_EXPORTED_FUNCTION pm_node_t *
22426pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const pm_options_t *options) {
22427 pm_buffer_init(buffer);
22428
22429 bool eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof);
22430
22431 pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
22432 pm_node_t *node = pm_parse(parser);
22433
22434 while (!eof && parser->error_list.size > 0 && (parser->lex_modes.index > 0 || pm_parse_stream_unterminated_heredoc_p(parser))) {
22435 pm_node_destroy(parser, node);
22436 eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof);
22437
22438 pm_parser_free(parser);
22439 pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
22440 node = pm_parse(parser);
22441 }
22442
22443 return node;
22444}
22445
22450pm_parse_success_p(const uint8_t *source, size_t size, const char *data) {
22451 pm_options_t options = { 0 };
22452 pm_options_read(&options, data);
22453
22454 pm_parser_t parser;
22455 pm_parser_init(&parser, source, size, &options);
22456
22457 pm_node_t *node = pm_parse(&parser);
22458 pm_node_destroy(&parser, node);
22459
22460 bool result = parser.error_list.size == 0;
22461 pm_parser_free(&parser);
22462 pm_options_free(&options);
22463
22464 return result;
22465}
22466
22467#undef PM_CASE_KEYWORD
22468#undef PM_CASE_OPERATOR
22469#undef PM_CASE_WRITABLE
22470#undef PM_STRING_EMPTY
22471
22472// We optionally support serializing to a binary string. For systems that don't
22473// want or need this functionality, it can be turned off with the
22474// PRISM_EXCLUDE_SERIALIZATION define.
22475#ifndef PRISM_EXCLUDE_SERIALIZATION
22476
22477static inline void
22478pm_serialize_header(pm_buffer_t *buffer) {
22479 pm_buffer_append_string(buffer, "PRISM", 5);
22480 pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR);
22481 pm_buffer_append_byte(buffer, PRISM_VERSION_MINOR);
22482 pm_buffer_append_byte(buffer, PRISM_VERSION_PATCH);
22483 pm_buffer_append_byte(buffer, PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS ? 1 : 0);
22484}
22485
22490pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
22491 pm_serialize_header(buffer);
22492 pm_serialize_content(parser, node, buffer);
22493 pm_buffer_append_byte(buffer, '\0');
22494}
22495
22501pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
22502 pm_options_t options = { 0 };
22503 pm_options_read(&options, data);
22504
22505 pm_parser_t parser;
22506 pm_parser_init(&parser, source, size, &options);
22507
22508 pm_node_t *node = pm_parse(&parser);
22509
22510 pm_serialize_header(buffer);
22511 pm_serialize_content(&parser, node, buffer);
22512 pm_buffer_append_byte(buffer, '\0');
22513
22514 pm_node_destroy(&parser, node);
22515 pm_parser_free(&parser);
22516 pm_options_free(&options);
22517}
22518
22524pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const char *data) {
22525 pm_parser_t parser;
22526 pm_options_t options = { 0 };
22527 pm_options_read(&options, data);
22528
22529 pm_buffer_t parser_buffer;
22530 pm_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, stream_fgets, stream_feof, &options);
22531 pm_serialize_header(buffer);
22532 pm_serialize_content(&parser, node, buffer);
22533 pm_buffer_append_byte(buffer, '\0');
22534
22535 pm_node_destroy(&parser, node);
22536 pm_buffer_free(&parser_buffer);
22537 pm_parser_free(&parser);
22538 pm_options_free(&options);
22539}
22540
22545pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
22546 pm_options_t options = { 0 };
22547 pm_options_read(&options, data);
22548
22549 pm_parser_t parser;
22550 pm_parser_init(&parser, source, size, &options);
22551
22552 pm_node_t *node = pm_parse(&parser);
22553 pm_serialize_header(buffer);
22554 pm_serialize_encoding(parser.encoding, buffer);
22555 pm_buffer_append_varsint(buffer, parser.start_line);
22556 pm_serialize_comment_list(&parser, &parser.comment_list, buffer);
22557
22558 pm_node_destroy(&parser, node);
22559 pm_parser_free(&parser);
22560 pm_options_free(&options);
22561}
22562
22563#endif
22564
22565/******************************************************************************/
22566/* Slice queries for the Ruby API */
22567/******************************************************************************/
22568
22570typedef enum {
22572 PM_SLICE_TYPE_ERROR = -1,
22573
22575 PM_SLICE_TYPE_NONE,
22576
22578 PM_SLICE_TYPE_LOCAL,
22579
22581 PM_SLICE_TYPE_CONSTANT,
22582
22584 PM_SLICE_TYPE_METHOD_NAME
22585} pm_slice_type_t;
22586
22590pm_slice_type_t
22591pm_slice_type(const uint8_t *source, size_t length, const char *encoding_name) {
22592 // first, get the right encoding object
22593 const pm_encoding_t *encoding = pm_encoding_find((const uint8_t *) encoding_name, (const uint8_t *) (encoding_name + strlen(encoding_name)));
22594 if (encoding == NULL) return PM_SLICE_TYPE_ERROR;
22595
22596 // check that there is at least one character
22597 if (length == 0) return PM_SLICE_TYPE_NONE;
22598
22599 size_t width;
22600 if ((width = encoding->alpha_char(source, (ptrdiff_t) length)) != 0) {
22601 // valid because alphabetical
22602 } else if (*source == '_') {
22603 // valid because underscore
22604 width = 1;
22605 } else if ((*source >= 0x80) && ((width = encoding->char_width(source, (ptrdiff_t) length)) > 0)) {
22606 // valid because multibyte
22607 } else {
22608 // invalid because no match
22609 return PM_SLICE_TYPE_NONE;
22610 }
22611
22612 // determine the type of the slice based on the first character
22613 const uint8_t *end = source + length;
22614 pm_slice_type_t result = encoding->isupper_char(source, end - source) ? PM_SLICE_TYPE_CONSTANT : PM_SLICE_TYPE_LOCAL;
22615
22616 // next, iterate through all of the bytes of the string to ensure that they
22617 // are all valid identifier characters
22618 source += width;
22619
22620 while (source < end) {
22621 if ((width = encoding->alnum_char(source, end - source)) != 0) {
22622 // valid because alphanumeric
22623 source += width;
22624 } else if (*source == '_') {
22625 // valid because underscore
22626 source++;
22627 } else if ((*source >= 0x80) && ((width = encoding->char_width(source, end - source)) > 0)) {
22628 // valid because multibyte
22629 source += width;
22630 } else {
22631 // invalid because no match
22632 break;
22633 }
22634 }
22635
22636 // accept a ! or ? at the end of the slice as a method name
22637 if (*source == '!' || *source == '?' || *source == '=') {
22638 source++;
22639 result = PM_SLICE_TYPE_METHOD_NAME;
22640 }
22641
22642 // valid if we are at the end of the slice
22643 return source == end ? result : PM_SLICE_TYPE_NONE;
22644}
22645
22650pm_string_query_local(const uint8_t *source, size_t length, const char *encoding_name) {
22651 switch (pm_slice_type(source, length, encoding_name)) {
22652 case PM_SLICE_TYPE_ERROR:
22653 return PM_STRING_QUERY_ERROR;
22654 case PM_SLICE_TYPE_NONE:
22655 case PM_SLICE_TYPE_CONSTANT:
22656 case PM_SLICE_TYPE_METHOD_NAME:
22657 return PM_STRING_QUERY_FALSE;
22658 case PM_SLICE_TYPE_LOCAL:
22659 return PM_STRING_QUERY_TRUE;
22660 }
22661
22662 assert(false && "unreachable");
22663 return PM_STRING_QUERY_FALSE;
22664}
22665
22670pm_string_query_constant(const uint8_t *source, size_t length, const char *encoding_name) {
22671 switch (pm_slice_type(source, length, encoding_name)) {
22672 case PM_SLICE_TYPE_ERROR:
22673 return PM_STRING_QUERY_ERROR;
22674 case PM_SLICE_TYPE_NONE:
22675 case PM_SLICE_TYPE_LOCAL:
22676 case PM_SLICE_TYPE_METHOD_NAME:
22677 return PM_STRING_QUERY_FALSE;
22678 case PM_SLICE_TYPE_CONSTANT:
22679 return PM_STRING_QUERY_TRUE;
22680 }
22681
22682 assert(false && "unreachable");
22683 return PM_STRING_QUERY_FALSE;
22684}
22685
22690pm_string_query_method_name(const uint8_t *source, size_t length, const char *encoding_name) {
22691#define B(p) ((p) ? PM_STRING_QUERY_TRUE : PM_STRING_QUERY_FALSE)
22692#define C1(c) (*source == c)
22693#define C2(s) (memcmp(source, s, 2) == 0)
22694#define C3(s) (memcmp(source, s, 3) == 0)
22695
22696 switch (pm_slice_type(source, length, encoding_name)) {
22697 case PM_SLICE_TYPE_ERROR:
22698 return PM_STRING_QUERY_ERROR;
22699 case PM_SLICE_TYPE_NONE:
22700 break;
22701 case PM_SLICE_TYPE_LOCAL:
22702 // numbered parameters are not valid method names
22703 return B((length != 2) || (source[0] != '_') || (source[1] == '0') || !pm_char_is_decimal_digit(source[1]));
22704 case PM_SLICE_TYPE_CONSTANT:
22705 // all constants are valid method names
22706 case PM_SLICE_TYPE_METHOD_NAME:
22707 // all method names are valid method names
22708 return PM_STRING_QUERY_TRUE;
22709 }
22710
22711 switch (length) {
22712 case 1:
22713 return B(C1('&') || C1('`') || C1('!') || C1('^') || C1('>') || C1('<') || C1('-') || C1('%') || C1('|') || C1('+') || C1('/') || C1('*') || C1('~'));
22714 case 2:
22715 return B(C2("!=") || C2("!~") || C2("[]") || C2("==") || C2("=~") || C2(">=") || C2(">>") || C2("<=") || C2("<<") || C2("**"));
22716 case 3:
22717 return B(C3("===") || C3("<=>") || C3("[]="));
22718 default:
22719 return PM_STRING_QUERY_FALSE;
22720 }
22721
22722#undef B
22723#undef C1
22724#undef C2
22725#undef C3
22726}
pm_diagnostic_id_t
The diagnostic IDs of all of the diagnostics, used to communicate the types of errors between the par...
Definition diagnostic.h:31
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define xcalloc
Old name of ruby_xcalloc.
Definition xmalloc.h:55
VALUE type(ANYARGS)
ANYARGS-ed function type.
struct pm_options_scope pm_options_scope_t
A scope of locals surrounding the code that is being parsed.
struct pm_options pm_options_t
The options that can be passed to the parser.
#define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
String literals should be made frozen.
Definition options.h:20
#define PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
String literals should be made mutable.
Definition options.h:31
#define PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET
String literals may be frozen or mutable depending on the implementation default.
Definition options.h:26
@ PM_OPTIONS_VERSION_CRUBY_3_3
The vendored version of prism in CRuby 3.3.x.
Definition options.h:89
@ PM_OPTIONS_VERSION_LATEST
The current version of prism.
Definition options.h:104
@ PM_OPTIONS_VERSION_UNSET
If an explicit version is not provided, the current version of prism will be used.
Definition options.h:86
@ PM_OPTIONS_VERSION_CRUBY_3_4
The vendored version of prism in CRuby 3.4.x.
Definition options.h:92
@ PM_OPTIONS_VERSION_CRUBY_4_0
The vendored version of prism in CRuby 4.0.x.
Definition options.h:98
struct pm_locals pm_locals_t
This is a set of local variables in a certain lexical context (method, class, module,...
pm_heredoc_indent_t
The type of indentation that a heredoc uses.
Definition parser.h:79
struct pm_context_node pm_context_node_t
This is a node in a linked list of contexts.
#define PM_LEX_STACK_SIZE
We pre-allocate a certain number of lex states in order to avoid having to call malloc too many times...
Definition parser.h:262
struct pm_parser pm_parser_t
The parser used to parse Ruby source.
Definition parser.h:267
struct pm_lex_mode pm_lex_mode_t
When lexing Ruby source, the lexer has a small amount of state to tell which kind of token it is curr...
struct pm_comment pm_comment_t
This is a node in the linked list of comments that we've found while parsing.
void(*) pm_encoding_changed_callback_t(pm_parser_t *parser)
When the encoding that is being used to parse the source is changed by prism, we provide the ability ...
Definition parser.h:499
pm_lex_state_t
This enum combines the various bits from the above enum into individual values that represent the var...
Definition parser.h:46
struct pm_scope pm_scope_t
This struct represents a node in a linked list of scopes.
pm_heredoc_quote_t
The type of quote that a heredoc uses.
Definition parser.h:69
pm_context_t
While parsing, we keep track of a stack of contexts.
Definition parser.h:274
@ PM_CONTEXT_CLASS_RESCUE
a rescue statement within a class statement
Definition parser.h:324
@ PM_CONTEXT_ELSIF
an elsif clause
Definition parser.h:351
@ PM_CONTEXT_DEF_RESCUE
a rescue statement within a method definition
Definition parser.h:336
@ PM_CONTEXT_ELSE
an else clause
Definition parser.h:348
@ PM_CONTEXT_FOR_INDEX
a for loop's index
Definition parser.h:360
@ PM_CONTEXT_CASE_WHEN
a case when statements
Definition parser.h:309
@ PM_CONTEXT_BLOCK_RESCUE
a rescue statement within a do..end block
Definition parser.h:306
@ PM_CONTEXT_MODULE
a module declaration
Definition parser.h:387
@ PM_CONTEXT_DEF_PARAMS
a method definition's parameters
Definition parser.h:339
@ PM_CONTEXT_CASE_IN
a case in statements
Definition parser.h:312
@ PM_CONTEXT_BLOCK_ELSE
a rescue else statement within a do..end block
Definition parser.h:300
@ PM_CONTEXT_LOOP_PREDICATE
the predicate clause of a loop statement
Definition parser.h:381
@ PM_CONTEXT_SCLASS
a singleton class definition
Definition parser.h:417
@ PM_CONTEXT_UNLESS
an unless statement
Definition parser.h:432
@ PM_CONTEXT_POSTEXE
an END block
Definition parser.h:405
@ PM_CONTEXT_IF
an if statement
Definition parser.h:363
@ PM_CONTEXT_MULTI_TARGET
a multiple target expression
Definition parser.h:399
@ PM_CONTEXT_LAMBDA_RESCUE
a rescue statement within a lambda expression
Definition parser.h:378
@ PM_CONTEXT_BEGIN_ELSE
a rescue else statement with an explicit begin
Definition parser.h:285
@ PM_CONTEXT_NONE
a null context, used for returning a value from a function
Definition parser.h:276
@ PM_CONTEXT_CLASS_ELSE
a rescue else statement within a class statement
Definition parser.h:321
@ PM_CONTEXT_LAMBDA_ENSURE
an ensure statement within a lambda expression
Definition parser.h:372
@ PM_CONTEXT_BLOCK_ENSURE
an ensure statement within a do..end block
Definition parser.h:297
@ PM_CONTEXT_CLASS_ENSURE
an ensure statement within a class statement
Definition parser.h:318
@ PM_CONTEXT_LAMBDA_BRACES
a lambda expression with braces
Definition parser.h:366
@ PM_CONTEXT_MODULE_ELSE
a rescue else statement within a module statement
Definition parser.h:393
@ PM_CONTEXT_PARENS
a parenthesized expression
Definition parser.h:402
@ PM_CONTEXT_BLOCK_BRACES
expressions in block arguments using braces
Definition parser.h:291
@ PM_CONTEXT_BLOCK_PARAMETERS
expressions in block parameters foo do |...| end
Definition parser.h:303
@ PM_CONTEXT_DEF_ENSURE
an ensure statement within a method definition
Definition parser.h:330
@ PM_CONTEXT_SCLASS_RESCUE
a rescue statement with a singleton class
Definition parser.h:426
@ PM_CONTEXT_PREEXE
a BEGIN block
Definition parser.h:411
@ PM_CONTEXT_DEFINED
a defined?
Definition parser.h:342
@ PM_CONTEXT_MODULE_ENSURE
an ensure statement within a module statement
Definition parser.h:390
@ PM_CONTEXT_BEGIN_RESCUE
a rescue statement with an explicit begin
Definition parser.h:288
@ PM_CONTEXT_UNTIL
an until statement
Definition parser.h:435
@ PM_CONTEXT_DEF_ELSE
a rescue else statement within a method definition
Definition parser.h:333
@ PM_CONTEXT_FOR
a for loop
Definition parser.h:357
@ PM_CONTEXT_PREDICATE
a predicate inside an if/elsif/unless statement
Definition parser.h:408
@ PM_CONTEXT_BEGIN_ENSURE
an ensure statement with an explicit begin
Definition parser.h:282
@ PM_CONTEXT_SCLASS_ENSURE
an ensure statement with a singleton class
Definition parser.h:420
@ PM_CONTEXT_DEFAULT_PARAMS
a method definition's default parameter
Definition parser.h:345
@ PM_CONTEXT_LAMBDA_ELSE
a rescue else statement within a lambda expression
Definition parser.h:375
@ PM_CONTEXT_CLASS
a class declaration
Definition parser.h:315
@ PM_CONTEXT_MAIN
the top level context
Definition parser.h:384
@ PM_CONTEXT_LAMBDA_DO_END
a lambda expression with do..end
Definition parser.h:369
@ PM_CONTEXT_BEGIN
a begin statement
Definition parser.h:279
@ PM_CONTEXT_RESCUE_MODIFIER
a modifier rescue clause
Definition parser.h:414
@ PM_CONTEXT_EMBEXPR
an interpolated expression
Definition parser.h:354
@ PM_CONTEXT_TERNARY
a ternary expression
Definition parser.h:429
@ PM_CONTEXT_DEF
a method definition
Definition parser.h:327
@ PM_CONTEXT_SCLASS_ELSE
a rescue else statement with a singleton class
Definition parser.h:423
@ PM_CONTEXT_MODULE_RESCUE
a rescue statement within a module statement
Definition parser.h:396
@ PM_CONTEXT_BLOCK_KEYWORDS
expressions in block arguments using do..end
Definition parser.h:294
@ PM_CONTEXT_WHILE
a while statement
Definition parser.h:438
uint8_t pm_scope_parameters_t
The flags about scope parameters that can be set.
Definition parser.h:569
uint8_t pm_shareable_constant_value_t
The type of shareable constant value that can be set.
Definition parser.h:525
pm_comment_type_t
This is the type of a comment that we've found while parsing.
Definition parser.h:451
#define PM_CONSTANT_ID_UNSET
When we allocate constants into the pool, we reserve 0 to mean that the slot is not yet filled.
uint32_t pm_constant_id_t
A constant id is a unique identifier for a constant in the constant pool.
struct pm_list_node pm_list_node_t
This struct represents an abstract linked list that provides common functionality.
#define PM_STRING_EMPTY
Defines an empty string.
Definition pm_string.h:70
#define PRISM_FALLTHROUGH
We use -Wimplicit-fallthrough to guard potentially unintended fall-through between cases of a switch.
Definition defines.h:253
#define PRISM_UNLIKELY(x)
The compiler should predicate that this branch will not be taken.
Definition defines.h:237
#define PRISM_ATTRIBUTE_UNUSED
GCC will warn if you specify a function or parameter that is unused at runtime.
Definition defines.h:81
#define PRISM_DEPTH_MAXIMUM
When we are parsing using recursive descent, we want to protect against malicious payloads that could...
Definition defines.h:37
#define PM_STATIC_ASSERT(line, condition, message)
We want to be able to use static assertions, but they weren't standardized until C11.
Definition defines.h:116
#define PRISM_EXPORTED_FUNCTION
By default, we compile with -fvisibility=hidden.
Definition defines.h:53
#define PM_ENCODING_US_ASCII_ENTRY
This is the US-ASCII encoding.
Definition encoding.h:252
#define PM_ENCODING_UTF_8_ENTRY
This is the default UTF-8 encoding.
Definition encoding.h:245
#define PRISM_ENCODING_ALPHABETIC_BIT
All of the lookup tables use the first bit of each embedded byte to indicate whether the codepoint is...
Definition encoding.h:68
#define PRISM_ENCODING_ALPHANUMERIC_BIT
All of the lookup tables use the second bit of each embedded byte to indicate whether the codepoint i...
Definition encoding.h:74
#define PM_NODE_LIST_FOREACH(list, index, node)
Loop through each node in the node list, writing each node to the given pm_node_t pointer.
Definition node.h:17
#define PRISM_VERSION
The version of the Prism library as a constant string.
Definition version.h:27
#define PRISM_VERSION_PATCH
The patch version of the Prism library as an int.
Definition version.h:22
#define PRISM_VERSION_MINOR
The minor version of the Prism library as an int.
Definition version.h:17
#define PRISM_VERSION_MAJOR
The major version of the Prism library as an int.
Definition version.h:12
The main header file for the prism parser.
pm_string_query_t
Represents the results of a slice query.
Definition prism.h:265
@ PM_STRING_QUERY_TRUE
Returned if the result of the slice query is true.
Definition prism.h:273
@ PM_STRING_QUERY_ERROR
Returned if the encoding given to a slice query was invalid.
Definition prism.h:267
@ PM_STRING_QUERY_FALSE
Returned if the result of the slice query is false.
Definition prism.h:270
void pm_serialize_content(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer)
Serialize the encoding, metadata, nodes, and constant pool.
Definition serialize.c:2147
int pm_parse_stream_feof_t(void *stream)
This function is used in pm_parse_stream to check whether a stream is EOF.
Definition prism.h:109
void pm_serialize_encoding(const pm_encoding_t *encoding, pm_buffer_t *buffer)
Serialize the name of the encoding to the buffer.
Definition serialize.c:2124
char * pm_parse_stream_fgets_t(char *string, int size, void *stream)
This function is used in pm_parse_stream() to retrieve a line of input from a stream.
Definition prism.h:102
void pm_serialize_comment_list(pm_parser_t *parser, pm_list_t *list, pm_buffer_t *buffer)
Serialize the given list of comments to the given buffer.
Definition serialize.c:2054
const char * pm_token_type_human(pm_token_type_t token_type)
Returns the human name of the given token type.
Definition token_type.c:364
This struct is used to pass information between the regular expression parser and the error callback.
Definition prism.c:17390
pm_parser_t * parser
The parser that we are parsing the regular expression for.
Definition prism.c:17392
const uint8_t * start
The start of the regular expression.
Definition prism.c:17395
bool shared
Whether or not the source of the regular expression is shared.
Definition prism.c:17406
const uint8_t * end
The end of the regular expression.
Definition prism.c:17398
This struct is used to pass information between the regular expression parser and the named capture c...
Definition prism.c:20402
pm_constant_id_list_t names
The list of names that have been parsed.
Definition prism.c:20413
pm_parser_t * parser
The parser that is parsing the regular expression.
Definition prism.c:20404
pm_match_write_node_t * match
The match write node that is being created.
Definition prism.c:20410
pm_call_node_t * call
The call node wrapping the regular expression node.
Definition prism.c:20407
bool shared
Whether the content of the regular expression is shared.
Definition prism.c:20420
struct pm_node * left
AndNode::left.
Definition ast.h:1288
struct pm_node * right
AndNode::right.
Definition ast.h:1301
pm_node_t base
The embedded base node.
Definition ast.h:1335
struct pm_node_list arguments
ArgumentsNode::arguments.
Definition ast.h:1346
This is a special out parameter to the parse_arguments_list function that includes opening and closin...
Definition prism.c:1586
pm_node_t * block
The optional block attached to the call.
Definition prism.c:1597
bool has_forwarding
The flag indicating whether this arguments list has forwarding argument.
Definition prism.c:1600
pm_location_t opening_loc
The optional location of the opening parenthesis or bracket.
Definition prism.c:1588
pm_arguments_node_t * arguments
The lazily-allocated optional arguments node.
Definition prism.c:1591
pm_location_t closing_loc
The optional location of the closing parenthesis or bracket.
Definition prism.c:1594
struct pm_node_list elements
ArrayNode::elements.
Definition ast.h:1374
struct pm_node * constant
ArrayPatternNode::constant.
Definition ast.h:1444
pm_location_t opening_loc
ArrayPatternNode::opening_loc.
Definition ast.h:1484
pm_node_t base
The embedded base node.
Definition ast.h:1427
pm_location_t closing_loc
ArrayPatternNode::closing_loc.
Definition ast.h:1494
struct pm_node * value
AssocNode::value.
Definition ast.h:1541
struct pm_node * key
AssocNode::key.
Definition ast.h:1528
struct pm_ensure_node * ensure_clause
BeginNode::ensure_clause.
Definition ast.h:1688
struct pm_rescue_node * rescue_clause
BeginNode::rescue_clause.
Definition ast.h:1668
struct pm_statements_node * statements
BeginNode::statements.
Definition ast.h:1658
pm_node_t base
The embedded base node.
Definition ast.h:1637
struct pm_else_node * else_clause
BeginNode::else_clause.
Definition ast.h:1678
This struct represents a set of binding powers used for a given token.
Definition prism.c:12187
bool binary
Whether or not this token can be used as a binary operator.
Definition prism.c:12195
pm_binding_power_t left
The left binding power.
Definition prism.c:12189
bool nonassoc
Whether or not this token can be used as non-associative binary operator.
Definition prism.c:12201
pm_binding_power_t right
The right binding power.
Definition prism.c:12192
A pm_buffer_t is a simple memory buffer that stores data in a contiguous block of memory.
Definition pm_buffer.h:22
size_t length
The length of the buffer in bytes.
Definition pm_buffer.h:24
char * value
A pointer to the start of the buffer.
Definition pm_buffer.h:30
pm_location_t opening_loc
CallNode::opening_loc.
Definition ast.h:2200
pm_location_t closing_loc
CallNode::closing_loc.
Definition ast.h:2220
struct pm_node * receiver
CallNode::receiver.
Definition ast.h:2158
pm_constant_id_t name
CallNode::name.
Definition ast.h:2181
pm_node_t base
The embedded base node.
Definition ast.h:2141
pm_location_t equal_loc
CallNode::equal_loc.
Definition ast.h:2233
pm_location_t call_operator_loc
CallNode::call_operator_loc.
Definition ast.h:2171
pm_location_t message_loc
CallNode::message_loc.
Definition ast.h:2191
struct pm_arguments_node * arguments
CallNode::arguments.
Definition ast.h:2210
struct pm_node * block
CallNode::block.
Definition ast.h:2243
struct pm_node_list conditions
CaseMatchNode::conditions.
Definition ast.h:2601
struct pm_node_list conditions
CaseNode::conditions.
Definition ast.h:2671
pm_location_t location
The location of the comment in the source.
Definition parser.h:466
A list of constant IDs.
size_t size
The number of constant ids in the list.
A constant in the pool which effectively stores a string.
size_t length
The length of the string.
const uint8_t * start
A pointer to the start of the string.
pm_context_t context
The context that this node represents.
Definition parser.h:444
struct pm_context_node * prev
A pointer to the previous context in the linked list.
Definition parser.h:447
This struct represents a diagnostic generated during parsing.
Definition diagnostic.h:368
struct pm_statements_node * statements
ElseNode::statements.
Definition ast.h:3686
This struct defines the functions necessary to implement the encoding interface so we can determine h...
Definition encoding.h:23
size_t(*) alpha_char(const uint8_t *b, ptrdiff_t n)
Return the number of bytes that the next character takes if it is valid in the encoding and is alphab...
Definition encoding.h:36
size_t(*) char_width(const uint8_t *b, ptrdiff_t n)
Return the number of bytes that the next character takes if it is valid in the encoding.
Definition encoding.h:29
bool(*) isupper_char(const uint8_t *b, ptrdiff_t n)
Return true if the next character is valid in the encoding and is an uppercase character.
Definition encoding.h:50
size_t(*) alnum_char(const uint8_t *b, ptrdiff_t n)
Return the number of bytes that the next character takes if it is valid in the encoding and is alphan...
Definition encoding.h:43
const char * name
The name of the encoding.
Definition encoding.h:56
struct pm_statements_node * statements
EnsureNode::statements.
Definition ast.h:3784
struct pm_node * constant
FindPatternNode::constant.
Definition ast.h:3844
pm_location_t opening_loc
FindPatternNode::opening_loc.
Definition ast.h:3896
pm_node_t base
The embedded base node.
Definition ast.h:3833
pm_location_t closing_loc
FindPatternNode::closing_loc.
Definition ast.h:3909
double value
FloatNode::value.
Definition ast.h:3970
pm_node_t base
The embedded base node.
Definition ast.h:3962
struct pm_node_list elements
HashNode::elements.
Definition ast.h:4403
pm_location_t opening_loc
HashPatternNode::opening_loc.
Definition ast.h:4492
pm_node_t base
The embedded base node.
Definition ast.h:4439
pm_location_t closing_loc
HashPatternNode::closing_loc.
Definition ast.h:4505
struct pm_node * constant
HashPatternNode::constant.
Definition ast.h:4453
All of the information necessary to store to lexing a heredoc.
Definition parser.h:88
size_t ident_length
The length of the heredoc identifier.
Definition parser.h:93
pm_heredoc_quote_t quote
The type of quote that the heredoc uses.
Definition parser.h:96
pm_heredoc_indent_t indent
The type of indentation that the heredoc uses.
Definition parser.h:99
const uint8_t * ident_start
A pointer to the start of the heredoc identifier.
Definition parser.h:90
struct pm_statements_node * statements
IfNode::statements.
Definition ast.h:4586
struct pm_node * subsequent
IfNode::subsequent.
Definition ast.h:4605
pm_integer_t value
IntegerNode::value.
Definition ast.h:5252
pm_node_t base
The embedded base node.
Definition ast.h:5244
bool negative
Whether or not the integer is negative.
Definition pm_integer.h:42
pm_node_t base
The embedded base node.
Definition ast.h:5365
pm_location_t opening_loc
InterpolatedStringNode::opening_loc.
Definition ast.h:5371
pm_node_t base
The embedded base node.
Definition ast.h:5398
pm_location_t opening_loc
InterpolatedXStringNode::opening_loc.
Definition ast.h:5437
pm_node_t base
The embedded base node.
Definition ast.h:5431
struct pm_node_list parts
InterpolatedXStringNode::parts.
Definition ast.h:5442
void(*) callback(void *data, pm_parser_t *parser, pm_token_t *token)
This is the callback that is called when a token is lexed.
Definition parser.h:521
void * data
This opaque pointer is used to provide whatever information the user deemed necessary to the callback...
Definition parser.h:515
struct pm_lex_mode * prev
The previous lex state so that it knows how to pop.
Definition parser.h:254
struct pm_list_node * next
A pointer to the next node in the list.
Definition pm_list.h:48
This represents the overall linked list.
Definition pm_list.h:55
pm_list_node_t * head
A pointer to the head of the list.
Definition pm_list.h:60
size_t size
The size of the list.
Definition pm_list.h:57
This tracks an individual local variable in a certain lexical context, as well as the number of times...
Definition parser.h:535
pm_constant_id_t name
The name of the local variable.
Definition parser.h:537
pm_location_t location
The location of the local variable in the source.
Definition parser.h:540
uint32_t hash
The hash of the local variable.
Definition parser.h:549
uint32_t index
The index of the local variable in the local table.
Definition parser.h:543
uint32_t reads
The number of times the local variable is read.
Definition parser.h:546
uint32_t depth
LocalVariableReadNode::depth.
Definition ast.h:5774
pm_constant_id_t name
LocalVariableReadNode::name.
Definition ast.h:5761
uint32_t depth
LocalVariableWriteNode::depth.
Definition ast.h:5847
pm_constant_id_t name
LocalVariableWriteNode::name.
Definition ast.h:5834
pm_local_t * locals
The nullable allocated memory for the local variables in the set.
Definition parser.h:565
uint32_t capacity
The capacity of the local variables set.
Definition parser.h:562
uint32_t size
The number of local variables in the set.
Definition parser.h:559
This represents a range of bytes in the source string to which a node or token corresponds.
Definition ast.h:544
const uint8_t * start
A pointer to the start location of the range in the source.
Definition ast.h:546
const uint8_t * end
A pointer to the end location of the range in the source.
Definition ast.h:549
This is a node in the linked list of magic comments that we've found while parsing.
Definition parser.h:478
struct pm_node_list targets
MatchWriteNode::targets.
Definition ast.h:6083
pm_node_t base
The embedded base node.
Definition ast.h:6168
pm_location_t lparen_loc
MultiTargetNode::lparen_loc.
Definition ast.h:6224
struct pm_node_list lefts
MultiTargetNode::lefts.
Definition ast.h:6184
pm_location_t rparen_loc
MultiTargetNode::rparen_loc.
Definition ast.h:6234
size_t * offsets
The list of offsets.
size_t size
The number of offsets in the list.
size_t size
The number of nodes in the list.
Definition ast.h:559
struct pm_node ** nodes
The nodes in the list.
Definition ast.h:565
pm_node_type_t type
This represents the type of the node.
Definition ast.h:1057
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1075
size_t locals_count
The number of locals in the scope.
Definition options.h:38
uint8_t forwarding
Flags for the set of forwarding parameters in this scope.
Definition options.h:44
uint8_t command_line
A bitset of the various options that were set on the command line.
Definition options.h:159
void * shebang_callback_data
Any additional data that should be passed along to the shebang callback if one was set.
Definition options.h:121
bool encoding_locked
Whether or not the encoding magic comments should be respected.
Definition options.h:175
bool main_script
When the file being parsed is the main script, the shebang will be considered for command-line flags ...
Definition options.h:182
pm_string_t encoding
The name of the encoding that the source file is in.
Definition options.h:136
int32_t line
The line within the file that the parse starts on.
Definition options.h:130
pm_options_shebang_callback_t shebang_callback
The callback to call when additional switches are found in a shebang comment.
Definition options.h:115
int8_t frozen_string_literal
Whether or not the frozen string literal option has been set.
Definition options.h:168
bool partial_script
When the file being parsed is considered a "partial" script, jumps will not be marked as errors if th...
Definition options.h:192
size_t scopes_count
The number of scopes surrounding the code that is being parsed.
Definition options.h:141
pm_string_t filepath
The name of the file that is currently being parsed.
Definition options.h:124
pm_options_version_t version
The version of prism that we should be parsing with.
Definition options.h:156
struct pm_node * left
OrNode::left.
Definition ast.h:6576
struct pm_node * right
OrNode::right.
Definition ast.h:6589
struct pm_node * rest
ParametersNode::rest.
Definition ast.h:6633
struct pm_block_parameter_node * block
ParametersNode::block.
Definition ast.h:6653
pm_node_t base
The embedded base node.
Definition ast.h:6617
struct pm_node * keyword_rest
ParametersNode::keyword_rest.
Definition ast.h:6648
struct pm_node * body
ParenthesesNode::body.
Definition ast.h:6679
const pm_encoding_t * explicit_encoding
When a string-like expression is being lexed, any byte or escape sequence that resolves to a value wh...
Definition parser.h:843
pm_lex_state_t lex_state
The current state of the lexer.
Definition parser.h:652
uint8_t command_line
The command line flags given from the options.
Definition parser.h:862
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser)
Initiate the parser with the given parser.
Definition prism.c:22339
const pm_encoding_t * encoding
The encoding functions for the current file is attached to the parser as it's parsing so that it can ...
Definition parser.h:758
bool partial_script
Whether or not we are parsing a "partial" script, which is a script that will be evaluated in the con...
Definition parser.h:885
PRISM_EXPORTED_FUNCTION void pm_parser_register_encoding_changed_callback(pm_parser_t *parser, pm_encoding_changed_callback_t callback)
Register a callback that will be called whenever prism changes the encoding it is using to parse base...
Definition prism.c:22275
bool pattern_matching_newlines
This flag indicates that we are currently parsing a pattern matching expression and impacts that calc...
Definition parser.h:912
const uint8_t * end
The pointer to the end of the source.
Definition parser.h:697
bool recovering
Whether or not we're currently recovering from a syntax error.
Definition parser.h:891
pm_node_flags_t integer_base
We want to add a flag to integer nodes that indicates their base.
Definition parser.h:800
bool warn_mismatched_indentation
By default, Ruby always warns about mismatched indentation.
Definition parser.h:933
pm_constant_pool_t constant_pool
This constant pool keeps all of the constants defined throughout the file so that we can reference th...
Definition parser.h:789
bool in_keyword_arg
This flag indicates that we are currently parsing a keyword argument.
Definition parser.h:915
PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser)
Free any memory associated with the given parser.
Definition prism.c:22313
const uint8_t * next_start
This is a special field set on the parser when we need the parser to jump to a specific location when...
Definition parser.h:710
pm_static_literals_t * current_hash_keys
The hash keys for the hash that is currently being parsed.
Definition parser.h:752
pm_list_t magic_comment_list
The list of magic comments that have been found while parsing.
Definition parser.h:724
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const pm_options_t *options)
Parse a stream of Ruby source and return the tree.
Definition prism.c:22426
int lambda_enclosure_nesting
Used to temporarily track the nesting of enclosures to determine if a { is the beginning of a lambda ...
Definition parser.h:661
pm_lex_callback_t * lex_callback
This is an optional callback that can be attached to the parser that will be called whenever a new to...
Definition parser.h:777
pm_options_version_t version
The version of prism that we should use to parse.
Definition parser.h:859
pm_token_t previous
The previous token we were considering.
Definition parser.h:700
pm_string_t current_string
This string is used to pass information from the lexer to the parser.
Definition parser.h:806
bool parsing_eval
Whether or not we are parsing an eval string.
Definition parser.h:878
bool current_regular_expression_ascii_only
True if the current regular expression being lexed contains only ASCII characters.
Definition parser.h:927
bool encoding_changed
Whether or not the encoding has been changed by a magic comment.
Definition parser.h:906
pm_location_t data_loc
An optional location that represents the location of the END marker and the rest of the content of th...
Definition parser.h:731
pm_context_node_t * current_context
The current parsing context.
Definition parser.h:743
const uint8_t * start
The pointer to the start of the source.
Definition parser.h:694
int enclosure_nesting
Tracks the current nesting of (), [], and {}.
Definition parser.h:655
pm_list_t error_list
The list of errors that have been found while parsing.
Definition parser.h:737
int8_t frozen_string_literal
Whether or not we have found a frozen_string_literal magic comment with a true or false value.
Definition parser.h:872
pm_node_list_t * current_block_exits
When parsing block exits (e.g., break, next, redo), we need to validate that they are in correct cont...
Definition parser.h:856
const uint8_t * encoding_comment_start
This pointer indicates where a comment must start if it is to be considered an encoding comment.
Definition parser.h:771
pm_token_t current
The current token we're considering.
Definition parser.h:703
pm_list_t warning_list
The list of warnings that have been found while parsing.
Definition parser.h:734
const uint8_t * heredoc_end
This field indicates the end of a heredoc whose identifier was found on the current line.
Definition parser.h:718
PRISM_EXPORTED_FUNCTION void pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options)
Initialize a parser with the given start and end pointers.
Definition prism.c:22014
int brace_nesting
Used to track the nesting of braces to ensure we get the correct value when we are interpolating bloc...
Definition parser.h:667
pm_encoding_changed_callback_t encoding_changed_callback
When the encoding that is being used to parse the source is changed by prism, we provide the ability ...
Definition parser.h:765
int32_t start_line
The line number at the start of the parse.
Definition parser.h:812
bool encoding_locked
This is very specialized behavior for when you want to parse in a context that does not respect encod...
Definition parser.h:899
pm_list_t comment_list
The list of comments that have been found while parsing.
Definition parser.h:721
pm_string_t filepath
This is the path of the file being parsed.
Definition parser.h:783
pm_scope_t * current_scope
The current local scope.
Definition parser.h:740
bool command_start
Whether or not we're at the beginning of a command.
Definition parser.h:888
pm_newline_list_t newline_list
This is the list of newline offsets in the source file.
Definition parser.h:792
bool semantic_token_seen
Whether or not the parser has seen a token that has semantic meaning (i.e., a token that is not a com...
Definition parser.h:921
struct pm_node * right
RangeNode::right.
Definition ast.h:6937
struct pm_node * left
RangeNode::left.
Definition ast.h:6923
pm_node_t base
The embedded base node.
Definition ast.h:6967
pm_integer_t numerator
RationalNode::numerator.
Definition ast.h:6977
In order to properly set a regular expression's encoding and to validate the byte sequence for the un...
Definition prism.c:9491
pm_buffer_t regexp_buffer
The buffer holding the regexp source.
Definition prism.c:9496
pm_token_buffer_t base
The embedded base buffer.
Definition prism.c:9493
pm_node_t base
The embedded base node.
Definition ast.h:7034
pm_string_t unescaped
RegularExpressionNode::unescaped.
Definition ast.h:7055
struct pm_node * rescue_expression
RescueModifierNode::rescue_expression.
Definition ast.h:7147
struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7205
pm_location_t then_keyword_loc
RescueNode::then_keyword_loc.
Definition ast.h:7195
pm_node_t base
The embedded base node.
Definition ast.h:7169
struct pm_scope * previous
A pointer to the previous scope in the linked list.
Definition parser.h:585
pm_node_list_t implicit_parameters
This is a list of the implicit parameters contained within the block.
Definition parser.h:596
pm_shareable_constant_value_t shareable_constant
The current state of constant shareability for this scope.
Definition parser.h:623
pm_locals_t locals
The IDs of the locals in the given scope.
Definition parser.h:588
pm_scope_parameters_t parameters
This is a bitfield that indicates the parameters that are being used in this scope.
Definition parser.h:617
bool closed
A boolean indicating whether or not this scope can see into its parent.
Definition parser.h:629
struct pm_node * expression
SplatNode::expression.
Definition ast.h:7480
struct pm_node_list body
StatementsNode::body.
Definition ast.h:7503
Certain sets of nodes (hash keys and when clauses) check for duplicate nodes to alert the user of pot...
pm_node_t base
The embedded base node.
Definition ast.h:7532
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7553
pm_location_t closing_loc
StringNode::closing_loc.
Definition ast.h:7548
pm_location_t opening_loc
StringNode::opening_loc.
Definition ast.h:7538
A generic string type that can have various ownership semantics.
Definition pm_string.h:33
const uint8_t * source
A pointer to the start of the string.
Definition pm_string.h:35
size_t length
The length of the string in bytes of memory.
Definition pm_string.h:38
pm_location_t value_loc
SymbolNode::value_loc.
Definition ast.h:7639
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7649
pm_node_t base
The embedded base node.
Definition ast.h:7628
When we're lexing certain types (strings, symbols, lists, etc.) we have string content associated wit...
Definition prism.c:9465
pm_buffer_t buffer
The buffer that we're using to keep track of the string content.
Definition prism.c:9470
const uint8_t * cursor
The cursor into the source string that points to how far we have currently copied into the buffer.
Definition prism.c:9476
This struct represents a token in the Ruby source.
Definition ast.h:529
const uint8_t * end
A pointer to the end location of the token in the source.
Definition ast.h:537
const uint8_t * start
A pointer to the start location of the token in the source.
Definition ast.h:534
pm_token_type_t type
The type of the token.
Definition ast.h:531
struct pm_statements_node * statements
UnlessNode::statements.
Definition ast.h:7763
struct pm_else_node * else_clause
UnlessNode::else_clause.
Definition ast.h:7773