Ruby 4.1.0dev (2026-10-30 revision 7704181e64db25a20de9d5e89ff9661af4fd1820)
prism.c
5
6#include "prism/internal/allocator.h"
7#include "prism/internal/arena.h"
8#include "prism/internal/bit.h"
9#include "prism/internal/buffer.h"
10#include "prism/internal/char.h"
11#include "prism/internal/comments.h"
12#include "prism/internal/constant_pool.h"
13#include "prism/internal/diagnostic.h"
14#include "prism/internal/encoding.h"
15#include "prism/internal/integer.h"
16#include "prism/internal/isinf.h"
17#include "prism/internal/line_offset_list.h"
18#include "prism/internal/list.h"
19#include "prism/internal/magic_comments.h"
20#include "prism/internal/memchr.h"
21#include "prism/internal/node.h"
22#include "prism/internal/oom.h"
23#include "prism/internal/options.h"
24#include "prism/internal/parser.h"
25#include "prism/internal/regexp.h"
26#include "prism/internal/serialize.h"
27#include "prism/internal/source.h"
28#include "prism/internal/static_literals.h"
29#include "prism/internal/stringy.h"
30#include "prism/internal/strncasecmp.h"
31#include "prism/internal/strpbrk.h"
32#include "prism/internal/tokens.h"
33
34#include "prism/excludes.h"
35#include "prism/serialize.h"
36#include "prism/stream.h"
37#include "prism/version.h"
38
39#include <assert.h>
40#include <errno.h>
41#include <limits.h>
42#include <locale.h>
43#include <math.h>
44#include <stdio.h>
45#include <stdlib.h>
46
52#ifndef PRISM_DEPTH_MAXIMUM
53 #define PRISM_DEPTH_MAXIMUM 10000
54#endif
55
60#define PM_CONCATENATE(left, right) left ## right
61
67#if defined(_Static_assert)
68# define PM_STATIC_ASSERT(line, condition, message) _Static_assert(condition, message)
69#else
70# define PM_STATIC_ASSERT(line, condition, message) typedef char PM_CONCATENATE(static_assert_, line)[(condition) ? 1 : -1]
71#endif
72
76const char *
77pm_version(void) {
78 return PRISM_VERSION;
79}
80
85#define PM_TAB_WHITESPACE_SIZE 8
86
87// Macros for min/max.
88#define MIN(a,b) (((a)<(b))?(a):(b))
89#define MAX(a,b) (((a)>(b))?(a):(b))
90
91/******************************************************************************/
92/* Helpful AST-related macros */
93/******************************************************************************/
94
95#define U32(value_) ((uint32_t) (value_))
96
97#define FL PM_NODE_FLAGS
98#define UP PM_NODE_UPCAST
99
100#define PM_TOKEN_START(parser_, token_) U32((token_)->start - (parser_)->start)
101#define PM_TOKEN_END(parser_, token_) U32((token_)->end - (parser_)->start)
102#define PM_TOKEN_LENGTH(token_) U32((token_)->end - (token_)->start)
103#define PM_TOKENS_LENGTH(left_, right_) U32((right_)->end - (left_)->start)
104
105#define PM_NODE_START(node_) (UP(node_)->location.start)
106#define PM_NODE_LENGTH(node_) (UP(node_)->location.length)
107#define PM_NODE_END(node_) (UP(node_)->location.start + UP(node_)->location.length)
108#define PM_NODES_LENGTH(left_, right_) (PM_NODE_END(right_) - PM_NODE_START(left_))
109
110#define PM_TOKEN_NODE_LENGTH(parser_, token_, node_) (PM_NODE_END(node_) - PM_TOKEN_START(parser_, token_))
111#define PM_NODE_TOKEN_LENGTH(parser_, node_, token_) (PM_TOKEN_END(parser_, token_) - PM_NODE_START(node_))
112
113#define PM_NODE_START_SET_NODE(left_, right_) (PM_NODE_START(left_) = PM_NODE_START(right_))
114#define PM_NODE_START_SET_TOKEN(parser_, node_, token_) (PM_NODE_START(node_) = PM_TOKEN_START(parser_, token_))
115#define PM_NODE_LENGTH_SET_NODE(left_, right_) (PM_NODE_LENGTH(left_) = PM_NODE_END(right_) - PM_NODE_START(left_))
116#define PM_NODE_LENGTH_SET_TOKEN(parser_, node_, token_) (PM_NODE_LENGTH(node_) = PM_TOKEN_END(parser_, token_) - PM_NODE_START(node_))
117#define PM_NODE_LENGTH_SET_LOCATION(node_, location_) (PM_NODE_LENGTH(node_) = PM_LOCATION_END(location_) - PM_NODE_START(node_))
118
127pm_location_init(uint32_t start, uint32_t length) {
128 pm_location_t location = { .start = start, .length = length };
129 return location;
130}
131
132#define PM_LOCATION_INIT(start_, length_) pm_location_init((start_), (length_))
133#define PM_LOCATION_INIT_UNSET PM_LOCATION_INIT(0, 0)
134#define PM_LOCATION_INIT_TOKEN(parser_, token_) PM_LOCATION_INIT(PM_TOKEN_START(parser_, token_), PM_TOKEN_LENGTH(token_))
135#define PM_LOCATION_INIT_NODE(node_) UP(node_)->location
136
137#define PM_LOCATION_INIT_TOKENS(parser_, left_, right_) PM_LOCATION_INIT(PM_TOKEN_START(parser_, left_), PM_TOKENS_LENGTH(left_, right_))
138#define PM_LOCATION_INIT_NODES(left_, right_) PM_LOCATION_INIT(PM_NODE_START(left_), PM_NODES_LENGTH(left_, right_))
139#define PM_LOCATION_INIT_TOKEN_NODE(parser_, token_, node_) PM_LOCATION_INIT(PM_TOKEN_START(parser_, token_), PM_TOKEN_NODE_LENGTH(parser_, token_, node_))
140#define PM_LOCATION_INIT_NODE_TOKEN(parser_, node_, token_) PM_LOCATION_INIT(PM_NODE_START(node_), PM_NODE_TOKEN_LENGTH(parser_, node_, token_))
141
142#define TOK2LOC(parser_, token_) PM_LOCATION_INIT_TOKEN(parser_, token_)
143#define NTOK2LOC(parser_, token_) ((token_) == NULL ? PM_LOCATION_INIT_UNSET : TOK2LOC(parser_, token_))
144#define NTOK2PTR(token_) ((token_).start == NULL ? NULL : &(token_))
145
146/******************************************************************************/
147/* Lex mode manipulations */
148/******************************************************************************/
149
154static PRISM_INLINE uint8_t
155lex_mode_incrementor(const uint8_t start) {
156 switch (start) {
157 case '(':
158 case '[':
159 case '{':
160 case '<':
161 return start;
162 default:
163 return '\0';
164 }
165}
166
171static PRISM_INLINE uint8_t
172lex_mode_terminator(const uint8_t start) {
173 switch (start) {
174 case '(':
175 return ')';
176 case '[':
177 return ']';
178 case '{':
179 return '}';
180 case '<':
181 return '>';
182 default:
183 return start;
184 }
185}
186
191static void
192lex_mode_push(pm_parser_t *parser, pm_lex_mode_t lex_mode) {
193 pm_lex_mode_t *next = parser->lex_modes.free;
194
195 if (next != NULL) {
196 parser->lex_modes.free = next->prev;
197 } else {
198 next = (pm_lex_mode_t *) pm_arena_alloc(&parser->scratch_arena, sizeof(pm_lex_mode_t), PRISM_ALIGNOF(pm_lex_mode_t));
199 }
200
201 lex_mode.prev = parser->lex_modes.current;
202 *next = lex_mode;
203 parser->lex_modes.current = next;
204}
205
209static PRISM_INLINE void
210lex_mode_push_list(pm_parser_t *parser, bool interpolation, uint8_t delimiter) {
211 uint8_t incrementor = lex_mode_incrementor(delimiter);
212 uint8_t terminator = lex_mode_terminator(delimiter);
213
214 pm_lex_mode_t lex_mode = {
215 .mode = PM_LEX_LIST,
216 .as.list = {
217 .nesting = 0,
218 .interpolation = interpolation,
219 .started = false,
220 .separated = false,
221 .incrementor = incrementor,
222 .terminator = terminator
223 }
224 };
225
226 // These are the places where we need to split up the content of the list.
227 // We'll use strpbrk to find the first of these characters.
228 uint8_t *breakpoints = lex_mode.as.list.breakpoints;
229 memset(breakpoints, 0, PM_STRPBRK_CACHE_SIZE);
230 memcpy(breakpoints, "\\ \t\f\r\v\n", sizeof("\\ \t\f\r\v\n") - 1);
231 size_t index = 7;
232
233 // Now we'll add the terminator to the list of breakpoints. If the
234 // terminator is not already a NULL byte, add it to the list.
235 if (terminator != '\0') {
236 breakpoints[index++] = terminator;
237 }
238
239 // If interpolation is allowed, then we're going to check for the #
240 // character. Otherwise we'll only look for escapes and the terminator.
241 if (interpolation) {
242 breakpoints[index++] = '#';
243 }
244
245 // If there is an incrementor, then we'll check for that as well.
246 if (incrementor != '\0') {
247 breakpoints[index++] = incrementor;
248 }
249
250 parser->explicit_encoding = NULL;
251 lex_mode_push(parser, lex_mode);
252}
253
259static PRISM_INLINE void
260lex_mode_push_list_eof(pm_parser_t *parser) {
261 lex_mode_push_list(parser, false, '\0');
262}
263
267static PRISM_INLINE void
268lex_mode_push_regexp(pm_parser_t *parser, uint8_t incrementor, uint8_t terminator) {
269 pm_lex_mode_t lex_mode = {
270 .mode = PM_LEX_REGEXP,
271 .as.regexp = {
272 .nesting = 0,
273 .incrementor = incrementor,
274 .terminator = terminator
275 }
276 };
277
278 // These are the places where we need to split up the content of the
279 // regular expression. We'll use strpbrk to find the first of these
280 // characters.
281 uint8_t *breakpoints = lex_mode.as.regexp.breakpoints;
282 memset(breakpoints, 0, PM_STRPBRK_CACHE_SIZE);
283 memcpy(breakpoints, "\r\n\\#", sizeof("\r\n\\#") - 1);
284 size_t index = 4;
285
286 // First we'll add the terminator.
287 if (terminator != '\0') {
288 breakpoints[index++] = terminator;
289 }
290
291 // Next, if there is an incrementor, then we'll check for that as well.
292 if (incrementor != '\0') {
293 breakpoints[index++] = incrementor;
294 }
295
296 parser->explicit_encoding = NULL;
297 lex_mode_push(parser, lex_mode);
298}
299
303static PRISM_INLINE void
304lex_mode_push_string(pm_parser_t *parser, bool interpolation, bool label_allowed, uint8_t incrementor, uint8_t terminator) {
305 pm_lex_mode_t lex_mode = {
306 .mode = PM_LEX_STRING,
307 .as.string = {
308 .nesting = 0,
309 .interpolation = interpolation,
310 .label_allowed = label_allowed,
311 .incrementor = incrementor,
312 .terminator = terminator
313 }
314 };
315
316 // These are the places where we need to split up the content of the
317 // string. We'll use strpbrk to find the first of these characters.
318 uint8_t *breakpoints = lex_mode.as.string.breakpoints;
319 memset(breakpoints, 0, PM_STRPBRK_CACHE_SIZE);
320 memcpy(breakpoints, "\r\n\\", sizeof("\r\n\\") - 1);
321 size_t index = 3;
322
323 // Now add in the terminator. If the terminator is not already a NULL byte,
324 // then we'll add it.
325 if (terminator != '\0') {
326 breakpoints[index++] = terminator;
327 }
328
329 // If interpolation is allowed, then we're going to check for the #
330 // character. Otherwise we'll only look for escapes and the terminator.
331 if (interpolation) {
332 breakpoints[index++] = '#';
333 }
334
335 // If we have an incrementor, then we'll add that in as a breakpoint as
336 // well.
337 if (incrementor != '\0') {
338 breakpoints[index++] = incrementor;
339 }
340
341 parser->explicit_encoding = NULL;
342 lex_mode_push(parser, lex_mode);
343}
344
350static PRISM_INLINE void
351lex_mode_push_string_eof(pm_parser_t *parser) {
352 lex_mode_push_string(parser, false, false, '\0', '\0');
353}
354
360static void
361lex_mode_pop(pm_parser_t *parser) {
362 pm_lex_mode_t *popped = parser->lex_modes.current;
363
364 if (popped == &parser->lex_modes.base) {
365 popped->mode = PM_LEX_DEFAULT;
366 } else {
367 parser->lex_modes.current = popped->prev;
368 popped->prev = parser->lex_modes.free;
369 parser->lex_modes.free = popped;
370 }
371}
372
376static PRISM_INLINE bool
377lex_state_p(const pm_parser_t *parser, pm_lex_state_t state) {
378 return parser->lex_state & state;
379}
380
381typedef enum {
382 PM_IGNORED_NEWLINE_NONE = 0,
383 PM_IGNORED_NEWLINE_ALL,
384 PM_IGNORED_NEWLINE_PATTERN
385} pm_ignored_newline_type_t;
386
387static PRISM_INLINE pm_ignored_newline_type_t
388lex_state_ignored_p(pm_parser_t *parser) {
389 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);
390
391 if (ignored) {
392 return PM_IGNORED_NEWLINE_ALL;
393 } else if ((parser->lex_state & ~((unsigned int) PM_LEX_STATE_LABEL)) == (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)) {
394 return PM_IGNORED_NEWLINE_PATTERN;
395 } else {
396 return PM_IGNORED_NEWLINE_NONE;
397 }
398}
399
400static PRISM_INLINE bool
401lex_state_beg_p(pm_parser_t *parser) {
402 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));
403}
404
405static PRISM_INLINE bool
406lex_state_arg_p(pm_parser_t *parser) {
407 return lex_state_p(parser, PM_LEX_STATE_ARG_ANY);
408}
409
410static PRISM_INLINE bool
411lex_state_spcarg_p(pm_parser_t *parser, bool space_seen) {
412 if (parser->current.end >= parser->end) {
413 return false;
414 }
415 return lex_state_arg_p(parser) && space_seen && !pm_char_is_whitespace(*parser->current.end);
416}
417
418static PRISM_INLINE bool
419lex_state_end_p(pm_parser_t *parser) {
420 return lex_state_p(parser, PM_LEX_STATE_END_ANY);
421}
422
426static PRISM_INLINE bool
427lex_state_operator_p(pm_parser_t *parser) {
428 return lex_state_p(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_DOT);
429}
430
435static PRISM_INLINE void
436lex_state_set(pm_parser_t *parser, pm_lex_state_t state) {
437 parser->lex_state = state;
438}
439
440#ifndef PM_DEBUG_LOGGING
445#define PM_DEBUG_LOGGING 0
446#endif
447
448#if PM_DEBUG_LOGGING
449PRISM_UNUSED static void
450debug_state(pm_parser_t *parser) {
451 fprintf(stderr, "STATE: ");
452 bool first = true;
453
454 if (parser->lex_state == PM_LEX_STATE_NONE) {
455 fprintf(stderr, "NONE\n");
456 return;
457 }
458
459#define CHECK_STATE(state) \
460 if (parser->lex_state & state) { \
461 if (!first) fprintf(stderr, "|"); \
462 fprintf(stderr, "%s", #state); \
463 first = false; \
464 }
465
466 CHECK_STATE(PM_LEX_STATE_BEG)
467 CHECK_STATE(PM_LEX_STATE_END)
468 CHECK_STATE(PM_LEX_STATE_ENDARG)
469 CHECK_STATE(PM_LEX_STATE_ENDFN)
470 CHECK_STATE(PM_LEX_STATE_ARG)
471 CHECK_STATE(PM_LEX_STATE_CMDARG)
472 CHECK_STATE(PM_LEX_STATE_MID)
473 CHECK_STATE(PM_LEX_STATE_FNAME)
474 CHECK_STATE(PM_LEX_STATE_DOT)
475 CHECK_STATE(PM_LEX_STATE_CLASS)
476 CHECK_STATE(PM_LEX_STATE_LABEL)
477 CHECK_STATE(PM_LEX_STATE_LABELED)
478 CHECK_STATE(PM_LEX_STATE_FITEM)
479
480#undef CHECK_STATE
481
482 fprintf(stderr, "\n");
483}
484
485static void
486debug_lex_state_set(pm_parser_t *parser, pm_lex_state_t state, char const * caller_name, int line_number) {
487 fprintf(stderr, "Caller: %s:%d\nPrevious: ", caller_name, line_number);
488 debug_state(parser);
489 lex_state_set(parser, state);
490 fprintf(stderr, "Now: ");
491 debug_state(parser);
492 fprintf(stderr, "\n");
493}
494
495#define lex_state_set(parser, state) debug_lex_state_set(parser, state, __func__, __LINE__)
496#endif
497
498/******************************************************************************/
499/* Command-line macro helpers */
500/******************************************************************************/
501
503#define PM_PARSER_COMMAND_LINE_OPTION(parser, option) ((parser)->command_line & (option))
504
506#define PM_PARSER_COMMAND_LINE_OPTION_A(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_A)
507
509#define PM_PARSER_COMMAND_LINE_OPTION_E(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_E)
510
512#define PM_PARSER_COMMAND_LINE_OPTION_L(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_L)
513
515#define PM_PARSER_COMMAND_LINE_OPTION_N(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_N)
516
518#define PM_PARSER_COMMAND_LINE_OPTION_P(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_P)
519
521#define PM_PARSER_COMMAND_LINE_OPTION_X(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_X)
522
523/******************************************************************************/
524/* Diagnostic-related functions */
525/******************************************************************************/
526
530static PRISM_INLINE void
531pm_parser_err(pm_parser_t *parser, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id) {
532 pm_diagnostic_list_append(&parser->metadata_arena, &parser->error_list, start, length, diag_id);
533}
534
539static PRISM_INLINE void
540pm_parser_err_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) {
541 pm_parser_err(parser, PM_TOKEN_START(parser, token), PM_TOKEN_LENGTH(token), diag_id);
542}
543
548static PRISM_INLINE void
549pm_parser_err_current(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
550 pm_parser_err_token(parser, &parser->current, diag_id);
551}
552
557static PRISM_INLINE void
558pm_parser_err_previous(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
559 pm_parser_err_token(parser, &parser->previous, diag_id);
560}
561
566static PRISM_INLINE void
567pm_parser_err_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) {
568 pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), diag_id);
569}
570
574#define PM_PARSER_ERR_FORMAT(parser_, start_, length_, diag_id_, ...) \
575 pm_diagnostic_list_append_format(&(parser_)->metadata_arena, &(parser_)->error_list, start_, length_, diag_id_, __VA_ARGS__)
576
581#define PM_PARSER_ERR_NODE_FORMAT(parser_, node_, diag_id_, ...) \
582 PM_PARSER_ERR_FORMAT(parser_, PM_NODE_START(node_), PM_NODE_LENGTH(node_), diag_id_, __VA_ARGS__)
583
588#define PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser_, node_, diag_id_) \
589 PM_PARSER_ERR_NODE_FORMAT(parser_, node_, diag_id_, (int) PM_NODE_LENGTH(node_), (const char *) (parser_->start + PM_NODE_START(node_)))
590
595#define PM_PARSER_ERR_TOKEN_FORMAT(parser_, token_, diag_id, ...) \
596 PM_PARSER_ERR_FORMAT(parser_, PM_TOKEN_START(parser_, token_), PM_TOKEN_LENGTH(token_), diag_id, __VA_ARGS__)
597
602#define PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser_, token_, diag_id_) \
603 PM_PARSER_ERR_TOKEN_FORMAT(parser_, token_, diag_id_, (int) PM_TOKEN_LENGTH(token_), (const char *) (token_)->start)
604
608static PRISM_INLINE void
609pm_parser_warn(pm_parser_t *parser, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id) {
610 pm_diagnostic_list_append(&parser->metadata_arena, &parser->warning_list, start, length, diag_id);
611}
612
617static PRISM_INLINE void
618pm_parser_warn_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) {
619 pm_parser_warn(parser, PM_TOKEN_START(parser, token), PM_TOKEN_LENGTH(token), diag_id);
620}
621
626static PRISM_INLINE void
627pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) {
628 pm_parser_warn(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), diag_id);
629}
630
635#define PM_PARSER_WARN_FORMAT(parser_, start_, length_, diag_id_, ...) \
636 pm_diagnostic_list_append_format(&(parser_)->metadata_arena, &(parser_)->warning_list, start_, length_, diag_id_, __VA_ARGS__)
637
642#define PM_PARSER_WARN_TOKEN_FORMAT(parser_, token_, diag_id_, ...) \
643 PM_PARSER_WARN_FORMAT(parser_, PM_TOKEN_START(parser_, token_), PM_TOKEN_LENGTH(token_), diag_id_, __VA_ARGS__)
644
649#define PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser_, token_, diag_id_) \
650 PM_PARSER_WARN_TOKEN_FORMAT(parser_, token_, diag_id_, (int) PM_TOKEN_LENGTH(token_), (const char *) (token_)->start)
651
656#define PM_PARSER_WARN_NODE_FORMAT(parser_, node_, diag_id_, ...) \
657 PM_PARSER_WARN_FORMAT(parser_, PM_NODE_START(node_), PM_NODE_LENGTH(node_), diag_id_, __VA_ARGS__)
658
664static void
665pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
666 PM_PARSER_ERR_FORMAT(
667 parser,
668 U32(ident_start - parser->start),
669 U32(ident_length),
670 PM_ERR_HEREDOC_TERM,
671 (int) ident_length,
672 (const char *) ident_start
673 );
674}
675
676/******************************************************************************/
677/* Scope-related functions */
678/******************************************************************************/
679
683#define PM_LOCALS_REUSE_CAPACITY 16
684
688static void
689pm_parser_scope_push(pm_parser_t *parser, bool closed) {
690 pm_scope_t *scope = parser->free_scopes;
691 pm_locals_t locals = { 0 };
692
693 if (scope != NULL) {
694 parser->free_scopes = scope->previous;
695
696 /* Reuse the locals table of the popped scope when it is small, so that
697 * most scopes allocate no locals storage at all. Large tables are
698 * dropped instead, because every scope that reuses a table pays to
699 * clear it and to scan its full capacity when the locals are ordered. */
700 if (scope->locals.capacity > 0 && scope->locals.capacity <= PM_LOCALS_REUSE_CAPACITY) {
701 locals.capacity = scope->locals.capacity;
702 locals.locals = scope->locals.locals;
703 memset(locals.locals, 0, locals.capacity * sizeof(pm_local_t));
704 }
705 } else {
706 scope = (pm_scope_t *) pm_arena_alloc(&parser->scratch_arena, sizeof(pm_scope_t), PRISM_ALIGNOF(pm_scope_t));
707 }
708
709 *scope = (pm_scope_t) {
710 .previous = parser->current_scope,
711 .locals = locals,
712 .parameters = PM_SCOPE_PARAMETERS_NONE,
713 .implicit_parameters = { 0 },
714 .shareable_constant = parser->current_scope == NULL ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant,
715 .closed = closed
716 };
717
718 parser->current_scope = scope;
719}
720
725static bool
726pm_parser_scope_toplevel_p(pm_parser_t *parser) {
727 pm_scope_t *scope = parser->current_scope;
728
729 do {
730 if (scope->previous == NULL) return true;
731 if (scope->closed) return false;
732 } while ((scope = scope->previous) != NULL);
733
734 assert(false && "unreachable");
735 return true;
736}
737
741static pm_scope_t *
742pm_parser_scope_find(pm_parser_t *parser, uint32_t depth) {
743 pm_scope_t *scope = parser->current_scope;
744
745 while (depth-- > 0) {
746 assert(scope != NULL);
747 scope = scope->previous;
748 }
749
750 return scope;
751}
752
753typedef enum {
754 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS,
755 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT,
756 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL
757} pm_scope_forwarding_param_check_result_t;
758
759static pm_scope_forwarding_param_check_result_t
760pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const uint8_t mask) {
761 pm_scope_t *scope = parser->current_scope;
762 bool conflict = false;
763
764 while (scope != NULL) {
765 if (scope->parameters & mask) {
766 if (scope->closed) {
767 if (conflict) {
768 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT;
769 } else {
770 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS;
771 }
772 }
773
774 conflict = true;
775 }
776
777 if (scope->closed) break;
778 scope = scope->previous;
779 }
780
781 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL;
782}
783
784static void
785pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) {
786 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK)) {
787 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
788 // Pass.
789 break;
790 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
791 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_AMPERSAND);
792 break;
793 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
794 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND);
795 break;
796 }
797}
798
799static void
800pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) {
801 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS)) {
802 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
803 // Pass.
804 break;
805 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
806 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR);
807 break;
808 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
809 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR);
810 break;
811 }
812}
813
814static void
815pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t *token) {
816 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_ALL)) {
817 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
818 // Pass.
819 break;
820 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
821 // This shouldn't happen, because ... is not allowed in the
822 // declaration of blocks. If we get here, we assume we already have
823 // an error for this.
824 break;
825 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
826 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES);
827 break;
828 }
829}
830
831static void
832pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) {
833 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS)) {
834 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
835 // Pass.
836 break;
837 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
838 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR_STAR);
839 break;
840 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
841 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR);
842 break;
843 }
844}
845
849static PRISM_INLINE pm_shareable_constant_value_t
850pm_parser_scope_shareable_constant_get(pm_parser_t *parser) {
851 return parser->current_scope->shareable_constant;
852}
853
858static void
859pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constant_value_t shareable_constant) {
860 pm_scope_t *scope = parser->current_scope;
861
862 do {
863 scope->shareable_constant = shareable_constant;
864 } while (!scope->closed && (scope = scope->previous) != NULL);
865}
866
867/******************************************************************************/
868/* Local variable-related functions */
869/******************************************************************************/
870
874#define PM_LOCALS_HASH_THRESHOLD 5
875
880static void
881pm_locals_resize(pm_arena_t *arena, pm_locals_t *locals) {
882 uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2);
883 assert(next_capacity > locals->capacity);
884
885 pm_local_t *next_locals = (pm_local_t *) pm_arena_zalloc(arena, next_capacity * sizeof(pm_local_t), PRISM_ALIGNOF(pm_local_t));
886
887 if (next_capacity < PM_LOCALS_HASH_THRESHOLD) {
888 if (locals->size > 0) {
889 memcpy(next_locals, locals->locals, locals->size * sizeof(pm_local_t));
890 }
891 } else {
892 // If we just switched from a list to a hash, then we need to fill in
893 // the hash values of all of the locals.
894 bool hash_needed = (locals->capacity <= PM_LOCALS_HASH_THRESHOLD);
895 uint32_t mask = next_capacity - 1;
896
897 for (uint32_t index = 0; index < locals->capacity; index++) {
898 pm_local_t *local = &locals->locals[index];
899
900 if (local->name != PM_CONSTANT_ID_UNSET) {
901 if (hash_needed) local->hash = pm_constant_id_hash(local->name);
902
903 uint32_t hash = local->hash;
904 while (next_locals[hash & mask].name != PM_CONSTANT_ID_UNSET) hash++;
905 next_locals[hash & mask] = *local;
906 }
907 }
908 }
909
910 locals->locals = next_locals;
911 locals->capacity = next_capacity;
912}
913
929static bool
930pm_locals_write(pm_arena_t *arena, pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint32_t length, uint32_t reads) {
931 if (locals->size >= (locals->capacity / 4 * 3)) {
932 pm_locals_resize(arena, locals);
933 }
934
935 locals->bloom |= (1u << (name & 31));
936
937 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
938 for (uint32_t index = 0; index < locals->capacity; index++) {
939 pm_local_t *local = &locals->locals[index];
940
941 if (local->name == PM_CONSTANT_ID_UNSET) {
942 *local = (pm_local_t) {
943 .name = name,
944 .location = { .start = start, .length = length },
945 .index = locals->size++,
946 .reads = reads,
947 .hash = 0
948 };
949 return true;
950 } else if (local->name == name) {
951 return false;
952 }
953 }
954 } else {
955 uint32_t mask = locals->capacity - 1;
956 uint32_t hash = pm_constant_id_hash(name);
957 uint32_t initial_hash = hash;
958
959 do {
960 pm_local_t *local = &locals->locals[hash & mask];
961
962 if (local->name == PM_CONSTANT_ID_UNSET) {
963 *local = (pm_local_t) {
964 .name = name,
965 .location = { .start = start, .length = length },
966 .index = locals->size++,
967 .reads = reads,
968 .hash = initial_hash
969 };
970 return true;
971 } else if (local->name == name) {
972 return false;
973 } else {
974 hash++;
975 }
976 } while ((hash & mask) != initial_hash);
977 }
978
979 assert(false && "unreachable");
980 return true;
981}
982
987static uint32_t
988pm_locals_find(pm_locals_t *locals, pm_constant_id_t name) {
989 if (!(locals->bloom & (1u << (name & 31)))) return UINT32_MAX;
990
991 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
992 for (uint32_t index = 0; index < locals->size; index++) {
993 pm_local_t *local = &locals->locals[index];
994 if (local->name == name) return index;
995 }
996 } else {
997 uint32_t mask = locals->capacity - 1;
998 uint32_t hash = pm_constant_id_hash(name);
999 uint32_t initial_hash = hash & mask;
1000
1001 do {
1002 pm_local_t *local = &locals->locals[hash & mask];
1003
1004 if (local->name == PM_CONSTANT_ID_UNSET) {
1005 return UINT32_MAX;
1006 } else if (local->name == name) {
1007 return hash & mask;
1008 } else {
1009 hash++;
1010 }
1011 } while ((hash & mask) != initial_hash);
1012 }
1013
1014 return UINT32_MAX;
1015}
1016
1021static void
1022pm_locals_read(pm_locals_t *locals, pm_constant_id_t name) {
1023 uint32_t index = pm_locals_find(locals, name);
1024 assert(index != UINT32_MAX);
1025
1026 pm_local_t *local = &locals->locals[index];
1027 assert(local->reads < UINT32_MAX);
1028
1029 local->reads++;
1030}
1031
1036static void
1037pm_locals_unread(pm_locals_t *locals, pm_constant_id_t name) {
1038 uint32_t index = pm_locals_find(locals, name);
1039 assert(index != UINT32_MAX);
1040
1041 pm_local_t *local = &locals->locals[index];
1042 assert(local->reads > 0);
1043
1044 local->reads--;
1045}
1046
1050static uint32_t
1051pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) {
1052 uint32_t index = pm_locals_find(locals, name);
1053 assert(index != UINT32_MAX);
1054
1055 return locals->locals[index].reads;
1056}
1057
1066static void
1067pm_locals_order(pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) {
1068 pm_constant_id_list_init_capacity(parser->arena, list, locals->size);
1069
1070 // If we're still below the threshold for switching to a hash, then we only
1071 // need to loop over the locals until we hit the size because the locals are
1072 // stored in a list.
1073 uint32_t capacity = locals->capacity < PM_LOCALS_HASH_THRESHOLD ? locals->size : locals->capacity;
1074
1075 // We will only warn for unused variables if we're not at the top level, or
1076 // if we're parsing a file outside of eval or -e.
1077 bool warn_unused = !toplevel || (!parser->parsing_eval && !PM_PARSER_COMMAND_LINE_OPTION_E(parser));
1078
1079 for (uint32_t index = 0; index < capacity; index++) {
1080 pm_local_t *local = &locals->locals[index];
1081
1082 if (local->name != PM_CONSTANT_ID_UNSET) {
1083 pm_constant_id_list_insert(list, (size_t) local->index, local->name);
1084
1085 if (warn_unused && local->reads == 0 && ((parser->start_line >= 0) || (pm_line_offset_list_line(&parser->line_offsets, local->location.start, parser->start_line) >= 0))) {
1086 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name);
1087
1088 if (constant->length >= 1 && *constant->start != '_') {
1089 PM_PARSER_WARN_FORMAT(
1090 parser,
1091 local->location.start,
1092 local->location.length,
1093 PM_WARN_UNUSED_LOCAL_VARIABLE,
1094 (int) constant->length,
1095 (const char *) constant->start
1096 );
1097 }
1098 }
1099 }
1100 }
1101}
1102
1103/******************************************************************************/
1104/* Node-related functions */
1105/******************************************************************************/
1106
1111pm_parser_constant_id_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
1112 /* Fast path: if this is the same token as the last lookup (same pointer
1113 * range), return the cached result. */
1114 if (start == parser->constant_cache.start && end == parser->constant_cache.end) {
1115 return parser->constant_cache.id;
1116 }
1117
1118 pm_constant_id_t id = pm_constant_pool_insert_shared(&parser->metadata_arena, &parser->constant_pool, start, (size_t) (end - start));
1119
1120 parser->constant_cache.start = start;
1121 parser->constant_cache.end = end;
1122 parser->constant_cache.id = id;
1123
1124 return id;
1125}
1126
1131pm_parser_constant_id_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
1132 return pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, start, length);
1133}
1134
1139pm_parser_constant_id_constant(pm_parser_t *parser, const char *start, size_t length) {
1140 return pm_constant_pool_insert_constant(&parser->metadata_arena, &parser->constant_pool, (const uint8_t *) start, length);
1141}
1142
1147pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
1148 return pm_parser_constant_id_raw(parser, token->start, token->end);
1149}
1150
1155#define PM_CASE_VOID_VALUE PM_RETURN_NODE: case PM_BREAK_NODE: case PM_NEXT_NODE: \
1156 case PM_REDO_NODE: case PM_RETRY_NODE: case PM_MATCH_REQUIRED_NODE
1157
1163static pm_node_t *
1164pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
1165 pm_node_t *void_node = NULL;
1166
1167 while (node != NULL) {
1168 switch (PM_NODE_TYPE(node)) {
1169 case PM_CASE_VOID_VALUE:
1170 return void_node != NULL ? void_node : node;
1171 case PM_MATCH_PREDICATE_NODE:
1172 return NULL;
1173 case PM_BEGIN_NODE: {
1175
1176 if (cast->ensure_clause != NULL) {
1177 if (cast->rescue_clause != NULL) {
1178 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->rescue_clause));
1179 if (vn != NULL) return vn;
1180 }
1181
1182 if (cast->statements != NULL) {
1183 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1184 if (vn != NULL) return vn;
1185 }
1186
1187 node = UP(cast->ensure_clause);
1188 } else if (cast->rescue_clause != NULL) {
1189 // https://bugs.ruby-lang.org/issues/21669
1190 if (cast->else_clause == NULL || parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1191 if (cast->statements == NULL) return NULL;
1192
1193 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1194 if (vn == NULL) return NULL;
1195 if (void_node == NULL) void_node = vn;
1196 }
1197
1198 for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) {
1199 pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements));
1200
1201 if (vn == NULL) {
1202 // https://bugs.ruby-lang.org/issues/21669
1203 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1204 return NULL;
1205 }
1206 void_node = NULL;
1207 break;
1208 }
1209 }
1210
1211 if (cast->else_clause != NULL) {
1212 node = UP(cast->else_clause);
1213
1214 // https://bugs.ruby-lang.org/issues/21669
1215 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1216 pm_node_t *vn = pm_check_value_expression(parser, node);
1217 if (vn != NULL) return vn;
1218 }
1219 } else {
1220 return void_node;
1221 }
1222 } else {
1223 node = UP(cast->statements);
1224 }
1225
1226 break;
1227 }
1228 case PM_CASE_NODE: {
1229 // https://bugs.ruby-lang.org/issues/21669
1230 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1231 return NULL;
1232 }
1233
1235 if (cast->else_clause == NULL) return NULL;
1236
1237 pm_node_t *condition;
1238 PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1239 assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
1240
1241 pm_when_node_t *cast = (pm_when_node_t *) condition;
1242 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1243 if (vn == NULL) return NULL;
1244 if (void_node == NULL) void_node = vn;
1245 }
1246
1247 node = UP(cast->else_clause);
1248 break;
1249 }
1250 case PM_CASE_MATCH_NODE: {
1251 // https://bugs.ruby-lang.org/issues/21669
1252 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1253 return NULL;
1254 }
1255
1257 if (cast->else_clause == NULL) return NULL;
1258
1259 pm_node_t *condition;
1260 PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1261 assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
1262
1263 pm_in_node_t *cast = (pm_in_node_t *) condition;
1264 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1265 if (vn == NULL) return NULL;
1266 if (void_node == NULL) void_node = vn;
1267 }
1268
1269 node = UP(cast->else_clause);
1270 break;
1271 }
1272 case PM_ENSURE_NODE: {
1274 node = UP(cast->statements);
1275 break;
1276 }
1277 case PM_PARENTHESES_NODE: {
1279 node = UP(cast->body);
1280 break;
1281 }
1282 case PM_STATEMENTS_NODE: {
1284
1285 // https://bugs.ruby-lang.org/issues/21669
1286 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1287 pm_node_t *body_part;
1288 PM_NODE_LIST_FOREACH(&cast->body, index, body_part) {
1289 switch (PM_NODE_TYPE(body_part)) {
1290 case PM_CASE_VOID_VALUE:
1291 if (void_node == NULL) {
1292 void_node = body_part;
1293 }
1294 return void_node;
1295 default: break;
1296 }
1297 }
1298 }
1299
1300 node = cast->body.nodes[cast->body.size - 1];
1301 break;
1302 }
1303 case PM_IF_NODE: {
1304 pm_if_node_t *cast = (pm_if_node_t *) node;
1305 if (cast->statements == NULL || cast->subsequent == NULL) {
1306 return NULL;
1307 }
1308 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1309 if (vn == NULL) {
1310 return NULL;
1311 }
1312 if (void_node == NULL) {
1313 void_node = vn;
1314 }
1315 node = cast->subsequent;
1316 break;
1317 }
1318 case PM_UNLESS_NODE: {
1320 if (cast->statements == NULL || cast->else_clause == NULL) {
1321 return NULL;
1322 }
1323 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1324 if (vn == NULL) {
1325 return NULL;
1326 }
1327 if (void_node == NULL) {
1328 void_node = vn;
1329 }
1330 node = UP(cast->else_clause);
1331 break;
1332 }
1333 case PM_ELSE_NODE: {
1335 node = UP(cast->statements);
1336 break;
1337 }
1338 case PM_AND_NODE:
1339 case PM_OR_NODE:
1340 // The left operand of an and/or node was already checked for a
1341 // value when the node was created, so descending into it again
1342 // would re-report the same void value and, in a chain such as
1343 // `a && a && ...`, walk the whole left branch on every operator,
1344 // which is quadratic in the length of the chain.
1345 return NULL;
1346 case PM_LOCAL_VARIABLE_WRITE_NODE: {
1348
1349 pm_scope_t *scope = parser->current_scope;
1350 for (uint32_t depth = 0; depth < cast->depth; depth++) scope = scope->previous;
1351
1352 pm_locals_read(&scope->locals, cast->name);
1353 return NULL;
1354 }
1355 default:
1356 return NULL;
1357 }
1358 }
1359
1360 return NULL;
1361}
1362
1363static PRISM_INLINE void
1364pm_assert_value_expression(pm_parser_t *parser, pm_node_t *node) {
1365 pm_node_t *void_node = pm_check_value_expression(parser, node);
1366 if (void_node != NULL) {
1367 pm_parser_err_node(parser, void_node, PM_ERR_VOID_EXPRESSION);
1368 }
1369}
1370
1374static void
1375pm_void_statement_check(pm_parser_t *parser, const pm_node_t *node) {
1376 const char *type = NULL;
1377 int length = 0;
1378
1379 switch (PM_NODE_TYPE(node)) {
1380 case PM_BACK_REFERENCE_READ_NODE:
1381 case PM_CLASS_VARIABLE_READ_NODE:
1382 case PM_GLOBAL_VARIABLE_READ_NODE:
1383 case PM_INSTANCE_VARIABLE_READ_NODE:
1384 case PM_LOCAL_VARIABLE_READ_NODE:
1385 case PM_NUMBERED_REFERENCE_READ_NODE:
1386 type = "a variable";
1387 length = 10;
1388 break;
1389 case PM_CALL_NODE: {
1390 const pm_call_node_t *cast = (const pm_call_node_t *) node;
1391 if (cast->call_operator_loc.length > 0 || cast->message_loc.length == 0) break;
1392
1393 const pm_constant_t *message = pm_constant_pool_id_to_constant(&parser->constant_pool, cast->name);
1394 switch (message->length) {
1395 case 1:
1396 switch (message->start[0]) {
1397 case '+':
1398 case '-':
1399 case '*':
1400 case '/':
1401 case '%':
1402 case '|':
1403 case '^':
1404 case '&':
1405 case '>':
1406 case '<':
1407 type = (const char *) message->start;
1408 length = 1;
1409 break;
1410 }
1411 break;
1412 case 2:
1413 switch (message->start[1]) {
1414 case '=':
1415 if (message->start[0] == '<' || message->start[0] == '>' || message->start[0] == '!' || message->start[0] == '=') {
1416 type = (const char *) message->start;
1417 length = 2;
1418 }
1419 break;
1420 case '@':
1421 if (message->start[0] == '+' || message->start[0] == '-') {
1422 type = (const char *) message->start;
1423 length = 2;
1424 }
1425 break;
1426 case '*':
1427 if (message->start[0] == '*') {
1428 type = (const char *) message->start;
1429 length = 2;
1430 }
1431 break;
1432 }
1433 break;
1434 case 3:
1435 if (memcmp(message->start, "<=>", 3) == 0) {
1436 type = "<=>";
1437 length = 3;
1438 }
1439 break;
1440 }
1441
1442 break;
1443 }
1444 case PM_CONSTANT_PATH_NODE:
1445 type = "::";
1446 length = 2;
1447 break;
1448 case PM_CONSTANT_READ_NODE:
1449 type = "a constant";
1450 length = 10;
1451 break;
1452 case PM_DEFINED_NODE:
1453 type = "defined?";
1454 length = 8;
1455 break;
1456 case PM_FALSE_NODE:
1457 type = "false";
1458 length = 5;
1459 break;
1460 case PM_FLOAT_NODE:
1461 case PM_IMAGINARY_NODE:
1462 case PM_INTEGER_NODE:
1463 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1464 case PM_INTERPOLATED_STRING_NODE:
1465 case PM_RATIONAL_NODE:
1466 case PM_REGULAR_EXPRESSION_NODE:
1467 case PM_SOURCE_ENCODING_NODE:
1468 case PM_SOURCE_FILE_NODE:
1469 case PM_SOURCE_LINE_NODE:
1470 case PM_STRING_NODE:
1471 case PM_SYMBOL_NODE:
1472 type = "a literal";
1473 length = 9;
1474 break;
1475 case PM_NIL_NODE:
1476 type = "nil";
1477 length = 3;
1478 break;
1479 case PM_RANGE_NODE: {
1480 const pm_range_node_t *cast = (const pm_range_node_t *) node;
1481
1482 if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) {
1483 type = "...";
1484 length = 3;
1485 } else {
1486 type = "..";
1487 length = 2;
1488 }
1489
1490 break;
1491 }
1492 case PM_SELF_NODE:
1493 type = "self";
1494 length = 4;
1495 break;
1496 case PM_TRUE_NODE:
1497 type = "true";
1498 length = 4;
1499 break;
1500 default:
1501 break;
1502 }
1503
1504 if (type != NULL) {
1505 PM_PARSER_WARN_NODE_FORMAT(parser, node, PM_WARN_VOID_STATEMENT, length, type);
1506 }
1507}
1508
1513static void
1514pm_void_statements_check(pm_parser_t *parser, const pm_statements_node_t *node, bool last_value) {
1515 assert(node->body.size > 0);
1516 const size_t size = node->body.size - (last_value ? 1 : 0);
1517 for (size_t index = 0; index < size; index++) {
1518 pm_void_statement_check(parser, node->body.nodes[index]);
1519 }
1520}
1521
1527typedef enum {
1528 PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL,
1529 PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP,
1530 PM_CONDITIONAL_PREDICATE_TYPE_NOT
1531} pm_conditional_predicate_type_t;
1532
1536static void
1537pm_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) {
1538 switch (type) {
1539 case PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL:
1540 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "condition");
1541 break;
1542 case PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP:
1543 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "flip-flop");
1544 break;
1545 case PM_CONDITIONAL_PREDICATE_TYPE_NOT:
1546 break;
1547 }
1548}
1549
1554static bool
1555pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) {
1556 switch (PM_NODE_TYPE(node)) {
1557 case PM_ARRAY_NODE: {
1558 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1559
1560 const pm_array_node_t *cast = (const pm_array_node_t *) node;
1561 for (size_t index = 0; index < cast->elements.size; index++) {
1562 if (!pm_conditional_predicate_warn_write_literal_p(cast->elements.nodes[index])) return false;
1563 }
1564
1565 return true;
1566 }
1567 case PM_HASH_NODE: {
1568 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1569
1570 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
1571 for (size_t index = 0; index < cast->elements.size; index++) {
1572 const pm_node_t *element = cast->elements.nodes[index];
1573 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return false;
1574
1575 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1576 if (!pm_conditional_predicate_warn_write_literal_p(assoc->key) || !pm_conditional_predicate_warn_write_literal_p(assoc->value)) return false;
1577 }
1578
1579 return true;
1580 }
1581 case PM_FALSE_NODE:
1582 case PM_FLOAT_NODE:
1583 case PM_IMAGINARY_NODE:
1584 case PM_INTEGER_NODE:
1585 case PM_NIL_NODE:
1586 case PM_RATIONAL_NODE:
1587 case PM_REGULAR_EXPRESSION_NODE:
1588 case PM_SOURCE_ENCODING_NODE:
1589 case PM_SOURCE_FILE_NODE:
1590 case PM_SOURCE_LINE_NODE:
1591 case PM_STRING_NODE:
1592 case PM_SYMBOL_NODE:
1593 case PM_TRUE_NODE:
1594 return true;
1595 default:
1596 return false;
1597 }
1598}
1599
1604static PRISM_INLINE void
1605pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) {
1606 if (pm_conditional_predicate_warn_write_literal_p(node)) {
1607 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);
1608 }
1609}
1610
1623static void
1624pm_conditional_predicate(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type) {
1625 switch (PM_NODE_TYPE(node)) {
1626 case PM_AND_NODE: {
1627 pm_and_node_t *cast = (pm_and_node_t *) node;
1628 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1629 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1630 break;
1631 }
1632 case PM_OR_NODE: {
1633 pm_or_node_t *cast = (pm_or_node_t *) node;
1634 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1635 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1636 break;
1637 }
1638 case PM_PARENTHESES_NODE: {
1640
1641 if ((cast->body != NULL) && PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE)) {
1642 pm_statements_node_t *statements = (pm_statements_node_t *) cast->body;
1643 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1644 }
1645
1646 break;
1647 }
1648 case PM_BEGIN_NODE: {
1650 if (cast->statements != NULL) {
1651 pm_statements_node_t *statements = cast->statements;
1652 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1653 }
1654 break;
1655 }
1656 case PM_RANGE_NODE: {
1658
1659 if (cast->left != NULL) pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1660 if (cast->right != NULL) pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1661
1662 // Here we change the range node into a flip flop node. We can do
1663 // this since the nodes are exactly the same except for the type.
1664 // We're only asserting against the size when we should probably
1665 // assert against the entire layout, but we'll assume tests will
1666 // catch this.
1667 assert(sizeof(pm_range_node_t) == sizeof(pm_flip_flop_node_t));
1668 node->type = PM_FLIP_FLOP_NODE;
1669
1670 break;
1671 }
1672 case PM_REGULAR_EXPRESSION_NODE:
1673 // Here we change the regular expression node into a match last line
1674 // node. We can do this since the nodes are exactly the same except
1675 // for the type.
1677 node->type = PM_MATCH_LAST_LINE_NODE;
1678
1679 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1680 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "regex ");
1681 }
1682
1683 break;
1684 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1685 // Here we change the interpolated regular expression node into an
1686 // interpolated match last line node. We can do this since the nodes
1687 // are exactly the same except for the type.
1689 node->type = PM_INTERPOLATED_MATCH_LAST_LINE_NODE;
1690
1691 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1692 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "regex ");
1693 }
1694
1695 break;
1696 case PM_INTEGER_NODE:
1697 if (type == PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP) {
1698 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1699 pm_parser_warn_node(parser, node, PM_WARN_INTEGER_IN_FLIP_FLOP);
1700 }
1701 } else {
1702 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1703 }
1704 break;
1705 case PM_STRING_NODE:
1706 case PM_SOURCE_FILE_NODE:
1707 case PM_INTERPOLATED_STRING_NODE:
1708 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "string ");
1709 break;
1710 case PM_SYMBOL_NODE:
1711 case PM_INTERPOLATED_SYMBOL_NODE:
1712 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "symbol ");
1713 break;
1714 case PM_SOURCE_LINE_NODE:
1715 case PM_SOURCE_ENCODING_NODE:
1716 case PM_FLOAT_NODE:
1717 case PM_RATIONAL_NODE:
1718 case PM_IMAGINARY_NODE:
1719 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1720 break;
1721 case PM_CLASS_VARIABLE_WRITE_NODE:
1722 pm_conditional_predicate_warn_write_literal(parser, ((pm_class_variable_write_node_t *) node)->value);
1723 break;
1724 case PM_CONSTANT_WRITE_NODE:
1725 pm_conditional_predicate_warn_write_literal(parser, ((pm_constant_write_node_t *) node)->value);
1726 break;
1727 case PM_GLOBAL_VARIABLE_WRITE_NODE:
1728 pm_conditional_predicate_warn_write_literal(parser, ((pm_global_variable_write_node_t *) node)->value);
1729 break;
1730 case PM_INSTANCE_VARIABLE_WRITE_NODE:
1731 pm_conditional_predicate_warn_write_literal(parser, ((pm_instance_variable_write_node_t *) node)->value);
1732 break;
1733 case PM_LOCAL_VARIABLE_WRITE_NODE:
1734 pm_conditional_predicate_warn_write_literal(parser, ((pm_local_variable_write_node_t *) node)->value);
1735 break;
1736 case PM_MULTI_WRITE_NODE:
1737 pm_conditional_predicate_warn_write_literal(parser, ((pm_multi_write_node_t *) node)->value);
1738 break;
1739 default:
1740 break;
1741 }
1742}
1743
1766
1770static PRISM_INLINE const pm_location_t *
1771pm_arguments_end(pm_arguments_t *arguments) {
1772 if (arguments->block != NULL) {
1773 uint32_t end = PM_NODE_END(arguments->block);
1774
1775 if (arguments->closing_loc.length > 0) {
1776 uint32_t arguments_end = PM_LOCATION_END(&arguments->closing_loc);
1777 if (arguments_end > end) {
1778 return &arguments->closing_loc;
1779 }
1780 }
1781 return &arguments->block->location;
1782 }
1783 if (arguments->closing_loc.length > 0) {
1784 return &arguments->closing_loc;
1785 }
1786 if (arguments->arguments != NULL) {
1787 return &arguments->arguments->base.location;
1788 }
1789 if (arguments->opening_loc.length > 0) {
1790 return &arguments->opening_loc;
1791 }
1792 return NULL;
1793}
1794
1799static void
1800pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_block_node_t *block) {
1801 // First, check that we have arguments and that we don't have a closing
1802 // location for them.
1803 if (arguments->arguments == NULL || arguments->closing_loc.length > 0) {
1804 return;
1805 }
1806
1807 // Next, check that we don't have a single parentheses argument. This would
1808 // look like:
1809 //
1810 // foo (1) {}
1811 //
1812 // In this case, it's actually okay for the block to be attached to the
1813 // call, even though it looks like it's attached to the argument.
1814 if (arguments->arguments->arguments.size == 1 && PM_NODE_TYPE_P(arguments->arguments->arguments.nodes[0], PM_PARENTHESES_NODE)) {
1815 return;
1816 }
1817
1818 // If we didn't hit a case before this check, then at this point we need to
1819 // add a syntax error.
1820 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_UNEXPECTED_BLOCK);
1821}
1822
1823/******************************************************************************/
1824/* Basic character checks */
1825/******************************************************************************/
1826
1833static PRISM_INLINE size_t
1834char_is_identifier_start(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
1835 if (n <= 0) return 0;
1836
1837 if (parser->encoding_changed) {
1838 size_t width;
1839
1840 if ((width = parser->encoding->alpha_char(b, n)) != 0) {
1841 return width;
1842 } else if (*b == '_') {
1843 return 1;
1844 } else if (*b >= 0x80) {
1845 return parser->encoding->char_width(b, n);
1846 } else {
1847 return 0;
1848 }
1849 } else if (*b < 0x80) {
1850 return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT ? 1 : 0) || (*b == '_');
1851 } else {
1852 return pm_encoding_utf_8_char_width(b, n);
1853 }
1854}
1855
1860static PRISM_INLINE size_t
1861char_is_identifier_utf8(const uint8_t *b, ptrdiff_t n) {
1862 if (n <= 0) {
1863 return 0;
1864 } else if (*b < 0x80) {
1865 return (*b == '_') || (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT ? 1 : 0);
1866 } else {
1867 return pm_encoding_utf_8_char_width(b, n);
1868 }
1869}
1870
1884#if defined(PRISM_HAS_NEON)
1885#include <arm_neon.h>
1886
1887static PRISM_INLINE size_t
1888scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
1889 const uint8_t *cursor = start;
1890
1891 // Nibble-based lookup tables for classifying [a-zA-Z0-9_].
1892 // Each high nibble is assigned a unique bit; the low nibble table
1893 // contains the OR of bits for all high nibbles that have an
1894 // identifier character at that low nibble position. A byte is an
1895 // identifier character iff (low_lut[lo] & high_lut[hi]) != 0.
1896 static const uint8_t low_lut_data[16] = {
1897 0x15, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
1898 0x1F, 0x1F, 0x1E, 0x0A, 0x0A, 0x0A, 0x0A, 0x0E
1899 };
1900 static const uint8_t high_lut_data[16] = {
1901 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10,
1902 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1903 };
1904 const uint8x16_t low_lut = vld1q_u8(low_lut_data);
1905 const uint8x16_t high_lut = vld1q_u8(high_lut_data);
1906 const uint8x16_t mask_0f = vdupq_n_u8(0x0F);
1907
1908 while (cursor + 16 <= end) {
1909 uint8x16_t v = vld1q_u8(cursor);
1910
1911 uint8x16_t lo_class = vqtbl1q_u8(low_lut, vandq_u8(v, mask_0f));
1912 uint8x16_t hi_class = vqtbl1q_u8(high_lut, vshrq_n_u8(v, 4));
1913 uint8x16_t ident = vandq_u8(lo_class, hi_class);
1914
1915 // Fast check: if the per-byte minimum is nonzero, every byte matched.
1916 if (vminvq_u8(ident) != 0) {
1917 cursor += 16;
1918 continue;
1919 }
1920
1921 // Find the first non-identifier byte (zero in ident).
1922 uint8x16_t is_zero = vceqq_u8(ident, vdupq_n_u8(0));
1923 uint64_t lo = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 0);
1924
1925 if (lo != 0) {
1926 cursor += pm_ctzll(lo) / 8;
1927 } else {
1928 uint64_t hi = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 1);
1929 cursor += 8 + pm_ctzll(hi) / 8;
1930 }
1931
1932 return (size_t) (cursor - start);
1933 }
1934
1935 return (size_t) (cursor - start);
1936}
1937
1938#elif defined(PRISM_HAS_SSE2)
1939#include <emmintrin.h>
1940
1941static PRISM_INLINE size_t
1942scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
1943 const uint8_t *cursor = start;
1944
1945 while (cursor + 16 <= end) {
1946 __m128i v = _mm_loadu_si128((const __m128i *) cursor);
1947 __m128i zero = _mm_setzero_si128();
1948
1949 // Unsigned range check via saturating subtraction:
1950 // byte >= lo ⟺ saturate(lo - byte) == 0
1951 // byte <= hi ⟺ saturate(byte - hi) == 0
1952
1953 // Fold case: OR with 0x20 maps A-Z to a-z.
1954 __m128i lowered = _mm_or_si128(v, _mm_set1_epi8(0x20));
1955 __m128i letter = _mm_and_si128(
1956 _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x61), lowered), zero),
1957 _mm_cmpeq_epi8(_mm_subs_epu8(lowered, _mm_set1_epi8(0x7A)), zero));
1958
1959 __m128i digit = _mm_and_si128(
1960 _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x30), v), zero),
1961 _mm_cmpeq_epi8(_mm_subs_epu8(v, _mm_set1_epi8(0x39)), zero));
1962
1963 __m128i underscore = _mm_cmpeq_epi8(v, _mm_set1_epi8(0x5F));
1964
1965 __m128i ident = _mm_or_si128(_mm_or_si128(letter, digit), underscore);
1966 int mask = _mm_movemask_epi8(ident);
1967
1968 if (mask == 0xFFFF) {
1969 cursor += 16;
1970 continue;
1971 }
1972
1973 cursor += pm_ctzll((uint64_t) (~mask & 0xFFFF));
1974 return (size_t) (cursor - start);
1975 }
1976
1977 return (size_t) (cursor - start);
1978}
1979
1980// The SWAR path uses pm_ctzll to find the first non-matching byte within a
1981// word, which only yields the correct byte index on little-endian targets.
1982// We gate on a positive little-endian check so that unknown-endianness
1983// platforms safely fall through to the no-op fallback.
1984#elif defined(PRISM_HAS_SWAR)
1985
1995static PRISM_INLINE size_t
1996scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
1997 static const uint64_t ones = 0x0101010101010101ULL;
1998 static const uint64_t highs = 0x8080808080808080ULL;
1999 const uint8_t *cursor = start;
2000
2001 while (cursor + 8 <= end) {
2002 uint64_t word;
2003 memcpy(&word, cursor, 8);
2004
2005 // Bail on any non-ASCII byte.
2006 if (word & highs) break;
2007
2008 uint64_t digit = ((word | highs) - ones * 0x30) & ((ones * 0x39 | highs) - word) & highs;
2009
2010 // Fold upper- and lowercase together by forcing bit 5 (OR 0x20),
2011 // then check the lowercase range once. A-Z maps to a-z; the
2012 // only non-letter byte that could alias into [0x61,0x7A] is one
2013 // whose original value was in [0x41,0x5A] — which is exactly
2014 // the uppercase letters we want to match.
2015 uint64_t lowered = word | (ones * 0x20);
2016 uint64_t letter = ((lowered | highs) - ones * 0x61) & ((ones * 0x7A | highs) - lowered) & highs;
2017
2018 // Standard SWAR "has zero byte" idiom on (word XOR 0x5F) to find
2019 // bytes equal to underscore. Safe from cross-byte borrows because
2020 // the ASCII guard above ensures all bytes are < 0x80.
2021 uint64_t xor_us = word ^ (ones * 0x5F);
2022 uint64_t underscore = (xor_us - ones) & ~xor_us & highs;
2023
2024 uint64_t ident = digit | letter | underscore;
2025
2026 if (ident == highs) {
2027 cursor += 8;
2028 continue;
2029 }
2030
2031 // Find the first non-identifier byte. On little-endian the first
2032 // byte sits in the least-significant position.
2033 uint64_t not_ident = ~ident & highs;
2034 cursor += pm_ctzll(not_ident) / 8;
2035 return (size_t) (cursor - start);
2036 }
2037
2038 return (size_t) (cursor - start);
2039}
2040
2041#else
2042
2043// No-op fallback for big-endian or other unsupported platforms.
2044// The caller's byte-at-a-time loop handles everything.
2045#define scan_identifier_ascii(start, end) ((size_t) 0)
2046
2047#endif
2048
2054static PRISM_INLINE size_t
2055char_is_identifier(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
2056 if (n <= 0) {
2057 return 0;
2058 } else if (parser->encoding_changed) {
2059 size_t width;
2060
2061 if ((width = parser->encoding->alnum_char(b, n)) != 0) {
2062 return width;
2063 } else if (*b == '_') {
2064 return 1;
2065 } else if (*b >= 0x80) {
2066 return parser->encoding->char_width(b, n);
2067 } else {
2068 return 0;
2069 }
2070 } else {
2071 return char_is_identifier_utf8(b, n);
2072 }
2073}
2074
2075// Here we're defining a perfect hash for the characters that are allowed in
2076// global names. This is used to quickly check the next character after a $ to
2077// see if it's a valid character for a global name.
2078#define BIT(c, idx) (((c) / 32 - 1 == idx) ? (1U << ((c) % 32)) : 0)
2079#define PUNCT(idx) ( \
2080 BIT('~', idx) | BIT('*', idx) | BIT('$', idx) | BIT('?', idx) | \
2081 BIT('!', idx) | BIT('@', idx) | BIT('/', idx) | BIT('\\', idx) | \
2082 BIT(';', idx) | BIT(',', idx) | BIT('.', idx) | BIT('=', idx) | \
2083 BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \
2084 BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \
2085 BIT('0', idx))
2086
2087const unsigned int pm_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { PUNCT(0), PUNCT(1), PUNCT(2) };
2088
2089#undef BIT
2090#undef PUNCT
2091
2092static PRISM_INLINE bool
2093char_is_global_name_punctuation(const uint8_t b) {
2094 const unsigned int i = (const unsigned int) b;
2095 if (i <= 0x20 || 0x7e < i) return false;
2096
2097 return (pm_global_name_punctuation_hash[(i - 0x20) / 32] >> (i % 32)) & 1;
2098}
2099
2100static PRISM_INLINE bool
2101token_is_setter_name(pm_token_t *token) {
2102 return (
2103 (token->type == PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL) ||
2104 ((token->type == PM_TOKEN_IDENTIFIER) &&
2105 (token->end - token->start >= 2) &&
2106 (token->end[-1] == '='))
2107 );
2108}
2109
2113static bool
2114pm_local_is_keyword(const char *source, size_t length) {
2115#define KEYWORD(name) if (memcmp(source, name, length) == 0) return true
2116
2117 switch (length) {
2118 case 2:
2119 switch (source[0]) {
2120 case 'd': KEYWORD("do"); return false;
2121 case 'i': KEYWORD("if"); KEYWORD("in"); return false;
2122 case 'o': KEYWORD("or"); return false;
2123 default: return false;
2124 }
2125 case 3:
2126 switch (source[0]) {
2127 case 'a': KEYWORD("and"); return false;
2128 case 'd': KEYWORD("def"); return false;
2129 case 'e': KEYWORD("end"); return false;
2130 case 'f': KEYWORD("for"); return false;
2131 case 'n': KEYWORD("nil"); KEYWORD("not"); return false;
2132 default: return false;
2133 }
2134 case 4:
2135 switch (source[0]) {
2136 case 'c': KEYWORD("case"); return false;
2137 case 'e': KEYWORD("else"); return false;
2138 case 'n': KEYWORD("next"); return false;
2139 case 'r': KEYWORD("redo"); return false;
2140 case 's': KEYWORD("self"); return false;
2141 case 't': KEYWORD("then"); KEYWORD("true"); return false;
2142 case 'w': KEYWORD("when"); return false;
2143 default: return false;
2144 }
2145 case 5:
2146 switch (source[0]) {
2147 case 'a': KEYWORD("alias"); return false;
2148 case 'b': KEYWORD("begin"); KEYWORD("break"); return false;
2149 case 'c': KEYWORD("class"); return false;
2150 case 'e': KEYWORD("elsif"); return false;
2151 case 'f': KEYWORD("false"); return false;
2152 case 'r': KEYWORD("retry"); return false;
2153 case 's': KEYWORD("super"); return false;
2154 case 'u': KEYWORD("undef"); KEYWORD("until"); return false;
2155 case 'w': KEYWORD("while"); return false;
2156 case 'y': KEYWORD("yield"); return false;
2157 default: return false;
2158 }
2159 case 6:
2160 switch (source[0]) {
2161 case 'e': KEYWORD("ensure"); return false;
2162 case 'm': KEYWORD("module"); return false;
2163 case 'r': KEYWORD("rescue"); KEYWORD("return"); return false;
2164 case 'u': KEYWORD("unless"); return false;
2165 default: return false;
2166 }
2167 case 8:
2168 KEYWORD("__LINE__");
2169 KEYWORD("__FILE__");
2170 return false;
2171 case 12:
2172 KEYWORD("__ENCODING__");
2173 return false;
2174 default:
2175 return false;
2176 }
2177
2178#undef KEYWORD
2179}
2180
2181/******************************************************************************/
2182/* Node flag handling functions */
2183/******************************************************************************/
2184
2188static PRISM_INLINE void
2189pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) {
2190 node->flags |= flag;
2191}
2192
2196static PRISM_INLINE void
2197pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) {
2198 node->flags &= (pm_node_flags_t) ~flag;
2199}
2200
2204static PRISM_INLINE void
2205pm_node_flag_set_repeated_parameter(pm_node_t *node) {
2206 assert(PM_NODE_TYPE(node) == PM_BLOCK_LOCAL_VARIABLE_NODE ||
2207 PM_NODE_TYPE(node) == PM_BLOCK_PARAMETER_NODE ||
2208 PM_NODE_TYPE(node) == PM_KEYWORD_REST_PARAMETER_NODE ||
2209 PM_NODE_TYPE(node) == PM_OPTIONAL_KEYWORD_PARAMETER_NODE ||
2210 PM_NODE_TYPE(node) == PM_OPTIONAL_PARAMETER_NODE ||
2211 PM_NODE_TYPE(node) == PM_REQUIRED_KEYWORD_PARAMETER_NODE ||
2212 PM_NODE_TYPE(node) == PM_REQUIRED_PARAMETER_NODE ||
2213 PM_NODE_TYPE(node) == PM_REST_PARAMETER_NODE);
2214
2215 pm_node_flag_set(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER);
2216}
2217
2218/******************************************************************************/
2219/* Node creation functions */
2220/******************************************************************************/
2221
2227#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)
2228
2232static PRISM_INLINE pm_node_flags_t
2233pm_regular_expression_flags_create(pm_parser_t *parser, const pm_token_t *closing) {
2234 pm_node_flags_t flags = 0;
2235
2236 if (closing->type == PM_TOKEN_REGEXP_END) {
2237 pm_buffer_t unknown_flags = { 0 };
2238
2239 // The closing delimiter is normally a single byte, so the options
2240 // follow it. A `\r\n` newline delimiter is two bytes, however, so we
2241 // skip past it to avoid misreading the trailing `\n` as an option.
2242 const uint8_t *flag = closing->start + 1;
2243 if ((closing->end - closing->start) >= 2 && closing->start[0] == '\r' && closing->start[1] == '\n') {
2244 flag++;
2245 }
2246
2247 for (; flag < closing->end; flag++) {
2248 switch (*flag) {
2249 case 'i': flags |= PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE; break;
2250 case 'm': flags |= PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE; break;
2251 case 'x': flags |= PM_REGULAR_EXPRESSION_FLAGS_EXTENDED; break;
2252 case 'o': flags |= PM_REGULAR_EXPRESSION_FLAGS_ONCE; break;
2253
2254 case 'e': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_EUC_JP); break;
2255 case 'n': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT); break;
2256 case 's': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J); break;
2257 case 'u': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_UTF_8); break;
2258
2259 default: pm_buffer_append_byte(&unknown_flags, *flag);
2260 }
2261 }
2262
2263 size_t unknown_flags_length = pm_buffer_length(&unknown_flags);
2264 if (unknown_flags_length != 0) {
2265 const char *word = unknown_flags_length >= 2 ? "options" : "option";
2266 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_REGEXP_UNKNOWN_OPTIONS, word, unknown_flags_length, pm_buffer_value(&unknown_flags));
2267 }
2268 pm_buffer_cleanup(&unknown_flags);
2269 }
2270
2271 return flags;
2272}
2273
2274#undef PM_REGULAR_EXPRESSION_ENCODING_MASK
2275
2276static pm_statements_node_t *
2277pm_statements_node_create(pm_parser_t *parser);
2278
2279static void
2280pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline);
2281
2282static size_t
2283pm_statements_node_body_length(pm_statements_node_t *node);
2284
2289static PRISM_INLINE void
2290pm_integer_arena_move(pm_arena_t *arena, pm_integer_t *integer) {
2291 if (integer->values != NULL) {
2292 size_t byte_size = integer->length * sizeof(uint32_t);
2293 uint32_t *old_values = integer->values;
2294 integer->values = (uint32_t *) pm_arena_memdup(arena, old_values, byte_size, PRISM_ALIGNOF(uint32_t));
2295 xfree(old_values);
2296 }
2297}
2298
2302static pm_error_recovery_node_t *
2303pm_error_recovery_node_create(pm_parser_t *parser, uint32_t start, uint32_t length) {
2304 return pm_error_recovery_node_new(
2305 parser->arena,
2306 ++parser->node_id,
2307 0,
2308 ((pm_location_t) { .start = start, .length = length }),
2309 NULL
2310 );
2311}
2312
2316static pm_error_recovery_node_t *
2317pm_error_recovery_node_create_unexpected(pm_parser_t *parser, pm_node_t *unexpected) {
2318 return pm_error_recovery_node_new(
2319 parser->arena,
2320 ++parser->node_id,
2321 0,
2322 unexpected->location,
2323 unexpected
2324 );
2325}
2326
2330static pm_alias_global_variable_node_t *
2331pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
2332 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
2333
2334 return pm_alias_global_variable_node_new(
2335 parser->arena,
2336 ++parser->node_id,
2337 0,
2338 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name),
2339 new_name,
2340 old_name,
2341 TOK2LOC(parser, keyword)
2342 );
2343}
2344
2348static pm_alias_method_node_t *
2349pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
2350 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
2351
2352 return pm_alias_method_node_new(
2353 parser->arena,
2354 ++parser->node_id,
2355 0,
2356 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name),
2357 new_name,
2358 old_name,
2359 TOK2LOC(parser, keyword)
2360 );
2361}
2362
2366static pm_alternation_pattern_node_t *
2367pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node_t *right, const pm_token_t *operator) {
2368 return pm_alternation_pattern_node_new(
2369 parser->arena,
2370 ++parser->node_id,
2371 0,
2372 PM_LOCATION_INIT_NODES(left, right),
2373 left,
2374 right,
2375 TOK2LOC(parser, operator)
2376 );
2377}
2378
2382static pm_and_node_t *
2383pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
2384 pm_assert_value_expression(parser, left);
2385
2386 return pm_and_node_new(
2387 parser->arena,
2388 ++parser->node_id,
2389 0,
2390 PM_LOCATION_INIT_NODES(left, right),
2391 left,
2392 right,
2393 TOK2LOC(parser, operator)
2394 );
2395}
2396
2400static pm_arguments_node_t *
2401pm_arguments_node_create(pm_parser_t *parser) {
2402 return pm_arguments_node_new(
2403 parser->arena,
2404 ++parser->node_id,
2405 0,
2406 PM_LOCATION_INIT_UNSET,
2407 ((pm_node_list_t) { 0 })
2408 );
2409}
2410
2414static size_t
2415pm_arguments_node_size(pm_arguments_node_t *node) {
2416 return node->arguments.size;
2417}
2418
2422static void
2423pm_arguments_node_arguments_append(pm_arena_t *arena, pm_arguments_node_t *node, pm_node_t *argument) {
2424 if (pm_arguments_node_size(node) == 0) {
2425 PM_NODE_START_SET_NODE(node, argument);
2426 }
2427
2428 if (PM_NODE_END(node) < PM_NODE_END(argument)) {
2429 PM_NODE_LENGTH_SET_NODE(node, argument);
2430 }
2431
2432 pm_node_list_append_inline(arena, &node->arguments, argument);
2433
2434 if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) {
2435 if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
2436 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS);
2437 } else {
2438 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT);
2439 }
2440 }
2441}
2442
2446static pm_array_node_t *
2447pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) {
2448 if (opening == NULL) {
2449 return pm_array_node_new(
2450 parser->arena,
2451 ++parser->node_id,
2452 PM_NODE_FLAG_STATIC_LITERAL,
2453 PM_LOCATION_INIT_UNSET,
2454 ((pm_node_list_t) { 0 }),
2455 ((pm_location_t) { 0 }),
2456 ((pm_location_t) { 0 })
2457 );
2458 } else {
2459 return pm_array_node_new(
2460 parser->arena,
2461 ++parser->node_id,
2462 PM_NODE_FLAG_STATIC_LITERAL,
2463 PM_LOCATION_INIT_TOKEN(parser, opening),
2464 ((pm_node_list_t) { 0 }),
2465 TOK2LOC(parser, opening),
2466 TOK2LOC(parser, opening)
2467 );
2468 }
2469}
2470
2474static PRISM_INLINE void
2475pm_array_node_elements_append(pm_arena_t *arena, pm_array_node_t *node, pm_node_t *element) {
2476 if (!node->elements.size && !node->opening_loc.length) {
2477 PM_NODE_START_SET_NODE(node, element);
2478 }
2479
2480 pm_node_list_append_inline(arena, &node->elements, element);
2481 PM_NODE_LENGTH_SET_NODE(node, element);
2482
2483 // If the element is not a static literal, then the array is not a static
2484 // literal. Turn that flag off.
2485 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)) {
2486 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
2487 }
2488
2489 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
2490 pm_node_flag_set(UP(node), PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT);
2491 }
2492}
2493
2497static void
2498pm_array_node_close_set(const pm_parser_t *parser, pm_array_node_t *node, const pm_token_t *closing) {
2499 assert(closing->type == PM_TOKEN_BRACKET_RIGHT || closing->type == PM_TOKEN_STRING_END || closing->type == 0);
2500 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
2501 node->closing_loc = TOK2LOC(parser, closing);
2502}
2503
2508static pm_array_pattern_node_t *
2509pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *nodes) {
2510 pm_array_pattern_node_t *node = pm_array_pattern_node_new(
2511 parser->arena,
2512 ++parser->node_id,
2513 0,
2514 PM_LOCATION_INIT_NODES(nodes->nodes[0], nodes->nodes[nodes->size - 1]),
2515 NULL,
2516 ((pm_node_list_t) { 0 }),
2517 NULL,
2518 ((pm_node_list_t) { 0 }),
2519 ((pm_location_t) { 0 }),
2520 ((pm_location_t) { 0 })
2521 );
2522
2523 // For now we're going to just copy over each pointer manually. This could be
2524 // much more efficient, as we could instead resize the node list.
2525 bool found_rest = false;
2526 pm_node_t *child;
2527
2528 PM_NODE_LIST_FOREACH(nodes, index, child) {
2529 if (!found_rest && (PM_NODE_TYPE_P(child, PM_SPLAT_NODE) || PM_NODE_TYPE_P(child, PM_IMPLICIT_REST_NODE))) {
2530 node->rest = child;
2531 found_rest = true;
2532 } else if (found_rest) {
2533 pm_node_list_append_inline(parser->arena, &node->posts, child);
2534 } else {
2535 pm_node_list_append_inline(parser->arena, &node->requireds, child);
2536 }
2537 }
2538
2539 return node;
2540}
2541
2545static pm_array_pattern_node_t *
2546pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) {
2547 return pm_array_pattern_node_new(
2548 parser->arena,
2549 ++parser->node_id,
2550 0,
2551 PM_LOCATION_INIT_NODE(rest),
2552 NULL,
2553 ((pm_node_list_t) { 0 }),
2554 rest,
2555 ((pm_node_list_t) { 0 }),
2556 ((pm_location_t) { 0 }),
2557 ((pm_location_t) { 0 })
2558 );
2559}
2560
2565static pm_array_pattern_node_t *
2566pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, const pm_token_t *opening, const pm_token_t *closing) {
2567 return pm_array_pattern_node_new(
2568 parser->arena,
2569 ++parser->node_id,
2570 0,
2571 PM_LOCATION_INIT_NODE_TOKEN(parser, constant, closing),
2572 constant,
2573 ((pm_node_list_t) { 0 }),
2574 NULL,
2575 ((pm_node_list_t) { 0 }),
2576 TOK2LOC(parser, opening),
2577 TOK2LOC(parser, closing)
2578 );
2579}
2580
2585static pm_array_pattern_node_t *
2586pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
2587 return pm_array_pattern_node_new(
2588 parser->arena,
2589 ++parser->node_id,
2590 0,
2591 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
2592 NULL,
2593 ((pm_node_list_t) { 0 }),
2594 NULL,
2595 ((pm_node_list_t) { 0 }),
2596 TOK2LOC(parser, opening),
2597 TOK2LOC(parser, closing)
2598 );
2599}
2600
2601static PRISM_INLINE void
2602pm_array_pattern_node_requireds_append(pm_arena_t *arena, pm_array_pattern_node_t *node, pm_node_t *inner) {
2603 pm_node_list_append_inline(arena, &node->requireds, inner);
2604}
2605
2609static pm_assoc_node_t *
2610pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *operator, pm_node_t *value) {
2611 uint32_t end;
2612
2613 if (value != NULL && PM_NODE_END(value) > PM_NODE_END(key)) {
2614 end = PM_NODE_END(value);
2615 } else if (operator != NULL) {
2616 end = PM_TOKEN_END(parser, operator);
2617 } else {
2618 end = PM_NODE_END(key);
2619 }
2620
2621 // Hash string keys will be frozen, so we can mark them as frozen here so
2622 // that the compiler picks them up and also when we check for static literal
2623 // on the keys it gets factored in.
2624 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
2625 key->flags |= PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL;
2626 }
2627
2628 // If the key and value of this assoc node are both static literals, then
2629 // we can mark this node as a static literal.
2630 pm_node_flags_t flags = 0;
2631 if (
2632 !PM_NODE_TYPE_P(key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(key, PM_HASH_NODE) && !PM_NODE_TYPE_P(key, PM_RANGE_NODE) &&
2633 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)
2634 ) {
2635 flags = key->flags & value->flags & PM_NODE_FLAG_STATIC_LITERAL;
2636 }
2637
2638 return pm_assoc_node_new(
2639 parser->arena,
2640 ++parser->node_id,
2641 flags,
2642 ((pm_location_t) { .start = PM_NODE_START(key), .length = U32(end - PM_NODE_START(key)) }),
2643 key,
2644 value,
2645 NTOK2LOC(parser, operator)
2646 );
2647}
2648
2652static pm_assoc_splat_node_t *
2653pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token_t *operator) {
2654 assert(operator->type == PM_TOKEN_USTAR_STAR);
2655
2656 return pm_assoc_splat_node_new(
2657 parser->arena,
2658 ++parser->node_id,
2659 0,
2660 (value == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, value),
2661 value,
2662 TOK2LOC(parser, operator)
2663 );
2664}
2665
2669static pm_back_reference_read_node_t *
2670pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
2671 assert(name->type == PM_TOKEN_BACK_REFERENCE);
2672
2673 return pm_back_reference_read_node_new(
2674 parser->arena,
2675 ++parser->node_id,
2676 0,
2677 PM_LOCATION_INIT_TOKEN(parser, name),
2678 pm_parser_constant_id_token(parser, name)
2679 );
2680}
2681
2685static pm_begin_node_t *
2686pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_statements_node_t *statements) {
2687 uint32_t start = begin_keyword == NULL ? 0 : PM_TOKEN_START(parser, begin_keyword);
2688 uint32_t end = statements == NULL ? (begin_keyword == NULL ? 0 : PM_TOKEN_END(parser, begin_keyword)) : PM_NODE_END(statements);
2689
2690 return pm_begin_node_new(
2691 parser->arena,
2692 ++parser->node_id,
2693 0,
2694 ((pm_location_t) { .start = start, .length = U32(end - start) }),
2695 NTOK2LOC(parser, begin_keyword),
2696 statements,
2697 NULL,
2698 NULL,
2699 NULL,
2700 ((pm_location_t) { 0 })
2701 );
2702}
2703
2707static void
2708pm_begin_node_rescue_clause_set(pm_begin_node_t *node, pm_rescue_node_t *rescue_clause) {
2709 if (node->begin_keyword_loc.length == 0) {
2710 PM_NODE_START_SET_NODE(node, rescue_clause);
2711 }
2712 PM_NODE_LENGTH_SET_NODE(node, rescue_clause);
2713 node->rescue_clause = rescue_clause;
2714}
2715
2719static void
2720pm_begin_node_else_clause_set(pm_begin_node_t *node, pm_else_node_t *else_clause) {
2721 if ((node->begin_keyword_loc.length == 0) && PM_NODE_START(node) == 0) {
2722 PM_NODE_START_SET_NODE(node, else_clause);
2723 }
2724 PM_NODE_LENGTH_SET_NODE(node, else_clause);
2725 node->else_clause = else_clause;
2726}
2727
2731static void
2732pm_begin_node_ensure_clause_set(pm_begin_node_t *node, pm_ensure_node_t *ensure_clause) {
2733 if ((node->begin_keyword_loc.length == 0) && PM_NODE_START(node) == 0) {
2734 PM_NODE_START_SET_NODE(node, ensure_clause);
2735 }
2736 PM_NODE_LENGTH_SET_NODE(node, ensure_clause);
2737 node->ensure_clause = ensure_clause;
2738}
2739
2743static void
2744pm_begin_node_end_keyword_set(const pm_parser_t *parser, pm_begin_node_t *node, const pm_token_t *end_keyword) {
2745 assert(end_keyword->type == PM_TOKEN_KEYWORD_END || end_keyword->type == 0);
2746 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
2747 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
2748}
2749
2753static pm_block_argument_node_t *
2754pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
2755 assert(operator->type == PM_TOKEN_UAMPERSAND);
2756
2757 return pm_block_argument_node_new(
2758 parser->arena,
2759 ++parser->node_id,
2760 0,
2761 (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression),
2762 expression,
2763 TOK2LOC(parser, operator)
2764 );
2765}
2766
2770static pm_block_node_t *
2771pm_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) {
2772 return pm_block_node_new(
2773 parser->arena,
2774 ++parser->node_id,
2775 0,
2776 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
2777 *locals,
2778 parameters,
2779 body,
2780 TOK2LOC(parser, opening),
2781 TOK2LOC(parser, closing)
2782 );
2783}
2784
2788static pm_block_parameter_node_t *
2789pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator) {
2790 assert(operator->type == PM_TOKEN_UAMPERSAND || operator->type == PM_TOKEN_AMPERSAND);
2791
2792 return pm_block_parameter_node_new(
2793 parser->arena,
2794 ++parser->node_id,
2795 0,
2796 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
2797 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
2798 NTOK2LOC(parser, name),
2799 TOK2LOC(parser, operator)
2800 );
2801}
2802
2806static pm_block_parameters_node_t *
2807pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *parameters, const pm_token_t *opening) {
2808 uint32_t start;
2809 if (opening != NULL) {
2810 start = PM_TOKEN_START(parser, opening);
2811 } else if (parameters != NULL) {
2812 start = PM_NODE_START(parameters);
2813 } else {
2814 start = 0;
2815 }
2816
2817 uint32_t end;
2818 if (parameters != NULL) {
2819 end = PM_NODE_END(parameters);
2820 } else if (opening != NULL) {
2821 end = PM_TOKEN_END(parser, opening);
2822 } else {
2823 end = 0;
2824 }
2825
2826 return pm_block_parameters_node_new(
2827 parser->arena,
2828 ++parser->node_id,
2829 0,
2830 ((pm_location_t) { .start = start, .length = U32(end - start) }),
2831 parameters,
2832 ((pm_node_list_t) { 0 }),
2833 NTOK2LOC(parser, opening),
2834 ((pm_location_t) { 0 })
2835 );
2836}
2837
2841static void
2842pm_block_parameters_node_closing_set(const pm_parser_t *parser, pm_block_parameters_node_t *node, const pm_token_t *closing) {
2843 assert(closing->type == PM_TOKEN_PIPE || closing->type == PM_TOKEN_PARENTHESIS_RIGHT || closing->type == 0);
2844 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
2845 node->closing_loc = TOK2LOC(parser, closing);
2846}
2847
2851static pm_block_local_variable_node_t *
2852pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) {
2853 return pm_block_local_variable_node_new(
2854 parser->arena,
2855 ++parser->node_id,
2856 0,
2857 PM_LOCATION_INIT_TOKEN(parser, name),
2858 pm_parser_constant_id_token(parser, name)
2859 );
2860}
2861
2865static void
2866pm_block_parameters_node_append_local(pm_arena_t *arena, pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) {
2867 pm_node_list_append_inline(arena, &node->locals, UP(local));
2868
2869 if (PM_NODE_LENGTH(node) == 0) {
2870 PM_NODE_START_SET_NODE(node, local);
2871 }
2872
2873 PM_NODE_LENGTH_SET_NODE(node, local);
2874}
2875
2879static pm_break_node_t *
2880pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
2881 assert(keyword->type == PM_TOKEN_KEYWORD_BREAK);
2882
2883 return pm_break_node_new(
2884 parser->arena,
2885 ++parser->node_id,
2886 0,
2887 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
2888 arguments,
2889 TOK2LOC(parser, keyword)
2890 );
2891}
2892
2893// There are certain flags that we want to use internally but don't want to
2894// expose because they are not relevant beyond parsing. Therefore we'll define
2895// them here and not define them in config.yml/a header file.
2896static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2);
2897
2898static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1);
2899static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2);
2900static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3);
2901
2907static pm_call_node_t *
2908pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) {
2909 return pm_call_node_new(
2910 parser->arena,
2911 ++parser->node_id,
2912 flags,
2913 PM_LOCATION_INIT_UNSET,
2914 NULL,
2915 ((pm_location_t) { 0 }),
2916 0,
2917 ((pm_location_t) { 0 }),
2918 ((pm_location_t) { 0 }),
2919 NULL,
2920 ((pm_location_t) { 0 }),
2921 ((pm_location_t) { 0 }),
2922 NULL
2923 );
2924}
2925
2930static PRISM_INLINE pm_node_flags_t
2931pm_call_node_ignore_visibility_flag(const pm_node_t *receiver) {
2932 return PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY : 0;
2933}
2934
2939static pm_call_node_t *
2940pm_call_node_aref_create(pm_parser_t *parser, pm_node_t *receiver, pm_arguments_t *arguments) {
2941 pm_assert_value_expression(parser, receiver);
2942
2943 pm_node_flags_t flags = pm_call_node_ignore_visibility_flag(receiver);
2944 if (arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_ARGUMENT_NODE)) {
2945 flags |= PM_CALL_NODE_FLAGS_INDEX;
2946 }
2947
2948 pm_call_node_t *node = pm_call_node_create(parser, flags);
2949
2950 PM_NODE_START_SET_NODE(node, receiver);
2951
2952 const pm_location_t *end = pm_arguments_end(arguments);
2953 assert(end != NULL && "unreachable");
2954 PM_NODE_LENGTH_SET_LOCATION(node, end);
2955
2956 node->receiver = receiver;
2957 node->message_loc.start = arguments->opening_loc.start;
2958 node->message_loc.length = (arguments->closing_loc.start + arguments->closing_loc.length) - arguments->opening_loc.start;
2959
2960 node->opening_loc = arguments->opening_loc;
2961 node->arguments = arguments->arguments;
2962 node->closing_loc = arguments->closing_loc;
2963 node->block = arguments->block;
2964
2965 node->name = pm_parser_constant_id_constant(parser, "[]", 2);
2966 return node;
2967}
2968
2972static pm_call_node_t *
2973pm_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) {
2974 pm_assert_value_expression(parser, receiver);
2975 pm_assert_value_expression(parser, argument);
2976
2977 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver) | flags);
2978
2979 PM_NODE_START_SET_NODE(node, PM_NODE_START(receiver) < PM_NODE_START(argument) ? receiver : argument);
2980 PM_NODE_LENGTH_SET_NODE(node, PM_NODE_END(receiver) > PM_NODE_END(argument) ? receiver : argument);
2981
2982 node->receiver = receiver;
2983 node->message_loc = TOK2LOC(parser, operator);
2984
2985 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
2986 pm_arguments_node_arguments_append(parser->arena, arguments, argument);
2987 node->arguments = arguments;
2988
2989 node->name = pm_parser_constant_id_token(parser, operator);
2990 return node;
2991}
2992
2993static const uint8_t * parse_operator_symbol_name(const pm_token_t *);
2994
2998static pm_call_node_t *
2999pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_token_t *message, pm_arguments_t *arguments) {
3000 pm_assert_value_expression(parser, receiver);
3001
3002 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3003
3004 PM_NODE_START_SET_NODE(node, receiver);
3005 const pm_location_t *end = pm_arguments_end(arguments);
3006 if (end == NULL) {
3007 PM_NODE_LENGTH_SET_TOKEN(parser, node, message);
3008 } else {
3009 PM_NODE_LENGTH_SET_LOCATION(node, end);
3010 }
3011
3012 node->receiver = receiver;
3013 node->call_operator_loc = TOK2LOC(parser, operator);
3014 node->message_loc = TOK2LOC(parser, message);
3015 node->opening_loc = arguments->opening_loc;
3016 node->arguments = arguments->arguments;
3017 node->closing_loc = arguments->closing_loc;
3018 node->block = arguments->block;
3019
3020 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
3021 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
3022 }
3023
3028 node->name = pm_parser_constant_id_raw(parser, message->start, parse_operator_symbol_name(message));
3029 return node;
3030}
3031
3035static pm_call_node_t *
3036pm_call_node_call_synthesized_create(pm_parser_t *parser, pm_node_t *receiver, const char *message, pm_arguments_node_t *arguments) {
3037 pm_call_node_t *node = pm_call_node_create(parser, 0);
3038 node->base.location = (pm_location_t) { .start = 0, .length = U32(parser->end - parser->start) };
3039
3040 node->receiver = receiver;
3041 node->arguments = arguments;
3042
3043 node->name = pm_parser_constant_id_constant(parser, message, strlen(message));
3044 return node;
3045}
3046
3051static pm_call_node_t *
3052pm_call_node_fcall_create(pm_parser_t *parser, pm_token_t *message, pm_arguments_t *arguments) {
3053 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3054
3055 PM_NODE_START_SET_TOKEN(parser, node, message);
3056 const pm_location_t *end = pm_arguments_end(arguments);
3057 assert(end != NULL && "unreachable");
3058 PM_NODE_LENGTH_SET_LOCATION(node, end);
3059
3060 node->message_loc = TOK2LOC(parser, message);
3061 node->opening_loc = arguments->opening_loc;
3062 node->arguments = arguments->arguments;
3063 node->closing_loc = arguments->closing_loc;
3064 node->block = arguments->block;
3065
3066 node->name = pm_parser_constant_id_token(parser, message);
3067 return node;
3068}
3069
3074static pm_call_node_t *
3075pm_call_node_fcall_synthesized_create(pm_parser_t *parser, pm_arguments_node_t *arguments, pm_constant_id_t name) {
3076 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3077
3078 node->base.location = (pm_location_t) { 0 };
3079 node->arguments = arguments;
3080
3081 node->name = name;
3082 return node;
3083}
3084
3088static pm_call_node_t *
3089pm_call_node_not_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *message, pm_arguments_t *arguments) {
3090 pm_assert_value_expression(parser, receiver);
3091 if (receiver != NULL) pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
3092
3093 pm_call_node_t *node = pm_call_node_create(parser, receiver == NULL ? 0 : pm_call_node_ignore_visibility_flag(receiver));
3094
3095 PM_NODE_START_SET_TOKEN(parser, node, message);
3096 if (arguments->closing_loc.length > 0) {
3097 PM_NODE_LENGTH_SET_LOCATION(node, &arguments->closing_loc);
3098 } else {
3099 assert(receiver != NULL);
3100 PM_NODE_LENGTH_SET_NODE(node, receiver);
3101 }
3102
3103 node->receiver = receiver;
3104 node->message_loc = TOK2LOC(parser, message);
3105 node->opening_loc = arguments->opening_loc;
3106 node->arguments = arguments->arguments;
3107 node->closing_loc = arguments->closing_loc;
3108
3109 node->name = pm_parser_constant_id_constant(parser, "!", 1);
3110 return node;
3111}
3112
3116static pm_call_node_t *
3117pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_arguments_t *arguments) {
3118 pm_assert_value_expression(parser, receiver);
3119
3120 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3121
3122 PM_NODE_START_SET_NODE(node, receiver);
3123 const pm_location_t *end = pm_arguments_end(arguments);
3124 assert(end != NULL && "unreachable");
3125 PM_NODE_LENGTH_SET_LOCATION(node, end);
3126
3127 node->receiver = receiver;
3128 node->call_operator_loc = TOK2LOC(parser, operator);
3129 node->opening_loc = arguments->opening_loc;
3130 node->arguments = arguments->arguments;
3131 node->closing_loc = arguments->closing_loc;
3132 node->block = arguments->block;
3133
3134 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
3135 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
3136 }
3137
3138 node->name = pm_parser_constant_id_constant(parser, "call", 4);
3139 return node;
3140}
3141
3145static pm_call_node_t *
3146pm_call_node_unary_create(pm_parser_t *parser, pm_token_t *operator, pm_node_t *receiver, const char *name) {
3147 pm_assert_value_expression(parser, receiver);
3148
3149 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3150
3151 PM_NODE_START_SET_TOKEN(parser, node, operator);
3152 PM_NODE_LENGTH_SET_NODE(node, receiver);
3153
3154 node->receiver = receiver;
3155 node->message_loc = TOK2LOC(parser, operator);
3156
3157 node->name = pm_parser_constant_id_constant(parser, name, strlen(name));
3158 return node;
3159}
3160
3165static pm_call_node_t *
3166pm_call_node_variable_call_create(pm_parser_t *parser, pm_token_t *message) {
3167 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3168
3169 node->base.location = TOK2LOC(parser, message);
3170 node->message_loc = TOK2LOC(parser, message);
3171
3172 node->name = pm_parser_constant_id_token(parser, message);
3173 return node;
3174}
3175
3180static PRISM_INLINE bool
3181pm_call_node_writable_p(const pm_parser_t *parser, const pm_call_node_t *node) {
3182 return (
3183 (node->message_loc.length > 0) &&
3184 (parser->start[node->message_loc.start + node->message_loc.length - 1] != '!') &&
3185 (parser->start[node->message_loc.start + node->message_loc.length - 1] != '?') &&
3186 char_is_identifier_start(parser, parser->start + node->message_loc.start, (ptrdiff_t) node->message_loc.length) &&
3187 (node->opening_loc.length == 0) &&
3188 (node->arguments == NULL) &&
3189 (node->block == NULL)
3190 );
3191}
3192
3196static void
3197pm_call_write_read_name_init(pm_parser_t *parser, pm_constant_id_t *read_name, pm_constant_id_t *write_name) {
3198 pm_constant_t *write_constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *write_name);
3199
3200 if (write_constant->length > 0) {
3201 size_t length = write_constant->length - 1;
3202
3203 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
3204 memcpy(memory, write_constant->start, length);
3205
3206 *read_name = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, memory, length);
3207 } else {
3208 // We can get here if the message was missing because of a syntax error.
3209 *read_name = pm_parser_constant_id_constant(parser, "", 0);
3210 }
3211}
3212
3216static pm_call_and_write_node_t *
3217pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3218 assert(target->block == NULL);
3219 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3220
3221 pm_call_and_write_node_t *node = pm_call_and_write_node_new(
3222 parser->arena,
3223 ++parser->node_id,
3224 FL(target),
3225 PM_LOCATION_INIT_NODES(target, value),
3226 target->receiver,
3227 target->call_operator_loc,
3228 target->message_loc,
3229 0,
3230 target->name,
3231 TOK2LOC(parser, operator),
3232 value
3233 );
3234
3235 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3236
3237 // The target is no longer necessary because we've reused its children.
3238 // It is arena-allocated so no explicit free is needed.
3239
3240 return node;
3241}
3242
3247static void
3248pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) {
3249 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
3250 if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) {
3251 pm_node_t *node;
3252 PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) {
3253 if (PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)) {
3254 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_INDEX_KEYWORDS);
3255 break;
3256 }
3257 }
3258 }
3259
3260 if (block != NULL) {
3261 pm_parser_err_node(parser, block, PM_ERR_UNEXPECTED_INDEX_BLOCK);
3262 }
3263 }
3264}
3265
3269static pm_index_and_write_node_t *
3270pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3271 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3272
3273 pm_index_arguments_check(parser, target->arguments, target->block);
3274
3275 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3276
3277 pm_index_and_write_node_t *node = pm_index_and_write_node_new(
3278 parser->arena,
3279 ++parser->node_id,
3280 FL(target),
3281 PM_LOCATION_INIT_NODES(target, value),
3282 target->receiver,
3283 target->call_operator_loc,
3284 target->opening_loc,
3285 target->arguments,
3286 target->closing_loc,
3287 (pm_block_argument_node_t *) target->block,
3288 TOK2LOC(parser, operator),
3289 value
3290 );
3291
3292 // The target is no longer necessary because we've reused its children.
3293 // It is arena-allocated so no explicit free is needed.
3294
3295 return node;
3296}
3297
3301static pm_call_operator_write_node_t *
3302pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3303 assert(target->block == NULL);
3304
3305 pm_call_operator_write_node_t *node = pm_call_operator_write_node_new(
3306 parser->arena,
3307 ++parser->node_id,
3308 FL(target),
3309 PM_LOCATION_INIT_NODES(target, value),
3310 target->receiver,
3311 target->call_operator_loc,
3312 target->message_loc,
3313 0,
3314 target->name,
3315 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
3316 TOK2LOC(parser, operator),
3317 value
3318 );
3319
3320 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3321
3322 // The target is no longer necessary because we've reused its children.
3323 // It is arena-allocated so no explicit free is needed.
3324
3325 return node;
3326}
3327
3331static pm_index_operator_write_node_t *
3332pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3333 pm_index_arguments_check(parser, target->arguments, target->block);
3334
3335 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3336
3337 pm_index_operator_write_node_t *node = pm_index_operator_write_node_new(
3338 parser->arena,
3339 ++parser->node_id,
3340 FL(target),
3341 PM_LOCATION_INIT_NODES(target, value),
3342 target->receiver,
3343 target->call_operator_loc,
3344 target->opening_loc,
3345 target->arguments,
3346 target->closing_loc,
3347 (pm_block_argument_node_t *) target->block,
3348 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
3349 TOK2LOC(parser, operator),
3350 value
3351 );
3352
3353 // The target is no longer necessary because we've reused its children.
3354 // It is arena-allocated so no explicit free is needed.
3355
3356 return node;
3357}
3358
3362static pm_call_or_write_node_t *
3363pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3364 assert(target->block == NULL);
3365 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3366
3367 pm_call_or_write_node_t *node = pm_call_or_write_node_new(
3368 parser->arena,
3369 ++parser->node_id,
3370 FL(target),
3371 PM_LOCATION_INIT_NODES(target, value),
3372 target->receiver,
3373 target->call_operator_loc,
3374 target->message_loc,
3375 0,
3376 target->name,
3377 TOK2LOC(parser, operator),
3378 value
3379 );
3380
3381 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3382
3383 // The target is no longer necessary because we've reused its children.
3384 // It is arena-allocated so no explicit free is needed.
3385
3386 return node;
3387}
3388
3392static pm_index_or_write_node_t *
3393pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3394 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3395
3396 pm_index_arguments_check(parser, target->arguments, target->block);
3397
3398 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3399
3400 pm_index_or_write_node_t *node = pm_index_or_write_node_new(
3401 parser->arena,
3402 ++parser->node_id,
3403 FL(target),
3404 PM_LOCATION_INIT_NODES(target, value),
3405 target->receiver,
3406 target->call_operator_loc,
3407 target->opening_loc,
3408 target->arguments,
3409 target->closing_loc,
3410 (pm_block_argument_node_t *) target->block,
3411 TOK2LOC(parser, operator),
3412 value
3413 );
3414
3415 // The target is no longer necessary because we've reused its children.
3416 // It is arena-allocated so no explicit free is needed.
3417
3418 return node;
3419}
3420
3425static pm_call_target_node_t *
3426pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3427 pm_call_target_node_t *node = pm_call_target_node_new(
3428 parser->arena,
3429 ++parser->node_id,
3430 FL(target),
3431 PM_LOCATION_INIT_NODE(target),
3432 target->receiver,
3433 target->call_operator_loc,
3434 target->name,
3435 target->message_loc
3436 );
3437
3438 /* It is possible to get here where we have parsed an invalid syntax tree
3439 * where the call operator was not present. In that case we will have a
3440 * problem because it is a required location. In this case we need to fill
3441 * it in with a fake location so that the syntax tree remains valid. */
3442 if (node->call_operator_loc.length == 0) {
3443 node->call_operator_loc = target->base.location;
3444 }
3445
3446 // The target is no longer necessary because we've reused its children.
3447 // It is arena-allocated so no explicit free is needed.
3448
3449 return node;
3450}
3451
3456static pm_index_target_node_t *
3457pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3458 pm_index_arguments_check(parser, target->arguments, target->block);
3459 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3460
3461 pm_index_target_node_t *node = pm_index_target_node_new(
3462 parser->arena,
3463 ++parser->node_id,
3464 FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE,
3465 PM_LOCATION_INIT_NODE(target),
3466 target->receiver,
3467 target->opening_loc,
3468 target->arguments,
3469 target->closing_loc,
3470 (pm_block_argument_node_t *) target->block
3471 );
3472
3473 // The target is no longer necessary because we've reused its children.
3474 // It is arena-allocated so no explicit free is needed.
3475
3476 return node;
3477}
3478
3482static pm_capture_pattern_node_t *
3483pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_local_variable_target_node_t *target, const pm_token_t *operator) {
3484 return pm_capture_pattern_node_new(
3485 parser->arena,
3486 ++parser->node_id,
3487 0,
3488 PM_LOCATION_INIT_NODES(value, target),
3489 value,
3490 target,
3491 TOK2LOC(parser, operator)
3492 );
3493}
3494
3498static pm_case_node_t *
3499pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) {
3500 return pm_case_node_new(
3501 parser->arena,
3502 ++parser->node_id,
3503 0,
3504 PM_LOCATION_INIT_TOKENS(parser, case_keyword, end_keyword == NULL ? case_keyword : end_keyword),
3505 predicate,
3506 ((pm_node_list_t) { 0 }),
3507 NULL,
3508 TOK2LOC(parser, case_keyword),
3509 NTOK2LOC(parser, end_keyword)
3510 );
3511}
3512
3516static void
3517pm_case_node_condition_append(pm_arena_t *arena, pm_case_node_t *node, pm_node_t *condition) {
3518 assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
3519
3520 pm_node_list_append_inline(arena, &node->conditions, condition);
3521 PM_NODE_LENGTH_SET_NODE(node, condition);
3522}
3523
3527static void
3528pm_case_node_else_clause_set(pm_case_node_t *node, pm_else_node_t *else_clause) {
3529 node->else_clause = else_clause;
3530 PM_NODE_LENGTH_SET_NODE(node, else_clause);
3531}
3532
3536static void
3537pm_case_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_node_t *node, const pm_token_t *end_keyword) {
3538 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
3539 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
3540}
3541
3545static pm_case_match_node_t *
3546pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate) {
3547 return pm_case_match_node_new(
3548 parser->arena,
3549 ++parser->node_id,
3550 0,
3551 PM_LOCATION_INIT_TOKEN(parser, case_keyword),
3552 predicate,
3553 ((pm_node_list_t) { 0 }),
3554 NULL,
3555 TOK2LOC(parser, case_keyword),
3556 ((pm_location_t) { 0 })
3557 );
3558}
3559
3563static void
3564pm_case_match_node_condition_append(pm_arena_t *arena, pm_case_match_node_t *node, pm_node_t *condition) {
3565 assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
3566
3567 pm_node_list_append_inline(arena, &node->conditions, condition);
3568 PM_NODE_LENGTH_SET_NODE(node, condition);
3569}
3570
3574static void
3575pm_case_match_node_else_clause_set(pm_case_match_node_t *node, pm_else_node_t *else_clause) {
3576 node->else_clause = else_clause;
3577 PM_NODE_LENGTH_SET_NODE(node, else_clause);
3578}
3579
3583static void
3584pm_case_match_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_match_node_t *node, const pm_token_t *end_keyword) {
3585 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
3586 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
3587}
3588
3592static pm_class_node_t *
3593pm_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) {
3594 return pm_class_node_new(
3595 parser->arena,
3596 ++parser->node_id,
3597 0,
3598 PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword),
3599 *locals,
3600 TOK2LOC(parser, class_keyword),
3601 constant_path,
3602 NTOK2LOC(parser, inheritance_operator),
3603 superclass,
3604 body,
3605 TOK2LOC(parser, end_keyword),
3606 pm_parser_constant_id_token(parser, name)
3607 );
3608}
3609
3613static pm_class_variable_and_write_node_t *
3614pm_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) {
3615 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3616
3617 return pm_class_variable_and_write_node_new(
3618 parser->arena,
3619 ++parser->node_id,
3620 0,
3621 PM_LOCATION_INIT_NODES(target, value),
3622 target->name,
3623 target->base.location,
3624 TOK2LOC(parser, operator),
3625 value
3626 );
3627}
3628
3632static pm_class_variable_operator_write_node_t *
3633pm_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) {
3634 return pm_class_variable_operator_write_node_new(
3635 parser->arena,
3636 ++parser->node_id,
3637 0,
3638 PM_LOCATION_INIT_NODES(target, value),
3639 target->name,
3640 target->base.location,
3641 TOK2LOC(parser, operator),
3642 value,
3643 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3644 );
3645}
3646
3650static pm_class_variable_or_write_node_t *
3651pm_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) {
3652 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3653
3654 return pm_class_variable_or_write_node_new(
3655 parser->arena,
3656 ++parser->node_id,
3657 0,
3658 PM_LOCATION_INIT_NODES(target, value),
3659 target->name,
3660 target->base.location,
3661 TOK2LOC(parser, operator),
3662 value
3663 );
3664}
3665
3669static pm_class_variable_read_node_t *
3670pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
3671 assert(token->type == PM_TOKEN_CLASS_VARIABLE);
3672
3673 return pm_class_variable_read_node_new(
3674 parser->arena,
3675 ++parser->node_id,
3676 0,
3677 PM_LOCATION_INIT_TOKEN(parser, token),
3678 pm_parser_constant_id_token(parser, token)
3679 );
3680}
3681
3688static PRISM_INLINE pm_node_flags_t
3689pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) {
3690 if (PM_NODE_TYPE_P(node, PM_ARRAY_NODE) && ((const pm_array_node_t *) node)->opening_loc.length == 0) {
3691 return flags;
3692 }
3693 return 0;
3694}
3695
3699static pm_class_variable_write_node_t *
3700pm_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) {
3701 return pm_class_variable_write_node_new(
3702 parser->arena,
3703 ++parser->node_id,
3704 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3705 PM_LOCATION_INIT_NODES(read_node, value),
3706 read_node->name,
3707 read_node->base.location,
3708 value,
3709 TOK2LOC(parser, operator)
3710 );
3711}
3712
3716static pm_constant_path_and_write_node_t *
3717pm_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) {
3718 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3719
3720 return pm_constant_path_and_write_node_new(
3721 parser->arena,
3722 ++parser->node_id,
3723 0,
3724 PM_LOCATION_INIT_NODES(target, value),
3725 target,
3726 TOK2LOC(parser, operator),
3727 value
3728 );
3729}
3730
3734static pm_constant_path_operator_write_node_t *
3735pm_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) {
3736 return pm_constant_path_operator_write_node_new(
3737 parser->arena,
3738 ++parser->node_id,
3739 0,
3740 PM_LOCATION_INIT_NODES(target, value),
3741 target,
3742 TOK2LOC(parser, operator),
3743 value,
3744 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3745 );
3746}
3747
3751static pm_constant_path_or_write_node_t *
3752pm_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) {
3753 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3754
3755 return pm_constant_path_or_write_node_new(
3756 parser->arena,
3757 ++parser->node_id,
3758 0,
3759 PM_LOCATION_INIT_NODES(target, value),
3760 target,
3761 TOK2LOC(parser, operator),
3762 value
3763 );
3764}
3765
3769static pm_constant_path_node_t *
3770pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_token_t *delimiter, const pm_token_t *name_token) {
3771 pm_assert_value_expression(parser, parent);
3772
3773 pm_constant_id_t name = PM_CONSTANT_ID_UNSET;
3774 if (name_token->type == PM_TOKEN_CONSTANT) {
3775 name = pm_parser_constant_id_token(parser, name_token);
3776 }
3777
3778 return pm_constant_path_node_new(
3779 parser->arena,
3780 ++parser->node_id,
3781 0,
3782 (parent == NULL) ? PM_LOCATION_INIT_TOKENS(parser, delimiter, name_token) : PM_LOCATION_INIT_NODE_TOKEN(parser, parent, name_token),
3783 parent,
3784 name,
3785 TOK2LOC(parser, delimiter),
3786 TOK2LOC(parser, name_token)
3787 );
3788}
3789
3793static pm_constant_path_write_node_t *
3794pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3795 return pm_constant_path_write_node_new(
3796 parser->arena,
3797 ++parser->node_id,
3798 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3799 PM_LOCATION_INIT_NODES(target, value),
3800 target,
3801 TOK2LOC(parser, operator),
3802 value
3803 );
3804}
3805
3809static pm_constant_and_write_node_t *
3810pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3811 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3812
3813 return pm_constant_and_write_node_new(
3814 parser->arena,
3815 ++parser->node_id,
3816 0,
3817 PM_LOCATION_INIT_NODES(target, value),
3818 target->name,
3819 target->base.location,
3820 TOK2LOC(parser, operator),
3821 value
3822 );
3823}
3824
3828static pm_constant_operator_write_node_t *
3829pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3830 return pm_constant_operator_write_node_new(
3831 parser->arena,
3832 ++parser->node_id,
3833 0,
3834 PM_LOCATION_INIT_NODES(target, value),
3835 target->name,
3836 target->base.location,
3837 TOK2LOC(parser, operator),
3838 value,
3839 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3840 );
3841}
3842
3846static pm_constant_or_write_node_t *
3847pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3848 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3849
3850 return pm_constant_or_write_node_new(
3851 parser->arena,
3852 ++parser->node_id,
3853 0,
3854 PM_LOCATION_INIT_NODES(target, value),
3855 target->name,
3856 target->base.location,
3857 TOK2LOC(parser, operator),
3858 value
3859 );
3860}
3861
3865static pm_constant_read_node_t *
3866pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
3867 assert(name->type == PM_TOKEN_CONSTANT || name->type == 0);
3868
3869 return pm_constant_read_node_new(
3870 parser->arena,
3871 ++parser->node_id,
3872 0,
3873 PM_LOCATION_INIT_TOKEN(parser, name),
3874 pm_parser_constant_id_token(parser, name)
3875 );
3876}
3877
3881static pm_constant_write_node_t *
3882pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3883 return pm_constant_write_node_new(
3884 parser->arena,
3885 ++parser->node_id,
3886 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3887 PM_LOCATION_INIT_NODES(target, value),
3888 target->name,
3889 target->base.location,
3890 value,
3891 TOK2LOC(parser, operator)
3892 );
3893}
3894
3898static void
3899pm_def_node_receiver_check(pm_parser_t *parser, const pm_node_t *node) {
3900 switch (PM_NODE_TYPE(node)) {
3901 case PM_BEGIN_NODE: {
3902 const pm_begin_node_t *cast = (pm_begin_node_t *) node;
3903 if (cast->statements != NULL) pm_def_node_receiver_check(parser, UP(cast->statements));
3904 break;
3905 }
3906 case PM_PARENTHESES_NODE: {
3907 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
3908 if (cast->body != NULL) pm_def_node_receiver_check(parser, cast->body);
3909 break;
3910 }
3911 case PM_STATEMENTS_NODE: {
3912 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3913 pm_def_node_receiver_check(parser, cast->body.nodes[cast->body.size - 1]);
3914 break;
3915 }
3916 case PM_ARRAY_NODE:
3917 case PM_FLOAT_NODE:
3918 case PM_IMAGINARY_NODE:
3919 case PM_INTEGER_NODE:
3920 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3921 case PM_INTERPOLATED_STRING_NODE:
3922 case PM_INTERPOLATED_SYMBOL_NODE:
3923 case PM_INTERPOLATED_X_STRING_NODE:
3924 case PM_RATIONAL_NODE:
3925 case PM_REGULAR_EXPRESSION_NODE:
3926 case PM_SOURCE_ENCODING_NODE:
3927 case PM_SOURCE_FILE_NODE:
3928 case PM_SOURCE_LINE_NODE:
3929 case PM_STRING_NODE:
3930 case PM_SYMBOL_NODE:
3931 case PM_X_STRING_NODE:
3932 pm_parser_err_node(parser, node, PM_ERR_SINGLETON_FOR_LITERALS);
3933 break;
3934 default:
3935 break;
3936 }
3937}
3938
3942static pm_def_node_t *
3943pm_def_node_create(
3944 pm_parser_t *parser,
3945 pm_constant_id_t name,
3946 const pm_token_t *name_loc,
3947 pm_node_t *receiver,
3948 pm_parameters_node_t *parameters,
3949 pm_node_t *body,
3950 pm_constant_id_list_t *locals,
3951 const pm_token_t *def_keyword,
3952 const pm_token_t *operator,
3953 const pm_token_t *lparen,
3954 const pm_token_t *rparen,
3955 const pm_token_t *equal,
3956 const pm_token_t *end_keyword
3957) {
3958 if (receiver != NULL) {
3959 pm_def_node_receiver_check(parser, receiver);
3960 }
3961
3962 return pm_def_node_new(
3963 parser->arena,
3964 ++parser->node_id,
3965 0,
3966 (end_keyword == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, def_keyword, body) : PM_LOCATION_INIT_TOKENS(parser, def_keyword, end_keyword),
3967 name,
3968 TOK2LOC(parser, name_loc),
3969 receiver,
3970 parameters,
3971 body,
3972 *locals,
3973 TOK2LOC(parser, def_keyword),
3974 NTOK2LOC(parser, operator),
3975 NTOK2LOC(parser, lparen),
3976 NTOK2LOC(parser, rparen),
3977 NTOK2LOC(parser, equal),
3978 NTOK2LOC(parser, end_keyword)
3979 );
3980}
3981
3985static pm_defined_node_t *
3986pm_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) {
3987 return pm_defined_node_new(
3988 parser->arena,
3989 ++parser->node_id,
3990 0,
3991 (rparen == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, value) : PM_LOCATION_INIT_TOKENS(parser, keyword, rparen),
3992 NTOK2LOC(parser, lparen),
3993 value,
3994 NTOK2LOC(parser, rparen),
3995 TOK2LOC(parser, keyword)
3996 );
3997}
3998
4002static pm_else_node_t *
4003pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
4004 return pm_else_node_new(
4005 parser->arena,
4006 ++parser->node_id,
4007 0,
4008 ((end_keyword == NULL) && (statements != NULL)) ? PM_LOCATION_INIT_TOKEN_NODE(parser, else_keyword, statements) : PM_LOCATION_INIT_TOKENS(parser, else_keyword, end_keyword),
4009 TOK2LOC(parser, else_keyword),
4010 statements,
4011 NTOK2LOC(parser, end_keyword)
4012 );
4013}
4014
4018static pm_embedded_statements_node_t *
4019pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
4020 return pm_embedded_statements_node_new(
4021 parser->arena,
4022 ++parser->node_id,
4023 0,
4024 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
4025 TOK2LOC(parser, opening),
4026 statements,
4027 TOK2LOC(parser, closing)
4028 );
4029}
4030
4034static pm_embedded_variable_node_t *
4035pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
4036 return pm_embedded_variable_node_new(
4037 parser->arena,
4038 ++parser->node_id,
4039 0,
4040 PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable),
4041 TOK2LOC(parser, operator),
4042 variable
4043 );
4044}
4045
4049static pm_ensure_node_t *
4050pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
4051 return pm_ensure_node_new(
4052 parser->arena,
4053 ++parser->node_id,
4054 0,
4055 PM_LOCATION_INIT_TOKENS(parser, ensure_keyword, end_keyword),
4056 TOK2LOC(parser, ensure_keyword),
4057 statements,
4058 TOK2LOC(parser, end_keyword)
4059 );
4060}
4061
4065static pm_false_node_t *
4066pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) {
4067 assert(token->type == PM_TOKEN_KEYWORD_FALSE);
4068
4069 return pm_false_node_new(
4070 parser->arena,
4071 ++parser->node_id,
4072 PM_NODE_FLAG_STATIC_LITERAL,
4073 PM_LOCATION_INIT_TOKEN(parser, token)
4074 );
4075}
4076
4081static pm_find_pattern_node_t *
4082pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) {
4083 assert(nodes->size >= 2);
4084 pm_node_t *left = nodes->nodes[0];
4085 pm_node_t *right = nodes->nodes[nodes->size - 1];
4086
4087 assert(PM_NODE_TYPE_P(left, PM_SPLAT_NODE));
4088 assert(PM_NODE_TYPE_P(right, PM_SPLAT_NODE));
4089
4090 pm_find_pattern_node_t *node = pm_find_pattern_node_new(
4091 parser->arena,
4092 ++parser->node_id,
4093 0,
4094 PM_LOCATION_INIT_NODES(left, right),
4095 NULL,
4096 (pm_splat_node_t *) left,
4097 ((pm_node_list_t) { 0 }),
4098 (pm_splat_node_t *) right,
4099 ((pm_location_t) { 0 }),
4100 ((pm_location_t) { 0 })
4101 );
4102
4103 // For now we're going to just copy over each pointer manually. This could be
4104 // much more efficient, as we could instead resize the node list to only point
4105 // to 1...-1.
4106 for (size_t index = 1; index < nodes->size - 1; index++) {
4107 pm_node_list_append_inline(parser->arena, &node->requireds, nodes->nodes[index]);
4108 }
4109
4110 return node;
4111}
4112
4117static double
4118pm_double_parse(pm_parser_t *parser, const pm_token_t *token) {
4119 ptrdiff_t diff = token->end - token->start;
4120 if (diff <= 0) return 0.0;
4121
4122 // First, get a buffer of the content.
4123 size_t length = (size_t) diff;
4124 const size_t buffer_size = sizeof(char) * (length + 1);
4125 char *buffer = xmalloc(buffer_size);
4126 if (buffer == NULL) PRISM_OOM();
4127
4128 memcpy((void *) buffer, token->start, length);
4129
4130 // Next, determine if we need to replace the decimal point because of
4131 // locale-specific options, and then normalize them if we have to.
4132 char decimal_point = *localeconv()->decimal_point;
4133 if (decimal_point != '.') {
4134 for (size_t index = 0; index < length; index++) {
4135 if (buffer[index] == '.') buffer[index] = decimal_point;
4136 }
4137 }
4138
4139 /*
4140 * Next, handle underscores by removing them from the buffer in a single
4141 * pass to avoid quadratic-time copies.
4142 */
4143 {
4144 size_t write = 0;
4145 for (size_t read = 0; read < length; read++) {
4146 if (buffer[read] != '_') buffer[write++] = buffer[read];
4147 }
4148 length = write;
4149 }
4150
4151 // Null-terminate the buffer so that strtod cannot read off the end.
4152 buffer[length] = '\0';
4153
4154 // Now, call strtod to parse the value. Note that CRuby has their own
4155 // version of strtod which avoids locales. We're okay using the locale-aware
4156 // version because we've already validated through the parser that the token
4157 // is in a valid format.
4158 errno = 0;
4159 char *eptr;
4160 double value = strtod(buffer, &eptr);
4161
4162 // This should never happen, because we've already checked that the token
4163 // is in a valid format. However it's good to be safe.
4164 if ((eptr != buffer + length) || (errno != 0 && errno != ERANGE)) {
4165 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, PM_ERR_FLOAT_PARSE);
4166 xfree_sized(buffer, buffer_size);
4167 return 0.0;
4168 }
4169
4170 // If errno is set, then it should only be ERANGE. At this point we need to
4171 // check if it's infinity (it should be).
4172 if (errno == ERANGE && PRISM_ISINF(value)) {
4173 int warn_width;
4174 const char *ellipsis;
4175
4176 if (length > 20) {
4177 warn_width = 20;
4178 ellipsis = "...";
4179 } else {
4180 warn_width = (int) length;
4181 ellipsis = "";
4182 }
4183
4184 pm_diagnostic_list_append_format(&parser->metadata_arena, &parser->warning_list, PM_TOKEN_START(parser, token), PM_TOKEN_LENGTH(token), PM_WARN_FLOAT_OUT_OF_RANGE, warn_width, (const char *) token->start, ellipsis);
4185 value = (value < 0.0) ? -HUGE_VAL : HUGE_VAL;
4186 }
4187
4188 // Finally we can free the buffer and return the value.
4189 xfree_sized(buffer, buffer_size);
4190 return value;
4191}
4192
4196static pm_float_node_t *
4197pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) {
4198 assert(token->type == PM_TOKEN_FLOAT);
4199
4200 return pm_float_node_new(
4201 parser->arena,
4202 ++parser->node_id,
4203 PM_NODE_FLAG_STATIC_LITERAL,
4204 PM_LOCATION_INIT_TOKEN(parser, token),
4205 pm_double_parse(parser, token)
4206 );
4207}
4208
4212static pm_imaginary_node_t *
4213pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
4214 assert(token->type == PM_TOKEN_FLOAT_IMAGINARY);
4215
4216 return pm_imaginary_node_new(
4217 parser->arena,
4218 ++parser->node_id,
4219 PM_NODE_FLAG_STATIC_LITERAL,
4220 PM_LOCATION_INIT_TOKEN(parser, token),
4221 UP(pm_float_node_create(parser, &((pm_token_t) {
4222 .type = PM_TOKEN_FLOAT,
4223 .start = token->start,
4224 .end = token->end - 1
4225 })))
4226 );
4227}
4228
4232static pm_rational_node_t *
4233pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
4234 assert(token->type == PM_TOKEN_FLOAT_RATIONAL);
4235
4236 pm_rational_node_t *node = pm_rational_node_new(
4237 parser->arena,
4238 ++parser->node_id,
4239 PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL,
4240 PM_LOCATION_INIT_TOKEN(parser, token),
4241 ((pm_integer_t) { 0 }),
4242 ((pm_integer_t) { 0 })
4243 );
4244
4245 const uint8_t *start = token->start;
4246 const uint8_t *end = token->end - 1; // r
4247
4248 while (start < end && *start == '0') start++; // 0.1 -> .1
4249 while (end > start && end[-1] == '0') end--; // 1.0 -> 1.
4250
4251 size_t length = (size_t) (end - start);
4252 if (length == 1) {
4253 node->denominator.value = 1;
4254 return node;
4255 }
4256
4257 const uint8_t *point = memchr(start, '.', length);
4258 assert(point && "should have a decimal point");
4259
4260 uint8_t *digits = xmalloc(length);
4261 if (digits == NULL) PRISM_OOM();
4262
4263 memcpy(digits, start, (unsigned long) (point - start));
4264 memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1));
4265 pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1);
4266
4267 size_t fract_length = 0;
4268 for (const uint8_t *fract = point; fract < end; ++fract) {
4269 if (*fract != '_') ++fract_length;
4270 }
4271 digits[0] = '1';
4272 if (fract_length > 1) memset(digits + 1, '0', fract_length - 1);
4273 pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + fract_length);
4274 xfree_sized(digits, length);
4275
4276 pm_integers_reduce(&node->numerator, &node->denominator);
4277 pm_integer_arena_move(parser->arena, &node->numerator);
4278 pm_integer_arena_move(parser->arena, &node->denominator);
4279 return node;
4280}
4281
4286static pm_imaginary_node_t *
4287pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
4288 assert(token->type == PM_TOKEN_FLOAT_RATIONAL_IMAGINARY);
4289
4290 return pm_imaginary_node_new(
4291 parser->arena,
4292 ++parser->node_id,
4293 PM_NODE_FLAG_STATIC_LITERAL,
4294 PM_LOCATION_INIT_TOKEN(parser, token),
4295 UP(pm_float_node_rational_create(parser, &((pm_token_t) {
4296 .type = PM_TOKEN_FLOAT_RATIONAL,
4297 .start = token->start,
4298 .end = token->end - 1
4299 })))
4300 );
4301}
4302
4306static pm_for_node_t *
4307pm_for_node_create(
4308 pm_parser_t *parser,
4309 pm_node_t *index,
4310 pm_node_t *collection,
4311 pm_statements_node_t *statements,
4312 const pm_token_t *for_keyword,
4313 const pm_token_t *in_keyword,
4314 const pm_token_t *do_keyword,
4315 const pm_token_t *end_keyword
4316) {
4317 return pm_for_node_new(
4318 parser->arena,
4319 ++parser->node_id,
4320 0,
4321 PM_LOCATION_INIT_TOKENS(parser, for_keyword, end_keyword),
4322 index,
4323 collection,
4324 statements,
4325 TOK2LOC(parser, for_keyword),
4326 TOK2LOC(parser, in_keyword),
4327 NTOK2LOC(parser, do_keyword),
4328 TOK2LOC(parser, end_keyword)
4329 );
4330}
4331
4335static pm_forwarding_arguments_node_t *
4336pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token) {
4337 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4338
4339 return pm_forwarding_arguments_node_new(
4340 parser->arena,
4341 ++parser->node_id,
4342 0,
4343 PM_LOCATION_INIT_TOKEN(parser, token)
4344 );
4345}
4346
4350static pm_forwarding_parameter_node_t *
4351pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
4352 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4353
4354 return pm_forwarding_parameter_node_new(
4355 parser->arena,
4356 ++parser->node_id,
4357 0,
4358 PM_LOCATION_INIT_TOKEN(parser, token)
4359 );
4360}
4361
4365static pm_forwarding_super_node_t *
4366pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm_arguments_t *arguments) {
4367 assert(arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_NODE));
4368 assert(token->type == PM_TOKEN_KEYWORD_SUPER);
4369
4370 pm_block_node_t *block = NULL;
4371 if (arguments->block != NULL) {
4372 block = (pm_block_node_t *) arguments->block;
4373 }
4374
4375 return pm_forwarding_super_node_new(
4376 parser->arena,
4377 ++parser->node_id,
4378 0,
4379 (block == NULL) ? PM_LOCATION_INIT_TOKEN(parser, token) : PM_LOCATION_INIT_TOKEN_NODE(parser, token, block),
4380 PM_LOCATION_INIT_TOKEN(parser, token),
4381 block
4382 );
4383}
4384
4389static pm_hash_pattern_node_t *
4390pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
4391 return pm_hash_pattern_node_new(
4392 parser->arena,
4393 ++parser->node_id,
4394 0,
4395 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
4396 NULL,
4397 ((pm_node_list_t) { 0 }),
4398 NULL,
4399 TOK2LOC(parser, opening),
4400 TOK2LOC(parser, closing)
4401 );
4402}
4403
4407static pm_hash_pattern_node_t *
4408pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *elements, pm_node_t *rest) {
4409 uint32_t start;
4410 uint32_t end;
4411
4412 if (elements->size > 0) {
4413 if (rest) {
4414 start = MIN(PM_NODE_START(rest), PM_NODE_START(elements->nodes[0]));
4415 end = MAX(PM_NODE_END(rest), PM_NODE_END(elements->nodes[elements->size - 1]));
4416 } else {
4417 start = PM_NODE_START(elements->nodes[0]);
4418 end = PM_NODE_END(elements->nodes[elements->size - 1]);
4419 }
4420 } else {
4421 assert(rest != NULL);
4422 start = PM_NODE_START(rest);
4423 end = PM_NODE_END(rest);
4424 }
4425
4426 pm_hash_pattern_node_t *node = pm_hash_pattern_node_new(
4427 parser->arena,
4428 ++parser->node_id,
4429 0,
4430 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4431 NULL,
4432 ((pm_node_list_t) { 0 }),
4433 rest,
4434 ((pm_location_t) { 0 }),
4435 ((pm_location_t) { 0 })
4436 );
4437
4438 pm_node_list_concat(parser->arena, &node->elements, elements);
4439 return node;
4440}
4441
4445static pm_constant_id_t
4446pm_global_variable_write_name(pm_parser_t *parser, const pm_node_t *target) {
4447 switch (PM_NODE_TYPE(target)) {
4448 case PM_GLOBAL_VARIABLE_READ_NODE:
4449 return ((pm_global_variable_read_node_t *) target)->name;
4450 case PM_BACK_REFERENCE_READ_NODE:
4451 return ((pm_back_reference_read_node_t *) target)->name;
4452 case PM_NUMBERED_REFERENCE_READ_NODE:
4453 // This will only ever happen in the event of a syntax error, but we
4454 // still need to provide something for the node.
4455 return pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target));
4456 default:
4457 assert(false && "unreachable");
4458 return (pm_constant_id_t) -1;
4459 }
4460}
4461
4465static pm_global_variable_and_write_node_t *
4466pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4467 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4468
4469 return pm_global_variable_and_write_node_new(
4470 parser->arena,
4471 ++parser->node_id,
4472 0,
4473 PM_LOCATION_INIT_NODES(target, value),
4474 pm_global_variable_write_name(parser, target),
4475 target->location,
4476 TOK2LOC(parser, operator),
4477 value
4478 );
4479}
4480
4484static pm_global_variable_operator_write_node_t *
4485pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4486 return pm_global_variable_operator_write_node_new(
4487 parser->arena,
4488 ++parser->node_id,
4489 0,
4490 PM_LOCATION_INIT_NODES(target, value),
4491 pm_global_variable_write_name(parser, target),
4492 target->location,
4493 TOK2LOC(parser, operator),
4494 value,
4495 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
4496 );
4497}
4498
4502static pm_global_variable_or_write_node_t *
4503pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4504 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4505
4506 return pm_global_variable_or_write_node_new(
4507 parser->arena,
4508 ++parser->node_id,
4509 0,
4510 PM_LOCATION_INIT_NODES(target, value),
4511 pm_global_variable_write_name(parser, target),
4512 target->location,
4513 TOK2LOC(parser, operator),
4514 value
4515 );
4516}
4517
4521static pm_global_variable_read_node_t *
4522pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
4523 return pm_global_variable_read_node_new(
4524 parser->arena,
4525 ++parser->node_id,
4526 0,
4527 PM_LOCATION_INIT_TOKEN(parser, name),
4528 pm_parser_constant_id_token(parser, name)
4529 );
4530}
4531
4535static pm_global_variable_read_node_t *
4536pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name) {
4537 return pm_global_variable_read_node_new(
4538 parser->arena,
4539 ++parser->node_id,
4540 0,
4541 PM_LOCATION_INIT_UNSET,
4542 name
4543 );
4544}
4545
4549static pm_global_variable_write_node_t *
4550pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4551 return pm_global_variable_write_node_new(
4552 parser->arena,
4553 ++parser->node_id,
4554 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
4555 PM_LOCATION_INIT_NODES(target, value),
4556 pm_global_variable_write_name(parser, target),
4557 target->location,
4558 value,
4559 TOK2LOC(parser, operator)
4560 );
4561}
4562
4566static pm_global_variable_write_node_t *
4567pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name, pm_node_t *value) {
4568 return pm_global_variable_write_node_new(
4569 parser->arena,
4570 ++parser->node_id,
4571 0,
4572 PM_LOCATION_INIT_UNSET,
4573 name,
4574 ((pm_location_t) { 0 }),
4575 value,
4576 ((pm_location_t) { 0 })
4577 );
4578}
4579
4583static pm_hash_node_t *
4584pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) {
4585 assert(opening != NULL);
4586
4587 return pm_hash_node_new(
4588 parser->arena,
4589 ++parser->node_id,
4590 PM_NODE_FLAG_STATIC_LITERAL,
4591 PM_LOCATION_INIT_TOKEN(parser, opening),
4592 TOK2LOC(parser, opening),
4593 ((pm_node_list_t) { 0 }),
4594 ((pm_location_t) { 0 })
4595 );
4596}
4597
4601static PRISM_INLINE void
4602pm_hash_node_elements_append(pm_arena_t *arena, pm_hash_node_t *hash, pm_node_t *element) {
4603 pm_node_list_append_inline(arena, &hash->elements, element);
4604
4605 bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE);
4606 if (static_literal) {
4607 pm_assoc_node_t *assoc = (pm_assoc_node_t *) element;
4608 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);
4609 static_literal = static_literal && PM_NODE_FLAG_P(assoc->key, PM_NODE_FLAG_STATIC_LITERAL);
4610 static_literal = static_literal && PM_NODE_FLAG_P(assoc, PM_NODE_FLAG_STATIC_LITERAL);
4611 }
4612
4613 if (!static_literal) {
4614 pm_node_flag_unset(UP(hash), PM_NODE_FLAG_STATIC_LITERAL);
4615 }
4616}
4617
4618static PRISM_INLINE void
4619pm_hash_node_closing_loc_set(const pm_parser_t *parser, pm_hash_node_t *hash, pm_token_t *token) {
4620 PM_NODE_LENGTH_SET_TOKEN(parser, hash, token);
4621 hash->closing_loc = TOK2LOC(parser, token);
4622}
4623
4627static pm_if_node_t *
4628pm_if_node_create(pm_parser_t *parser,
4629 const pm_token_t *if_keyword,
4630 pm_node_t *predicate,
4631 const pm_token_t *then_keyword,
4632 pm_statements_node_t *statements,
4633 pm_node_t *subsequent,
4634 const pm_token_t *end_keyword
4635) {
4636 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4637
4638 uint32_t start = PM_TOKEN_START(parser, if_keyword);
4639 uint32_t end;
4640
4641 if (end_keyword != NULL) {
4642 end = PM_TOKEN_END(parser, end_keyword);
4643 } else if (subsequent != NULL) {
4644 end = PM_NODE_END(subsequent);
4645 } else if (pm_statements_node_body_length(statements) != 0) {
4646 end = PM_NODE_END(statements);
4647 } else {
4648 end = PM_NODE_END(predicate);
4649 }
4650
4651 return pm_if_node_new(
4652 parser->arena,
4653 ++parser->node_id,
4654 PM_NODE_FLAG_NEWLINE,
4655 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4656 TOK2LOC(parser, if_keyword),
4657 predicate,
4658 NTOK2LOC(parser, then_keyword),
4659 statements,
4660 subsequent,
4661 NTOK2LOC(parser, end_keyword)
4662 );
4663}
4664
4668static pm_if_node_t *
4669pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *if_keyword, pm_node_t *predicate) {
4670 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4671
4672 pm_statements_node_t *statements = pm_statements_node_create(parser);
4673 pm_statements_node_body_append(parser, statements, statement, true);
4674
4675 return pm_if_node_new(
4676 parser->arena,
4677 ++parser->node_id,
4678 PM_NODE_FLAG_NEWLINE,
4679 PM_LOCATION_INIT_NODES(statement, predicate),
4680 TOK2LOC(parser, if_keyword),
4681 predicate,
4682 ((pm_location_t) { 0 }),
4683 statements,
4684 NULL,
4685 ((pm_location_t) { 0 })
4686 );
4687}
4688
4692static pm_if_node_t *
4693pm_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) {
4694 pm_assert_value_expression(parser, predicate);
4695 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4696
4697 pm_statements_node_t *if_statements = pm_statements_node_create(parser);
4698 pm_statements_node_body_append(parser, if_statements, true_expression, true);
4699
4700 pm_statements_node_t *else_statements = pm_statements_node_create(parser);
4701 pm_statements_node_body_append(parser, else_statements, false_expression, true);
4702
4703 pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, NULL);
4704 return pm_if_node_new(
4705 parser->arena,
4706 ++parser->node_id,
4707 PM_NODE_FLAG_NEWLINE,
4708 PM_LOCATION_INIT_NODES(predicate, false_expression),
4709 ((pm_location_t) { 0 }),
4710 predicate,
4711 TOK2LOC(parser, qmark),
4712 if_statements,
4713 UP(else_node),
4714 ((pm_location_t) { 0 })
4715 );
4716}
4717
4718static PRISM_INLINE void
4719pm_if_node_end_keyword_loc_set(const pm_parser_t *parser, pm_if_node_t *node, const pm_token_t *keyword) {
4720 PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword);
4721 node->end_keyword_loc = TOK2LOC(parser, keyword);
4722}
4723
4724static PRISM_INLINE void
4725pm_else_node_end_keyword_loc_set(const pm_parser_t *parser, pm_else_node_t *node, const pm_token_t *keyword) {
4726 PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword);
4727 node->end_keyword_loc = TOK2LOC(parser, keyword);
4728}
4729
4733static pm_implicit_node_t *
4734pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) {
4735 return pm_implicit_node_new(
4736 parser->arena,
4737 ++parser->node_id,
4738 0,
4739 PM_LOCATION_INIT_NODE(value),
4740 value
4741 );
4742}
4743
4747static pm_implicit_rest_node_t *
4748pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) {
4749 assert(token->type == PM_TOKEN_COMMA);
4750
4751 return pm_implicit_rest_node_new(
4752 parser->arena,
4753 ++parser->node_id,
4754 0,
4755 PM_LOCATION_INIT_TOKEN(parser, token)
4756 );
4757}
4758
4762static pm_integer_node_t *
4763pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4764 assert(token->type == PM_TOKEN_INTEGER);
4765
4766 pm_integer_node_t *node = pm_integer_node_new(
4767 parser->arena,
4768 ++parser->node_id,
4769 base | PM_NODE_FLAG_STATIC_LITERAL,
4770 PM_LOCATION_INIT_TOKEN(parser, token),
4771 ((pm_integer_t) { 0 })
4772 );
4773
4774 if (parser->integer.lexed) {
4775 // The value was already computed during lexing.
4776 node->value.value = parser->integer.value;
4777 parser->integer.lexed = false;
4778 } else {
4779 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4780 switch (base) {
4781 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4782 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4783 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4784 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4785 default: assert(false && "unreachable"); break;
4786 }
4787
4788 pm_integer_parse(&node->value, integer_base, token->start, token->end);
4789 pm_integer_arena_move(parser->arena, &node->value);
4790 }
4791
4792 return node;
4793}
4794
4799static pm_imaginary_node_t *
4800pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4801 assert(token->type == PM_TOKEN_INTEGER_IMAGINARY);
4802
4803 return pm_imaginary_node_new(
4804 parser->arena,
4805 ++parser->node_id,
4806 PM_NODE_FLAG_STATIC_LITERAL,
4807 PM_LOCATION_INIT_TOKEN(parser, token),
4808 UP(pm_integer_node_create(parser, base, &((pm_token_t) {
4809 .type = PM_TOKEN_INTEGER,
4810 .start = token->start,
4811 .end = token->end - 1
4812 })))
4813 );
4814}
4815
4820static pm_rational_node_t *
4821pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4822 assert(token->type == PM_TOKEN_INTEGER_RATIONAL);
4823
4824 pm_rational_node_t *node = pm_rational_node_new(
4825 parser->arena,
4826 ++parser->node_id,
4827 base | PM_NODE_FLAG_STATIC_LITERAL,
4828 PM_LOCATION_INIT_TOKEN(parser, token),
4829 ((pm_integer_t) { 0 }),
4830 ((pm_integer_t) { .value = 1 })
4831 );
4832
4833 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4834 switch (base) {
4835 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4836 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4837 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4838 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4839 default: assert(false && "unreachable"); break;
4840 }
4841
4842 pm_integer_parse(&node->numerator, integer_base, token->start, token->end - 1);
4843 pm_integer_arena_move(parser->arena, &node->numerator);
4844
4845 return node;
4846}
4847
4852static pm_imaginary_node_t *
4853pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4854 assert(token->type == PM_TOKEN_INTEGER_RATIONAL_IMAGINARY);
4855
4856 return pm_imaginary_node_new(
4857 parser->arena,
4858 ++parser->node_id,
4859 PM_NODE_FLAG_STATIC_LITERAL,
4860 PM_LOCATION_INIT_TOKEN(parser, token),
4861 UP(pm_integer_node_rational_create(parser, base, &((pm_token_t) {
4862 .type = PM_TOKEN_INTEGER_RATIONAL,
4863 .start = token->start,
4864 .end = token->end - 1
4865 })))
4866 );
4867}
4868
4872static pm_in_node_t *
4873pm_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) {
4874 uint32_t start = PM_TOKEN_START(parser, in_keyword);
4875 uint32_t end;
4876
4877 if (statements != NULL) {
4878 end = PM_NODE_END(statements);
4879 } else if (then_keyword != NULL) {
4880 end = PM_TOKEN_END(parser, then_keyword);
4881 } else {
4882 end = PM_NODE_END(pattern);
4883 }
4884
4885 return pm_in_node_new(
4886 parser->arena,
4887 ++parser->node_id,
4888 0,
4889 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4890 pattern,
4891 statements,
4892 TOK2LOC(parser, in_keyword),
4893 NTOK2LOC(parser, then_keyword)
4894 );
4895}
4896
4900static pm_instance_variable_and_write_node_t *
4901pm_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) {
4902 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4903
4904 return pm_instance_variable_and_write_node_new(
4905 parser->arena,
4906 ++parser->node_id,
4907 0,
4908 PM_LOCATION_INIT_NODES(target, value),
4909 target->name,
4910 target->base.location,
4911 TOK2LOC(parser, operator),
4912 value
4913 );
4914}
4915
4919static pm_instance_variable_operator_write_node_t *
4920pm_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) {
4921 return pm_instance_variable_operator_write_node_new(
4922 parser->arena,
4923 ++parser->node_id,
4924 0,
4925 PM_LOCATION_INIT_NODES(target, value),
4926 target->name,
4927 target->base.location,
4928 TOK2LOC(parser, operator),
4929 value,
4930 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
4931 );
4932}
4933
4937static pm_instance_variable_or_write_node_t *
4938pm_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) {
4939 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4940
4941 return pm_instance_variable_or_write_node_new(
4942 parser->arena,
4943 ++parser->node_id,
4944 0,
4945 PM_LOCATION_INIT_NODES(target, value),
4946 target->name,
4947 target->base.location,
4948 TOK2LOC(parser, operator),
4949 value
4950 );
4951}
4952
4956static pm_instance_variable_read_node_t *
4957pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
4958 assert(token->type == PM_TOKEN_INSTANCE_VARIABLE);
4959
4960 return pm_instance_variable_read_node_new(
4961 parser->arena,
4962 ++parser->node_id,
4963 0,
4964 PM_LOCATION_INIT_TOKEN(parser, token),
4965 pm_parser_constant_id_token(parser, token)
4966 );
4967}
4968
4973static pm_instance_variable_write_node_t *
4974pm_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) {
4975 return pm_instance_variable_write_node_new(
4976 parser->arena,
4977 ++parser->node_id,
4978 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
4979 PM_LOCATION_INIT_NODES(read_node, value),
4980 read_node->name,
4981 read_node->base.location,
4982 value,
4983 TOK2LOC(parser, operator)
4984 );
4985}
4986
4992static void
4993pm_interpolated_node_append(pm_arena_t *arena, pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) {
4994 switch (PM_NODE_TYPE(part)) {
4995 case PM_STRING_NODE:
4996 pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
4997 break;
4998 case PM_EMBEDDED_STATEMENTS_NODE: {
4999 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
5000 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
5001
5002 if (embedded == NULL) {
5003 // If there are no statements or more than one statement, then
5004 // we lose the static literal flag.
5005 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
5006 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
5007 // If the embedded statement is a string, then we can keep the
5008 // static literal flag and mark the string as frozen.
5009 pm_node_flag_set(embedded, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
5010 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
5011 // If the embedded statement is an interpolated string and it's
5012 // a static literal, then we can keep the static literal flag.
5013 } else {
5014 // Otherwise we lose the static literal flag.
5015 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
5016 }
5017
5018 break;
5019 }
5020 case PM_EMBEDDED_VARIABLE_NODE:
5021 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
5022 break;
5023 default:
5024 assert(false && "unexpected node type");
5025 break;
5026 }
5027
5028 pm_node_list_append_inline(arena, parts, part);
5029}
5030
5034static pm_interpolated_regular_expression_node_t *
5035pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening) {
5036 return pm_interpolated_regular_expression_node_new(
5037 parser->arena,
5038 ++parser->node_id,
5039 PM_NODE_FLAG_STATIC_LITERAL,
5040 PM_LOCATION_INIT_TOKEN(parser, opening),
5041 TOK2LOC(parser, opening),
5042 ((pm_node_list_t) { 0 }),
5043 TOK2LOC(parser, opening)
5044 );
5045}
5046
5047static PRISM_INLINE void
5048pm_interpolated_regular_expression_node_append(pm_arena_t *arena, pm_interpolated_regular_expression_node_t *node, pm_node_t *part) {
5049 if (PM_NODE_START(node) > PM_NODE_START(part)) {
5050 PM_NODE_START_SET_NODE(node, part);
5051 }
5052 if (PM_NODE_END(node) < PM_NODE_END(part)) {
5053 PM_NODE_LENGTH_SET_NODE(node, part);
5054 }
5055
5056 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5057}
5058
5059static PRISM_INLINE void
5060pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) {
5061 node->closing_loc = TOK2LOC(parser, closing);
5062 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5063 pm_node_flag_set(UP(node), pm_regular_expression_flags_create(parser, closing));
5064}
5065
5089static PRISM_INLINE void
5090pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_node_t *node, pm_node_t *part) {
5091 pm_arena_t *arena = parser->arena;
5092#define CLEAR_FLAGS(node) \
5093 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))
5094
5095#define MUTABLE_FLAGS(node) \
5096 node->base.flags = (pm_node_flags_t) ((FL(node) | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN);
5097
5098 if (node->parts.size == 0 && node->opening_loc.length == 0) {
5099 PM_NODE_START_SET_NODE(node, part);
5100 }
5101
5102 if (PM_NODE_END(part) > PM_NODE_END(node)) {
5103 PM_NODE_LENGTH_SET_NODE(node, part);
5104 }
5105
5106 switch (PM_NODE_TYPE(part)) {
5107 case PM_STRING_NODE:
5108 // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags,
5109 // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for
5110 // as long as this interpolation only consists of other string literals.
5111 if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
5112 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
5113 }
5114 part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5115 break;
5116 case PM_INTERPOLATED_STRING_NODE:
5117 if (PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
5118 // If the string that we're concatenating is a static literal,
5119 // then we can keep the static literal flag for this string.
5120 } else {
5121 // Otherwise, we lose the static literal flag here and we should
5122 // also clear the mutability flags.
5123 CLEAR_FLAGS(node);
5124 }
5125 break;
5126 case PM_EMBEDDED_STATEMENTS_NODE: {
5127 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
5128 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
5129
5130 if (embedded == NULL) {
5131 // If we're embedding multiple statements or no statements, then
5132 // the string is not longer a static literal.
5133 CLEAR_FLAGS(node);
5134 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
5135 // If the embedded statement is a string, then we can make that
5136 // string as frozen and static literal, and not touch the static
5137 // literal status of this string.
5138 embedded->flags = (pm_node_flags_t) ((embedded->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5139
5140 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
5141 MUTABLE_FLAGS(node);
5142 }
5143 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
5144 // If the embedded statement is an interpolated string, but that
5145 // string is marked as static literal, then we can keep our
5146 // static literal status for this string.
5147 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
5148 MUTABLE_FLAGS(node);
5149 }
5150 } else {
5151 // In all other cases, we lose the static literal flag here and
5152 // become mutable.
5153 CLEAR_FLAGS(node);
5154 }
5155
5156 break;
5157 }
5158 case PM_EMBEDDED_VARIABLE_NODE:
5159 // Embedded variables clear static literal, which means we also
5160 // should clear the mutability flags.
5161 CLEAR_FLAGS(node);
5162 break;
5163 case PM_X_STRING_NODE:
5164 case PM_INTERPOLATED_X_STRING_NODE:
5165 case PM_SYMBOL_NODE:
5166 case PM_INTERPOLATED_SYMBOL_NODE:
5167 // These will only happen in error cases. But we want to handle it
5168 // here so that we don't fail the assertion.
5169 CLEAR_FLAGS(node);
5170 pm_node_list_append_inline(arena, &node->parts, UP(pm_error_recovery_node_create_unexpected(parser, part)));
5171 return;
5172 case PM_ERROR_RECOVERY_NODE:
5173 CLEAR_FLAGS(node);
5174 break;
5175 default:
5176 assert(false && "unexpected node type");
5177 break;
5178 }
5179
5180 pm_node_list_append_inline(arena, &node->parts, part);
5181
5182#undef CLEAR_FLAGS
5183#undef MUTABLE_FLAGS
5184}
5185
5189static pm_interpolated_string_node_t *
5190pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
5191 pm_node_flags_t flags = PM_NODE_FLAG_STATIC_LITERAL;
5192
5193 switch (parser->frozen_string_literal) {
5194 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
5195 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE;
5196 break;
5197 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
5198 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN;
5199 break;
5200 }
5201
5202 uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening);
5203 uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing);
5204
5205 pm_interpolated_string_node_t *node = pm_interpolated_string_node_new(
5206 parser->arena,
5207 ++parser->node_id,
5208 flags,
5209 ((pm_location_t) { .start = start, .length = U32(end - start) }),
5210 NTOK2LOC(parser, opening),
5211 ((pm_node_list_t) { 0 }),
5212 NTOK2LOC(parser, closing)
5213 );
5214
5215 if (parts != NULL) {
5216 pm_node_t *part;
5217 PM_NODE_LIST_FOREACH(parts, index, part) {
5218 pm_interpolated_string_node_append(parser, node, part);
5219 }
5220 }
5221
5222 return node;
5223}
5224
5228static void
5229pm_interpolated_string_node_closing_set(const pm_parser_t *parser, pm_interpolated_string_node_t *node, const pm_token_t *closing) {
5230 node->closing_loc = TOK2LOC(parser, closing);
5231 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5232}
5233
5234static void
5235pm_interpolated_symbol_node_append(pm_arena_t *arena, pm_interpolated_symbol_node_t *node, pm_node_t *part) {
5236 if (node->parts.size == 0 && node->opening_loc.length == 0) {
5237 PM_NODE_START_SET_NODE(node, part);
5238 }
5239
5240 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5241
5242 if (PM_NODE_END(part) > PM_NODE_END(node)) {
5243 PM_NODE_LENGTH_SET_NODE(node, part);
5244 }
5245}
5246
5247static void
5248pm_interpolated_symbol_node_closing_loc_set(const pm_parser_t *parser, pm_interpolated_symbol_node_t *node, const pm_token_t *closing) {
5249 node->closing_loc = TOK2LOC(parser, closing);
5250 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5251}
5252
5256static pm_interpolated_symbol_node_t *
5257pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
5258 uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening);
5259 uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing);
5260
5261 pm_interpolated_symbol_node_t *node = pm_interpolated_symbol_node_new(
5262 parser->arena,
5263 ++parser->node_id,
5264 PM_NODE_FLAG_STATIC_LITERAL,
5265 ((pm_location_t) { .start = start, .length = U32(end - start) }),
5266 NTOK2LOC(parser, opening),
5267 ((pm_node_list_t) { 0 }),
5268 NTOK2LOC(parser, closing)
5269 );
5270
5271 if (parts != NULL) {
5272 pm_node_t *part;
5273 PM_NODE_LIST_FOREACH(parts, index, part) {
5274 pm_interpolated_symbol_node_append(parser->arena, node, part);
5275 }
5276 }
5277
5278 return node;
5279}
5280
5284static pm_interpolated_x_string_node_t *
5285pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
5286 return pm_interpolated_x_string_node_new(
5287 parser->arena,
5288 ++parser->node_id,
5289 0,
5290 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
5291 TOK2LOC(parser, opening),
5292 ((pm_node_list_t) { 0 }),
5293 TOK2LOC(parser, closing)
5294 );
5295}
5296
5297static PRISM_INLINE void
5298pm_interpolated_xstring_node_append(pm_arena_t *arena, pm_interpolated_x_string_node_t *node, pm_node_t *part) {
5299 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5300 PM_NODE_LENGTH_SET_NODE(node, part);
5301}
5302
5303static PRISM_INLINE void
5304pm_interpolated_xstring_node_closing_set(const pm_parser_t *parser, pm_interpolated_x_string_node_t *node, const pm_token_t *closing) {
5305 node->closing_loc = TOK2LOC(parser, closing);
5306 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5307}
5308
5312static pm_it_local_variable_read_node_t *
5313pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
5314 return pm_it_local_variable_read_node_new(
5315 parser->arena,
5316 ++parser->node_id,
5317 0,
5318 PM_LOCATION_INIT_TOKEN(parser, name)
5319 );
5320}
5321
5325static pm_it_parameters_node_t *
5326pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
5327 return pm_it_parameters_node_new(
5328 parser->arena,
5329 ++parser->node_id,
5330 0,
5331 PM_LOCATION_INIT_TOKENS(parser, opening, closing)
5332 );
5333}
5334
5338static pm_keyword_hash_node_t *
5339pm_keyword_hash_node_create(pm_parser_t *parser) {
5340 return pm_keyword_hash_node_new(
5341 parser->arena,
5342 ++parser->node_id,
5343 PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS,
5344 PM_LOCATION_INIT_UNSET,
5345 ((pm_node_list_t) { 0 })
5346 );
5347}
5348
5352static void
5353pm_keyword_hash_node_elements_append(pm_arena_t *arena, pm_keyword_hash_node_t *hash, pm_node_t *element) {
5354 // If the element being added is not an AssocNode or does not have a symbol
5355 // key, then we want to turn the SYMBOL_KEYS flag off.
5356 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_TYPE_P(((pm_assoc_node_t *) element)->key, PM_SYMBOL_NODE)) {
5357 pm_node_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS);
5358 }
5359
5360 pm_node_list_append_inline(arena, &hash->elements, element);
5361 if (PM_NODE_LENGTH(hash) == 0) {
5362 PM_NODE_START_SET_NODE(hash, element);
5363 }
5364 PM_NODE_LENGTH_SET_NODE(hash, element);
5365}
5366
5370static pm_required_keyword_parameter_node_t *
5371pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name) {
5372 return pm_required_keyword_parameter_node_new(
5373 parser->arena,
5374 ++parser->node_id,
5375 0,
5376 PM_LOCATION_INIT_TOKEN(parser, name),
5377 pm_parser_constant_id_raw(parser, name->start, name->end - 1),
5378 TOK2LOC(parser, name)
5379 );
5380}
5381
5385static pm_optional_keyword_parameter_node_t *
5386pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, pm_node_t *value) {
5387 return pm_optional_keyword_parameter_node_new(
5388 parser->arena,
5389 ++parser->node_id,
5390 0,
5391 PM_LOCATION_INIT_TOKEN_NODE(parser, name, value),
5392 pm_parser_constant_id_raw(parser, name->start, name->end - 1),
5393 TOK2LOC(parser, name),
5394 value
5395 );
5396}
5397
5401static pm_keyword_rest_parameter_node_t *
5402pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
5403 return pm_keyword_rest_parameter_node_new(
5404 parser->arena,
5405 ++parser->node_id,
5406 0,
5407 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
5408 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
5409 NTOK2LOC(parser, name),
5410 TOK2LOC(parser, operator)
5411 );
5412}
5413
5417static pm_lambda_node_t *
5418pm_lambda_node_create(
5419 pm_parser_t *parser,
5420 pm_constant_id_list_t *locals,
5421 const pm_token_t *operator,
5422 const pm_token_t *opening,
5423 const pm_token_t *closing,
5424 pm_node_t *parameters,
5425 pm_node_t *body
5426) {
5427 return pm_lambda_node_new(
5428 parser->arena,
5429 ++parser->node_id,
5430 0,
5431 PM_LOCATION_INIT_TOKENS(parser, operator, closing),
5432 *locals,
5433 TOK2LOC(parser, operator),
5434 TOK2LOC(parser, opening),
5435 TOK2LOC(parser, closing),
5436 parameters,
5437 body
5438 );
5439}
5440
5444static pm_local_variable_and_write_node_t *
5445pm_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) {
5446 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));
5447 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
5448
5449 return pm_local_variable_and_write_node_new(
5450 parser->arena,
5451 ++parser->node_id,
5452 0,
5453 PM_LOCATION_INIT_NODES(target, value),
5454 target->location,
5455 TOK2LOC(parser, operator),
5456 value,
5457 name,
5458 depth
5459 );
5460}
5461
5465static pm_local_variable_operator_write_node_t *
5466pm_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) {
5467 return pm_local_variable_operator_write_node_new(
5468 parser->arena,
5469 ++parser->node_id,
5470 0,
5471 PM_LOCATION_INIT_NODES(target, value),
5472 target->location,
5473 TOK2LOC(parser, operator),
5474 value,
5475 name,
5476 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
5477 depth
5478 );
5479}
5480
5484static pm_local_variable_or_write_node_t *
5485pm_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) {
5486 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));
5487 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
5488
5489 return pm_local_variable_or_write_node_new(
5490 parser->arena,
5491 ++parser->node_id,
5492 0,
5493 PM_LOCATION_INIT_NODES(target, value),
5494 target->location,
5495 TOK2LOC(parser, operator),
5496 value,
5497 name,
5498 depth
5499 );
5500}
5501
5505static pm_local_variable_read_node_t *
5506pm_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) {
5507 if (!missing) pm_locals_read(&pm_parser_scope_find(parser, depth)->locals, name_id);
5508
5509 return pm_local_variable_read_node_new(
5510 parser->arena,
5511 ++parser->node_id,
5512 0,
5513 PM_LOCATION_INIT_TOKEN(parser, name),
5514 name_id,
5515 depth
5516 );
5517}
5518
5522static pm_local_variable_read_node_t *
5523pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5524 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5525 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, false);
5526}
5527
5532static pm_local_variable_read_node_t *
5533pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5534 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5535 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, true);
5536}
5537
5541static pm_local_variable_write_node_t *
5542pm_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) {
5543 return pm_local_variable_write_node_new(
5544 parser->arena,
5545 ++parser->node_id,
5546 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5547 ((pm_location_t) { .start = name_loc->start, .length = PM_NODE_END(value) - name_loc->start }),
5548 name,
5549 depth,
5550 *name_loc,
5551 value,
5552 TOK2LOC(parser, operator)
5553 );
5554}
5555
5559static PRISM_INLINE bool
5560pm_token_is_it(const uint8_t *start, const uint8_t *end) {
5561 return (end - start == 2) && (start[0] == 'i') && (start[1] == 't');
5562}
5563
5568static PRISM_INLINE bool
5569pm_token_is_numbered_parameter(const pm_parser_t *parser, uint32_t start, uint32_t length) {
5570 return (
5571 (length == 2) &&
5572 (parser->start[start] == '_') &&
5573 (parser->start[start + 1] != '0') &&
5574 pm_char_is_decimal_digit(parser->start[start + 1])
5575 );
5576}
5577
5582static PRISM_INLINE void
5583pm_refute_numbered_parameter(pm_parser_t *parser, uint32_t start, uint32_t length) {
5584 if (pm_token_is_numbered_parameter(parser, start, length)) {
5585 PM_PARSER_ERR_FORMAT(parser, start, length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + start);
5586 }
5587}
5588
5593static pm_local_variable_target_node_t *
5594pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *location, pm_constant_id_t name, uint32_t depth) {
5595 pm_refute_numbered_parameter(parser, location->start, location->length);
5596
5597 return pm_local_variable_target_node_new(
5598 parser->arena,
5599 ++parser->node_id,
5600 0,
5601 ((pm_location_t) { .start = location->start, .length = location->length }),
5602 name,
5603 depth
5604 );
5605}
5606
5610static pm_match_predicate_node_t *
5611pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5612 pm_assert_value_expression(parser, value);
5613
5614 return pm_match_predicate_node_new(
5615 parser->arena,
5616 ++parser->node_id,
5617 0,
5618 PM_LOCATION_INIT_NODES(value, pattern),
5619 value,
5620 pattern,
5621 TOK2LOC(parser, operator)
5622 );
5623}
5624
5628static pm_match_required_node_t *
5629pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5630 pm_assert_value_expression(parser, value);
5631
5632 return pm_match_required_node_new(
5633 parser->arena,
5634 ++parser->node_id,
5635 0,
5636 PM_LOCATION_INIT_NODES(value, pattern),
5637 value,
5638 pattern,
5639 TOK2LOC(parser, operator)
5640 );
5641}
5642
5646static pm_match_write_node_t *
5647pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) {
5648 return pm_match_write_node_new(
5649 parser->arena,
5650 ++parser->node_id,
5651 0,
5652 PM_LOCATION_INIT_NODE(call),
5653 call,
5654 ((pm_node_list_t) { 0 })
5655 );
5656}
5657
5661static pm_module_node_t *
5662pm_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) {
5663 pm_constant_id_list_t module_locals = { .ids = NULL, .size = 0, .capacity = 0 };
5664 if (locals != NULL) module_locals = *locals;
5665
5666 return pm_module_node_new(
5667 parser->arena,
5668 ++parser->node_id,
5669 0,
5670 PM_LOCATION_INIT_TOKENS(parser, module_keyword, end_keyword),
5671 module_locals,
5672 TOK2LOC(parser, module_keyword),
5673 constant_path,
5674 body,
5675 TOK2LOC(parser, end_keyword),
5676 pm_parser_constant_id_token(parser, name)
5677 );
5678}
5679
5683static pm_multi_target_node_t *
5684pm_multi_target_node_create(pm_parser_t *parser) {
5685 return pm_multi_target_node_new(
5686 parser->arena,
5687 ++parser->node_id,
5688 0,
5689 PM_LOCATION_INIT_UNSET,
5690 ((pm_node_list_t) { 0 }),
5691 NULL,
5692 ((pm_node_list_t) { 0 }),
5693 ((pm_location_t) { 0 }),
5694 ((pm_location_t) { 0 })
5695 );
5696}
5697
5701static void
5702pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t *node, pm_node_t *target) {
5703 if (PM_NODE_TYPE_P(target, PM_SPLAT_NODE)) {
5704 if (node->rest == NULL) {
5705 node->rest = target;
5706 } else {
5707 pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
5708 pm_node_list_append_inline(parser->arena, &node->rights, target);
5709 }
5710 } else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
5711 if (node->rest == NULL) {
5712 node->rest = target;
5713 } else {
5714 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
5715 pm_node_list_append_inline(parser->arena, &node->rights, target);
5716 }
5717 } else if (node->rest == NULL) {
5718 pm_node_list_append_inline(parser->arena, &node->lefts, target);
5719 } else {
5720 pm_node_list_append_inline(parser->arena, &node->rights, target);
5721 }
5722
5723 if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_START(node) > PM_NODE_START(target))) {
5724 PM_NODE_START_SET_NODE(node, target);
5725 }
5726
5727 if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_END(node) < PM_NODE_END(target))) {
5728 PM_NODE_LENGTH_SET_NODE(node, target);
5729 }
5730}
5731
5735static void
5736pm_multi_target_node_opening_set(const pm_parser_t *parser, pm_multi_target_node_t *node, const pm_token_t *lparen) {
5737 PM_NODE_START_SET_TOKEN(parser, node, lparen);
5738 PM_NODE_LENGTH_SET_TOKEN(parser, node, lparen);
5739 node->lparen_loc = TOK2LOC(parser, lparen);
5740}
5741
5745static void
5746pm_multi_target_node_closing_set(const pm_parser_t *parser, pm_multi_target_node_t *node, const pm_token_t *rparen) {
5747 PM_NODE_LENGTH_SET_TOKEN(parser, node, rparen);
5748 node->rparen_loc = TOK2LOC(parser, rparen);
5749}
5750
5754static pm_multi_write_node_t *
5755pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, const pm_token_t *operator, pm_node_t *value) {
5756 /* The target is no longer necessary because we have reused its children. It
5757 * is arena-allocated so no explicit free is needed. */
5758 return pm_multi_write_node_new(
5759 parser->arena,
5760 ++parser->node_id,
5761 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5762 PM_LOCATION_INIT_NODES(target, value),
5763 target->lefts,
5764 target->rest,
5765 target->rights,
5766 target->lparen_loc,
5767 target->rparen_loc,
5768 TOK2LOC(parser, operator),
5769 value
5770 );
5771}
5772
5776static pm_next_node_t *
5777pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
5778 assert(keyword->type == PM_TOKEN_KEYWORD_NEXT);
5779
5780 return pm_next_node_new(
5781 parser->arena,
5782 ++parser->node_id,
5783 0,
5784 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
5785 arguments,
5786 TOK2LOC(parser, keyword)
5787 );
5788}
5789
5793static pm_nil_node_t *
5794pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) {
5795 assert(token->type == PM_TOKEN_KEYWORD_NIL);
5796
5797 return pm_nil_node_new(
5798 parser->arena,
5799 ++parser->node_id,
5800 PM_NODE_FLAG_STATIC_LITERAL,
5801 PM_LOCATION_INIT_TOKEN(parser, token)
5802 );
5803}
5804
5808static pm_no_block_parameter_node_t *
5809pm_no_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
5810 assert(operator->type == PM_TOKEN_AMPERSAND || operator->type == PM_TOKEN_UAMPERSAND);
5811 assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
5812
5813 return pm_no_block_parameter_node_new(
5814 parser->arena,
5815 ++parser->node_id,
5816 0,
5817 PM_LOCATION_INIT_TOKENS(parser, operator, keyword),
5818 TOK2LOC(parser, operator),
5819 TOK2LOC(parser, keyword)
5820 );
5821}
5822
5826static pm_no_keywords_parameter_node_t *
5827pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
5828 assert(operator->type == PM_TOKEN_USTAR_STAR || operator->type == PM_TOKEN_STAR_STAR);
5829 assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
5830
5831 return pm_no_keywords_parameter_node_new(
5832 parser->arena,
5833 ++parser->node_id,
5834 0,
5835 PM_LOCATION_INIT_TOKENS(parser, operator, keyword),
5836 TOK2LOC(parser, operator),
5837 TOK2LOC(parser, keyword)
5838 );
5839}
5840
5844static pm_numbered_parameters_node_t *
5845pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing, uint8_t maximum) {
5846 return pm_numbered_parameters_node_new(
5847 parser->arena,
5848 ++parser->node_id,
5849 0,
5850 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
5851 maximum
5852 );
5853}
5854
5861static uint32_t
5862pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *token) {
5867#define NTH_REF_MAX ((uint64_t) (uint32_t) (INT_MAX >> 1))
5868
5869 const uint8_t *start = token->start + 1;
5870 ptrdiff_t diff = token->end - start;
5871 assert(diff > 0);
5872
5873 size_t length = (size_t) diff;
5874 uint64_t value = 0;
5875
5876 for (size_t idx = 0; idx < length; ++idx) {
5877 value = value * 10 + (uint64_t) (start[idx] - '0');
5878
5879 if (value > NTH_REF_MAX) {
5880 PM_PARSER_WARN_FORMAT(parser, U32(start - parser->start), U32(length), PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start);
5881 value = 0;
5882 break;
5883 }
5884 }
5885
5886 return (uint32_t) value;
5887
5888#undef NTH_REF_MAX
5889}
5890
5894static pm_numbered_reference_read_node_t *
5895pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
5896 assert(name->type == PM_TOKEN_NUMBERED_REFERENCE);
5897
5898 return pm_numbered_reference_read_node_new(
5899 parser->arena,
5900 ++parser->node_id,
5901 0,
5902 PM_LOCATION_INIT_TOKEN(parser, name),
5903 pm_numbered_reference_read_node_number(parser, name)
5904 );
5905}
5906
5910static pm_optional_parameter_node_t *
5911pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator, pm_node_t *value) {
5912 return pm_optional_parameter_node_new(
5913 parser->arena,
5914 ++parser->node_id,
5915 0,
5916 PM_LOCATION_INIT_TOKEN_NODE(parser, name, value),
5917 pm_parser_constant_id_token(parser, name),
5918 TOK2LOC(parser, name),
5919 TOK2LOC(parser, operator),
5920 value
5921 );
5922}
5923
5927static pm_or_node_t *
5928pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
5929 pm_assert_value_expression(parser, left);
5930
5931 return pm_or_node_new(
5932 parser->arena,
5933 ++parser->node_id,
5934 0,
5935 PM_LOCATION_INIT_NODES(left, right),
5936 left,
5937 right,
5938 TOK2LOC(parser, operator)
5939 );
5940}
5941
5945static pm_parameters_node_t *
5946pm_parameters_node_create(pm_parser_t *parser) {
5947 return pm_parameters_node_new(
5948 parser->arena,
5949 ++parser->node_id,
5950 0,
5951 PM_LOCATION_INIT_UNSET,
5952 ((pm_node_list_t) { 0 }),
5953 ((pm_node_list_t) { 0 }),
5954 NULL,
5955 ((pm_node_list_t) { 0 }),
5956 ((pm_node_list_t) { 0 }),
5957 NULL,
5958 NULL
5959 );
5960}
5961
5965static void
5966pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param) {
5967 if ((params->base.location.length == 0) || PM_NODE_START(params) > PM_NODE_START(param)) {
5968 PM_NODE_START_SET_NODE(params, param);
5969 }
5970
5971 if ((params->base.location.length == 0) || (PM_NODE_END(params) < PM_NODE_END(param))) {
5972 PM_NODE_LENGTH_SET_NODE(params, param);
5973 }
5974}
5975
5979static void
5980pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
5981 pm_parameters_node_location_set(params, param);
5982 pm_node_list_append_inline(arena, &params->requireds, param);
5983}
5984
5988static void
5989pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_optional_parameter_node_t *param) {
5990 pm_parameters_node_location_set(params, UP(param));
5991 pm_node_list_append_inline(arena, &params->optionals, UP(param));
5992}
5993
5997static void
5998pm_parameters_node_posts_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
5999 pm_parameters_node_location_set(params, param);
6000 pm_node_list_append_inline(arena, &params->posts, param);
6001}
6002
6006static void
6007pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
6008 pm_parameters_node_location_set(params, param);
6009 params->rest = param;
6010}
6011
6015static void
6016pm_parameters_node_keywords_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6017 pm_parameters_node_location_set(params, param);
6018 pm_node_list_append_inline(arena, &params->keywords, param);
6019}
6020
6024static void
6025pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
6026 assert(params->keyword_rest == NULL);
6027 pm_parameters_node_location_set(params, param);
6028 params->keyword_rest = param;
6029}
6030
6034static void
6035pm_parameters_node_block_set(pm_parameters_node_t *params, pm_node_t *param) {
6036 assert(params->block == NULL);
6037 pm_parameters_node_location_set(params, param);
6038 params->block = param;
6039}
6040
6044static pm_program_node_t *
6045pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) {
6046 return pm_program_node_new(
6047 parser->arena,
6048 ++parser->node_id,
6049 0,
6050 PM_LOCATION_INIT_NODE(statements),
6051 *locals,
6052 statements
6053 );
6054}
6055
6059static pm_parentheses_node_t *
6060pm_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) {
6061 return pm_parentheses_node_new(
6062 parser->arena,
6063 ++parser->node_id,
6064 flags,
6065 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6066 body,
6067 TOK2LOC(parser, opening),
6068 TOK2LOC(parser, closing)
6069 );
6070}
6071
6075static pm_pinned_expression_node_t *
6076pm_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) {
6077 return pm_pinned_expression_node_new(
6078 parser->arena,
6079 ++parser->node_id,
6080 0,
6081 PM_LOCATION_INIT_TOKENS(parser, operator, rparen),
6082 expression,
6083 TOK2LOC(parser, operator),
6084 TOK2LOC(parser, lparen),
6085 TOK2LOC(parser, rparen)
6086 );
6087}
6088
6092static pm_pinned_variable_node_t *
6093pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
6094 return pm_pinned_variable_node_new(
6095 parser->arena,
6096 ++parser->node_id,
6097 0,
6098 PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable),
6099 variable,
6100 TOK2LOC(parser, operator)
6101 );
6102}
6103
6107static pm_post_execution_node_t *
6108pm_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) {
6109 return pm_post_execution_node_new(
6110 parser->arena,
6111 ++parser->node_id,
6112 0,
6113 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
6114 statements,
6115 TOK2LOC(parser, keyword),
6116 TOK2LOC(parser, opening),
6117 TOK2LOC(parser, closing)
6118 );
6119}
6120
6124static pm_pre_execution_node_t *
6125pm_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) {
6126 return pm_pre_execution_node_new(
6127 parser->arena,
6128 ++parser->node_id,
6129 0,
6130 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
6131 statements,
6132 TOK2LOC(parser, keyword),
6133 TOK2LOC(parser, opening),
6134 TOK2LOC(parser, closing)
6135 );
6136}
6137
6141static pm_range_node_t *
6142pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
6143 pm_assert_value_expression(parser, left);
6144 pm_assert_value_expression(parser, right);
6145 pm_node_flags_t flags = 0;
6146
6147 // Indicate that this node is an exclusive range if the operator is `...`.
6148 if (operator->type == PM_TOKEN_DOT_DOT_DOT || operator->type == PM_TOKEN_UDOT_DOT_DOT) {
6149 flags |= PM_RANGE_FLAGS_EXCLUDE_END;
6150 }
6151
6152 // Indicate that this node is a static literal (i.e., can be compiled with
6153 // a putobject in CRuby) if the left and right are implicit nil, explicit
6154 // nil, or integers.
6155 if (
6156 (left == NULL || PM_NODE_TYPE_P(left, PM_NIL_NODE) || PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) &&
6157 (right == NULL || PM_NODE_TYPE_P(right, PM_NIL_NODE) || PM_NODE_TYPE_P(right, PM_INTEGER_NODE))
6158 ) {
6159 flags |= PM_NODE_FLAG_STATIC_LITERAL;
6160 }
6161
6162 uint32_t start = left == NULL ? PM_TOKEN_START(parser, operator) : PM_NODE_START(left);
6163 uint32_t end = right == NULL ? PM_TOKEN_END(parser, operator) : PM_NODE_END(right);
6164
6165 return pm_range_node_new(
6166 parser->arena,
6167 ++parser->node_id,
6168 flags,
6169 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6170 left,
6171 right,
6172 TOK2LOC(parser, operator)
6173 );
6174}
6175
6179static pm_redo_node_t *
6180pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) {
6181 assert(token->type == PM_TOKEN_KEYWORD_REDO);
6182
6183 return pm_redo_node_new(
6184 parser->arena,
6185 ++parser->node_id,
6186 0,
6187 PM_LOCATION_INIT_TOKEN(parser, token)
6188 );
6189}
6190
6195static pm_regular_expression_node_t *
6196pm_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) {
6197 return pm_regular_expression_node_new(
6198 parser->arena,
6199 ++parser->node_id,
6200 pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL,
6201 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6202 TOK2LOC(parser, opening),
6203 TOK2LOC(parser, content),
6204 TOK2LOC(parser, closing),
6205 *unescaped
6206 );
6207}
6208
6212static PRISM_INLINE pm_regular_expression_node_t *
6213pm_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6214 return pm_regular_expression_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6215}
6216
6220static pm_required_parameter_node_t *
6221pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
6222 return pm_required_parameter_node_new(
6223 parser->arena,
6224 ++parser->node_id,
6225 0,
6226 PM_LOCATION_INIT_TOKEN(parser, token),
6227 pm_parser_constant_id_token(parser, token)
6228 );
6229}
6230
6234static pm_rescue_modifier_node_t *
6235pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *keyword, pm_node_t *rescue_expression) {
6236 return pm_rescue_modifier_node_new(
6237 parser->arena,
6238 ++parser->node_id,
6239 0,
6240 PM_LOCATION_INIT_NODES(expression, rescue_expression),
6241 expression,
6242 TOK2LOC(parser, keyword),
6243 rescue_expression
6244 );
6245}
6246
6250static pm_rescue_node_t *
6251pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
6252 return pm_rescue_node_new(
6253 parser->arena,
6254 ++parser->node_id,
6255 0,
6256 PM_LOCATION_INIT_TOKEN(parser, keyword),
6257 TOK2LOC(parser, keyword),
6258 ((pm_node_list_t) { 0 }),
6259 ((pm_location_t) { 0 }),
6260 NULL,
6261 ((pm_location_t) { 0 }),
6262 NULL,
6263 NULL
6264 );
6265}
6266
6267static PRISM_INLINE void
6268pm_rescue_node_operator_set(const pm_parser_t *parser, pm_rescue_node_t *node, const pm_token_t *operator) {
6269 node->operator_loc = TOK2LOC(parser, operator);
6270}
6271
6275static void
6276pm_rescue_node_reference_set(pm_rescue_node_t *node, pm_node_t *reference) {
6277 node->reference = reference;
6278 PM_NODE_LENGTH_SET_NODE(node, reference);
6279}
6280
6284static void
6285pm_rescue_node_statements_set(pm_rescue_node_t *node, pm_statements_node_t *statements) {
6286 node->statements = statements;
6287 if (pm_statements_node_body_length(statements) > 0) {
6288 PM_NODE_LENGTH_SET_NODE(node, statements);
6289 }
6290}
6291
6295static void
6296pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subsequent) {
6297 node->subsequent = subsequent;
6298 PM_NODE_LENGTH_SET_NODE(node, subsequent);
6299}
6300
6304static void
6305pm_rescue_node_exceptions_append(pm_arena_t *arena, pm_rescue_node_t *node, pm_node_t *exception) {
6306 pm_node_list_append_inline(arena, &node->exceptions, exception);
6307 PM_NODE_LENGTH_SET_NODE(node, exception);
6308}
6309
6313static pm_rest_parameter_node_t *
6314pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
6315 return pm_rest_parameter_node_new(
6316 parser->arena,
6317 ++parser->node_id,
6318 0,
6319 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
6320 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
6321 NTOK2LOC(parser, name),
6322 TOK2LOC(parser, operator)
6323 );
6324}
6325
6329static pm_retry_node_t *
6330pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) {
6331 assert(token->type == PM_TOKEN_KEYWORD_RETRY);
6332
6333 return pm_retry_node_new(
6334 parser->arena,
6335 ++parser->node_id,
6336 0,
6337 PM_LOCATION_INIT_TOKEN(parser, token)
6338 );
6339}
6340
6344static pm_return_node_t *
6345pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
6346 return pm_return_node_new(
6347 parser->arena,
6348 ++parser->node_id,
6349 0,
6350 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
6351 TOK2LOC(parser, keyword),
6352 arguments
6353 );
6354}
6355
6359static pm_self_node_t *
6360pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) {
6361 assert(token->type == PM_TOKEN_KEYWORD_SELF);
6362
6363 return pm_self_node_new(
6364 parser->arena,
6365 ++parser->node_id,
6366 0,
6367 PM_LOCATION_INIT_TOKEN(parser, token)
6368 );
6369}
6370
6374static pm_shareable_constant_node_t *
6375pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shareable_constant_value_t value) {
6376 return pm_shareable_constant_node_new(
6377 parser->arena,
6378 ++parser->node_id,
6379 (pm_node_flags_t) value,
6380 PM_LOCATION_INIT_NODE(write),
6381 write
6382 );
6383}
6384
6388static pm_singleton_class_node_t *
6389pm_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) {
6390 return pm_singleton_class_node_new(
6391 parser->arena,
6392 ++parser->node_id,
6393 0,
6394 PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword),
6395 *locals,
6396 TOK2LOC(parser, class_keyword),
6397 TOK2LOC(parser, operator),
6398 expression,
6399 body,
6400 TOK2LOC(parser, end_keyword)
6401 );
6402}
6403
6407static pm_source_encoding_node_t *
6408pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) {
6409 assert(token->type == PM_TOKEN_KEYWORD___ENCODING__);
6410
6411 return pm_source_encoding_node_new(
6412 parser->arena,
6413 ++parser->node_id,
6414 PM_NODE_FLAG_STATIC_LITERAL,
6415 PM_LOCATION_INIT_TOKEN(parser, token)
6416 );
6417}
6418
6422static pm_source_file_node_t*
6423pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) {
6424 assert(file_keyword->type == PM_TOKEN_KEYWORD___FILE__);
6425
6426 pm_node_flags_t flags = 0;
6427
6428 switch (parser->frozen_string_literal) {
6429 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6430 flags |= PM_STRING_FLAGS_MUTABLE;
6431 break;
6432 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6433 flags |= PM_STRING_FLAGS_FROZEN;
6434 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
6435 flags |= PM_NODE_FLAG_STATIC_LITERAL;
6436 }
6437 break;
6438 }
6439
6440 return pm_source_file_node_new(
6441 parser->arena,
6442 ++parser->node_id,
6443 flags,
6444 PM_LOCATION_INIT_TOKEN(parser, file_keyword),
6445 parser->filepath
6446 );
6447}
6448
6452static pm_source_line_node_t *
6453pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) {
6454 assert(token->type == PM_TOKEN_KEYWORD___LINE__);
6455
6456 return pm_source_line_node_new(
6457 parser->arena,
6458 ++parser->node_id,
6459 PM_NODE_FLAG_STATIC_LITERAL,
6460 PM_LOCATION_INIT_TOKEN(parser, token)
6461 );
6462}
6463
6467static pm_splat_node_t *
6468pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
6469 return pm_splat_node_new(
6470 parser->arena,
6471 ++parser->node_id,
6472 0,
6473 (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression),
6474 TOK2LOC(parser, operator),
6475 expression
6476 );
6477}
6478
6482static pm_statements_node_t *
6483pm_statements_node_create(pm_parser_t *parser) {
6484 return pm_statements_node_new(
6485 parser->arena,
6486 ++parser->node_id,
6487 0,
6488 PM_LOCATION_INIT_UNSET,
6489 ((pm_node_list_t) { 0 })
6490 );
6491}
6492
6496static size_t
6497pm_statements_node_body_length(pm_statements_node_t *node) {
6498 return node && node->body.size;
6499}
6500
6505static PRISM_INLINE void
6506pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement) {
6507 if (pm_statements_node_body_length(node) == 0 || PM_NODE_START(statement) < PM_NODE_START(node)) {
6508 PM_NODE_START_SET_NODE(node, statement);
6509 }
6510
6511 if (PM_NODE_END(statement) > PM_NODE_END(node)) {
6512 PM_NODE_LENGTH_SET_NODE(node, statement);
6513 }
6514}
6515
6519static void
6520pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline) {
6521 pm_statements_node_body_update(node, statement);
6522
6523 if (node->body.size > 0) {
6524 const pm_node_t *previous = node->body.nodes[node->body.size - 1];
6525
6526 switch (PM_NODE_TYPE(previous)) {
6527 case PM_BREAK_NODE:
6528 case PM_NEXT_NODE:
6529 case PM_REDO_NODE:
6530 case PM_RETRY_NODE:
6531 case PM_RETURN_NODE:
6532 pm_parser_warn_node(parser, statement, PM_WARN_UNREACHABLE_STATEMENT);
6533 break;
6534 default:
6535 break;
6536 }
6537 }
6538
6539 pm_node_list_append_inline(parser->arena, &node->body, statement);
6540 if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6541}
6542
6546static void
6547pm_statements_node_body_prepend(pm_arena_t *arena, pm_statements_node_t *node, pm_node_t *statement) {
6548 pm_statements_node_body_update(node, statement);
6549 pm_node_list_prepend(arena, &node->body, statement);
6550 pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6551}
6552
6556static PRISM_INLINE pm_string_node_t *
6557pm_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) {
6558 pm_node_flags_t flags = 0;
6559
6560 switch (parser->frozen_string_literal) {
6561 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6562 flags = PM_STRING_FLAGS_MUTABLE;
6563 break;
6564 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6565 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6566 break;
6567 }
6568
6569 uint32_t start = PM_TOKEN_START(parser, opening == NULL ? content : opening);
6570 uint32_t end = PM_TOKEN_END(parser, closing == NULL ? content : closing);
6571
6572 return pm_string_node_new(
6573 parser->arena,
6574 ++parser->node_id,
6575 flags,
6576 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6577 NTOK2LOC(parser, opening),
6578 TOK2LOC(parser, content),
6579 NTOK2LOC(parser, closing),
6580 *string
6581 );
6582}
6583
6587static pm_string_node_t *
6588pm_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6589 return pm_string_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6590}
6591
6596static pm_string_node_t *
6597pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6598 pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, &parser->current_string);
6599 parser->current_string = PM_STRING_EMPTY;
6600 return node;
6601}
6602
6606static pm_super_node_t *
6607pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_t *arguments) {
6608 assert(keyword->type == PM_TOKEN_KEYWORD_SUPER);
6609
6610 const pm_location_t *end = pm_arguments_end(arguments);
6611 assert(end != NULL && "unreachable");
6612
6613 return pm_super_node_new(
6614 parser->arena,
6615 ++parser->node_id,
6616 0,
6617 ((pm_location_t) { .start = PM_TOKEN_START(parser, keyword), .length = PM_LOCATION_END(end) - PM_TOKEN_START(parser, keyword) }),
6618 TOK2LOC(parser, keyword),
6619 arguments->opening_loc,
6620 arguments->arguments,
6621 arguments->closing_loc,
6622 arguments->block
6623 );
6624}
6625
6630static bool
6631pm_ascii_only_p(const pm_string_t *contents) {
6632 const size_t length = pm_string_length(contents);
6633 const uint8_t *source = pm_string_source(contents);
6634
6635 for (size_t index = 0; index < length; index++) {
6636 if (source[index] & 0x80) return false;
6637 }
6638
6639 return true;
6640}
6641
6645static void
6646parse_symbol_encoding_validate_utf8(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6647 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6648 size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor);
6649
6650 if (width == 0) {
6651 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6652 break;
6653 }
6654
6655 cursor += width;
6656 }
6657}
6658
6663static void
6664parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6665 const pm_encoding_t *encoding = parser->encoding;
6666
6667 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6668 size_t width = encoding->char_width(cursor, end - cursor);
6669
6670 if (width == 0) {
6671 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6672 break;
6673 }
6674
6675 cursor += width;
6676 }
6677}
6678
6688static PRISM_INLINE pm_node_flags_t
6689parse_symbol_encoding(pm_parser_t *parser, const pm_encoding_t *explicit_encoding, const pm_token_t *location, const pm_string_t *contents, bool validate) {
6690 if (explicit_encoding != NULL) {
6691 // A Symbol may optionally have its encoding explicitly set. This will
6692 // happen if an escape sequence results in a non-ASCII code point.
6693 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
6694 if (validate) parse_symbol_encoding_validate_utf8(parser, location, contents);
6695 return PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
6696 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
6697 return PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING;
6698 } else if (validate) {
6699 parse_symbol_encoding_validate_other(parser, location, contents);
6700 }
6701 } else if (pm_ascii_only_p(contents)) {
6702 // Ruby stipulates that all source files must use an ASCII-compatible
6703 // encoding. Thus, all symbols appearing in source are eligible for
6704 // "downgrading" to US-ASCII.
6705 return PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
6706 } else if (validate) {
6707 parse_symbol_encoding_validate_other(parser, location, contents);
6708 }
6709
6710 return 0;
6711}
6712
6717static pm_symbol_node_t *
6718pm_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) {
6719 uint32_t start = opening == NULL ? PM_TOKEN_START(parser, value) : PM_TOKEN_START(parser, opening);
6720 uint32_t end = closing == NULL ? PM_TOKEN_END(parser, value) : PM_TOKEN_END(parser, closing);
6721
6722 return pm_symbol_node_new(
6723 parser->arena,
6724 ++parser->node_id,
6725 PM_NODE_FLAG_STATIC_LITERAL | flags,
6726 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6727 NTOK2LOC(parser, opening),
6728 NTOK2LOC(parser, value),
6729 NTOK2LOC(parser, closing),
6730 *unescaped
6731 );
6732}
6733
6737static PRISM_INLINE pm_symbol_node_t *
6738pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6739 return pm_symbol_node_create_unescaped(parser, opening, value, closing, &PM_STRING_EMPTY, 0);
6740}
6741
6745static pm_symbol_node_t *
6746pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6747 pm_symbol_node_t *node = pm_symbol_node_create_unescaped(parser, opening, value, closing, &parser->current_string, parse_symbol_encoding(parser, parser->explicit_encoding, value, &parser->current_string, false));
6748 parser->current_string = PM_STRING_EMPTY;
6749 return node;
6750}
6751
6755static pm_symbol_node_t *
6756pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) {
6757 assert(token->type == PM_TOKEN_LABEL);
6758
6759 pm_token_t closing = { .type = PM_TOKEN_LABEL_END, .start = token->end - 1, .end = token->end };
6760 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end - 1 };
6761 pm_symbol_node_t *node = pm_symbol_node_create(parser, NULL, &label, &closing);
6762
6763 assert((label.end - label.start) >= 0);
6764 pm_string_shared_init(&node->unescaped, label.start, label.end);
6765 pm_node_flag_set(UP(node), parse_symbol_encoding(parser, parser->explicit_encoding, &label, &node->unescaped, false));
6766
6767 return node;
6768}
6769
6773static pm_symbol_node_t *
6774pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
6775 pm_symbol_node_t *node = pm_symbol_node_new(
6776 parser->arena,
6777 ++parser->node_id,
6778 PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING,
6779 PM_LOCATION_INIT_UNSET,
6780 ((pm_location_t) { 0 }),
6781 ((pm_location_t) { 0 }),
6782 ((pm_location_t) { 0 }),
6783 ((pm_string_t) { 0 })
6784 );
6785
6786 pm_string_init(&node->unescaped, (const uint8_t *) content, strlen(content));
6787 return node;
6788}
6789
6793static bool
6794pm_symbol_node_label_p(const pm_parser_t *parser, const pm_node_t *node) {
6795 const pm_location_t *location = NULL;
6796
6797 switch (PM_NODE_TYPE(node)) {
6798 case PM_SYMBOL_NODE: {
6799 const pm_symbol_node_t *cast = (pm_symbol_node_t *) node;
6800 if (cast->closing_loc.length > 0) {
6801 location = &cast->closing_loc;
6802 }
6803 break;
6804 }
6805 case PM_INTERPOLATED_SYMBOL_NODE: {
6806 const pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node;
6807 if (cast->closing_loc.length > 0) {
6808 location = &cast->closing_loc;
6809 }
6810 break;
6811 }
6812 default:
6813 return false;
6814 }
6815
6816 return (location != NULL) && (parser->start[PM_LOCATION_END(location) - 1] == ':');
6817}
6818
6822static pm_symbol_node_t *
6823pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t *opening, const pm_token_t *closing) {
6824 pm_symbol_node_t *new_node = pm_symbol_node_new(
6825 parser->arena,
6826 ++parser->node_id,
6827 PM_NODE_FLAG_STATIC_LITERAL,
6828 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6829 TOK2LOC(parser, opening),
6830 node->content_loc,
6831 TOK2LOC(parser, closing),
6832 node->unescaped
6833 );
6834
6835 pm_token_t content = {
6836 .type = PM_TOKEN_IDENTIFIER,
6837 .start = parser->start + node->content_loc.start,
6838 .end = parser->start + node->content_loc.start + node->content_loc.length
6839 };
6840
6841 pm_node_flag_set(UP(new_node), parse_symbol_encoding(parser, parser->explicit_encoding, &content, &node->unescaped, true));
6842
6843 /* The old node is arena-allocated so no explicit free is needed. */
6844 return new_node;
6845}
6846
6850static pm_string_node_t *
6851pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) {
6852 pm_node_flags_t flags = 0;
6853
6854 switch (parser->frozen_string_literal) {
6855 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6856 flags = PM_STRING_FLAGS_MUTABLE;
6857 break;
6858 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6859 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6860 break;
6861 }
6862
6863 pm_string_node_t *new_node = pm_string_node_new(
6864 parser->arena,
6865 ++parser->node_id,
6866 flags,
6867 PM_LOCATION_INIT_NODE(node),
6868 node->opening_loc,
6869 node->content_loc,
6870 node->closing_loc,
6871 node->unescaped
6872 );
6873
6874 /* The old node is arena-allocated so no explicit free is needed. */
6875 return new_node;
6876}
6877
6881static pm_true_node_t *
6882pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) {
6883 assert(token->type == PM_TOKEN_KEYWORD_TRUE);
6884
6885 return pm_true_node_new(
6886 parser->arena,
6887 ++parser->node_id,
6888 PM_NODE_FLAG_STATIC_LITERAL,
6889 PM_LOCATION_INIT_TOKEN(parser, token)
6890 );
6891}
6892
6896static pm_true_node_t *
6897pm_true_node_synthesized_create(pm_parser_t *parser) {
6898 return pm_true_node_new(
6899 parser->arena,
6900 ++parser->node_id,
6901 PM_NODE_FLAG_STATIC_LITERAL,
6902 PM_LOCATION_INIT_UNSET
6903 );
6904}
6905
6909static pm_undef_node_t *
6910pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
6911 assert(token->type == PM_TOKEN_KEYWORD_UNDEF);
6912
6913 return pm_undef_node_new(
6914 parser->arena,
6915 ++parser->node_id,
6916 0,
6917 PM_LOCATION_INIT_TOKEN(parser, token),
6918 ((pm_node_list_t) { 0 }),
6919 TOK2LOC(parser, token)
6920 );
6921}
6922
6926static void
6927pm_undef_node_append(pm_arena_t *arena, pm_undef_node_t *node, pm_node_t *name) {
6928 PM_NODE_LENGTH_SET_NODE(node, name);
6929 pm_node_list_append_inline(arena, &node->names, name);
6930}
6931
6935static pm_unless_node_t *
6936pm_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) {
6937 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6938 pm_node_t *end = statements == NULL ? predicate : UP(statements);
6939
6940 return pm_unless_node_new(
6941 parser->arena,
6942 ++parser->node_id,
6943 PM_NODE_FLAG_NEWLINE,
6944 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, end),
6945 TOK2LOC(parser, keyword),
6946 predicate,
6947 NTOK2LOC(parser, then_keyword),
6948 statements,
6949 NULL,
6950 ((pm_location_t) { 0 })
6951 );
6952}
6953
6957static pm_unless_node_t *
6958pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) {
6959 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6960
6961 pm_statements_node_t *statements = pm_statements_node_create(parser);
6962 pm_statements_node_body_append(parser, statements, statement, true);
6963
6964 return pm_unless_node_new(
6965 parser->arena,
6966 ++parser->node_id,
6967 PM_NODE_FLAG_NEWLINE,
6968 PM_LOCATION_INIT_NODES(statement, predicate),
6969 TOK2LOC(parser, unless_keyword),
6970 predicate,
6971 ((pm_location_t) { 0 }),
6972 statements,
6973 NULL,
6974 ((pm_location_t) { 0 })
6975 );
6976}
6977
6978static PRISM_INLINE void
6979pm_unless_node_end_keyword_loc_set(const pm_parser_t *parser, pm_unless_node_t *node, const pm_token_t *end_keyword) {
6980 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
6981 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
6982}
6983
6989static void
6990pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statements) {
6991 assert(parser->current_block_exits != NULL);
6992
6993 // All of the block exits that we want to remove should be within the
6994 // statements, and since we are modifying the statements, we shouldn't have
6995 // to check the end location.
6996 uint32_t start = statements->base.location.start;
6997
6998 for (size_t index = parser->current_block_exits->size; index > 0; index--) {
6999 pm_node_t *block_exit = parser->current_block_exits->nodes[index - 1];
7000 if (block_exit->location.start < start) break;
7001
7002 // Implicitly remove from the list by lowering the size.
7003 parser->current_block_exits->size--;
7004 }
7005}
7006
7010static pm_until_node_t *
7011pm_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) {
7012 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7013
7014 return pm_until_node_new(
7015 parser->arena,
7016 ++parser->node_id,
7017 flags,
7018 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7019 TOK2LOC(parser, keyword),
7020 NTOK2LOC(parser, do_keyword),
7021 TOK2LOC(parser, closing),
7022 predicate,
7023 statements
7024 );
7025}
7026
7030static pm_until_node_t *
7031pm_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) {
7032 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7033 pm_loop_modifier_block_exits(parser, statements);
7034
7035 return pm_until_node_new(
7036 parser->arena,
7037 ++parser->node_id,
7038 flags,
7039 PM_LOCATION_INIT_NODES(statements, predicate),
7040 TOK2LOC(parser, keyword),
7041 ((pm_location_t) { 0 }),
7042 ((pm_location_t) { 0 }),
7043 predicate,
7044 statements
7045 );
7046}
7047
7051static pm_when_node_t *
7052pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
7053 return pm_when_node_new(
7054 parser->arena,
7055 ++parser->node_id,
7056 0,
7057 PM_LOCATION_INIT_TOKEN(parser, keyword),
7058 TOK2LOC(parser, keyword),
7059 ((pm_node_list_t) { 0 }),
7060 ((pm_location_t) { 0 }),
7061 NULL
7062 );
7063}
7064
7068static void
7069pm_when_node_conditions_append(pm_arena_t *arena, pm_when_node_t *node, pm_node_t *condition) {
7070 PM_NODE_LENGTH_SET_NODE(node, condition);
7071 pm_node_list_append_inline(arena, &node->conditions, condition);
7072}
7073
7077static PRISM_INLINE void
7078pm_when_node_then_keyword_loc_set(const pm_parser_t *parser, pm_when_node_t *node, const pm_token_t *then_keyword) {
7079 PM_NODE_LENGTH_SET_TOKEN(parser, node, then_keyword);
7080 node->then_keyword_loc = TOK2LOC(parser, then_keyword);
7081}
7082
7086static void
7087pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statements) {
7088 if (PM_NODE_END(statements) > PM_NODE_END(node)) {
7089 PM_NODE_LENGTH_SET_NODE(node, statements);
7090 }
7091
7092 node->statements = statements;
7093}
7094
7098static pm_while_node_t *
7099pm_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) {
7100 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7101
7102 return pm_while_node_new(
7103 parser->arena,
7104 ++parser->node_id,
7105 flags,
7106 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7107 TOK2LOC(parser, keyword),
7108 NTOK2LOC(parser, do_keyword),
7109 TOK2LOC(parser, closing),
7110 predicate,
7111 statements
7112 );
7113}
7114
7118static pm_while_node_t *
7119pm_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) {
7120 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7121 pm_loop_modifier_block_exits(parser, statements);
7122
7123 return pm_while_node_new(
7124 parser->arena,
7125 ++parser->node_id,
7126 flags,
7127 PM_LOCATION_INIT_NODES(statements, predicate),
7128 TOK2LOC(parser, keyword),
7129 ((pm_location_t) { 0 }),
7130 ((pm_location_t) { 0 }),
7131 predicate,
7132 statements
7133 );
7134}
7135
7139static pm_while_node_t *
7140pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_statements_node_t *statements) {
7141 return pm_while_node_new(
7142 parser->arena,
7143 ++parser->node_id,
7144 0,
7145 PM_LOCATION_INIT_UNSET,
7146 ((pm_location_t) { 0 }),
7147 ((pm_location_t) { 0 }),
7148 ((pm_location_t) { 0 }),
7149 predicate,
7150 statements
7151 );
7152}
7153
7158static pm_x_string_node_t *
7159pm_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) {
7160 return pm_x_string_node_new(
7161 parser->arena,
7162 ++parser->node_id,
7163 PM_STRING_FLAGS_FROZEN,
7164 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
7165 TOK2LOC(parser, opening),
7166 TOK2LOC(parser, content),
7167 TOK2LOC(parser, closing),
7168 *unescaped
7169 );
7170}
7171
7175static PRISM_INLINE pm_x_string_node_t *
7176pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
7177 return pm_xstring_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
7178}
7179
7183static pm_yield_node_t *
7184pm_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) {
7185 uint32_t start = PM_TOKEN_START(parser, keyword);
7186 uint32_t end;
7187
7188 if (rparen_loc->length > 0) {
7189 end = PM_LOCATION_END(rparen_loc);
7190 } else if (arguments != NULL) {
7191 end = PM_NODE_END(arguments);
7192 } else if (lparen_loc->length > 0) {
7193 end = PM_LOCATION_END(lparen_loc);
7194 } else {
7195 end = PM_TOKEN_END(parser, keyword);
7196 }
7197
7198 return pm_yield_node_new(
7199 parser->arena,
7200 ++parser->node_id,
7201 0,
7202 ((pm_location_t) { .start = start, .length = U32(end - start) }),
7203 TOK2LOC(parser, keyword),
7204 *lparen_loc,
7205 arguments,
7206 *rparen_loc
7207 );
7208}
7209
7214static int
7215pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant_id) {
7216 pm_scope_t *scope = parser->current_scope;
7217 int depth = 0;
7218
7219 while (scope != NULL) {
7220 if (pm_locals_find(&scope->locals, constant_id) != UINT32_MAX) return depth;
7221 if (scope->closed) break;
7222
7223 scope = scope->previous;
7224 depth++;
7225 }
7226
7227 return -1;
7228}
7229
7235static PRISM_INLINE int
7236pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
7237 return pm_parser_local_depth_constant_id(parser, pm_parser_constant_id_token(parser, token));
7238}
7239
7243static PRISM_INLINE void
7244pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7245 pm_locals_write(&parser->scratch_arena, &parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
7246}
7247
7251static pm_constant_id_t
7252pm_parser_local_add_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7253 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
7254 if (constant_id != 0) pm_parser_local_add(parser, constant_id, start, end, reads);
7255 return constant_id;
7256}
7257
7261static PRISM_INLINE pm_constant_id_t
7262pm_parser_local_add_location(pm_parser_t *parser, pm_location_t *location, uint32_t reads) {
7263 return pm_parser_local_add_raw(parser, parser->start + location->start, parser->start + location->start + location->length, reads);
7264}
7265
7269static PRISM_INLINE pm_constant_id_t
7270pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token, uint32_t reads) {
7271 return pm_parser_local_add_raw(parser, token->start, token->end, reads);
7272}
7273
7277static pm_constant_id_t
7278pm_parser_local_add_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
7279 pm_constant_id_t constant_id = pm_parser_constant_id_owned(parser, start, length);
7280 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7281 return constant_id;
7282}
7283
7287static pm_constant_id_t
7288pm_parser_local_add_constant(pm_parser_t *parser, const char *start, size_t length) {
7289 pm_constant_id_t constant_id = pm_parser_constant_id_constant(parser, start, length);
7290 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7291 return constant_id;
7292}
7293
7301static bool
7302pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) {
7303 // We want to check whether the parameter name is a numbered parameter or
7304 // not.
7305 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, name), PM_TOKEN_LENGTH(name));
7306
7307 // Otherwise we'll fetch the constant id for the parameter name and check
7308 // whether it's already in the current scope.
7309 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name);
7310
7311 if (pm_locals_find(&parser->current_scope->locals, constant_id) != UINT32_MAX) {
7312 // Add an error if the parameter doesn't start with _ and has been seen before
7313 if ((name->start < name->end) && (*name->start != '_')) {
7314 pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_DUPLICATED);
7315 }
7316 return true;
7317 }
7318 return false;
7319}
7320
7324static void
7325pm_parser_scope_pop(pm_parser_t *parser) {
7326 pm_scope_t *scope = parser->current_scope;
7327 parser->current_scope = scope->previous;
7328 scope->previous = parser->free_scopes;
7329 parser->free_scopes = scope;
7330}
7331
7332/******************************************************************************/
7333/* Stack helpers */
7334/******************************************************************************/
7335
7339static PRISM_INLINE void
7340pm_state_stack_push(pm_state_stack_t *stack, bool value) {
7341 *stack = (*stack << 1) | (value & 1);
7342}
7343
7347static PRISM_INLINE void
7348pm_state_stack_pop(pm_state_stack_t *stack) {
7349 *stack >>= 1;
7350}
7351
7355static PRISM_INLINE bool
7356pm_state_stack_p(const pm_state_stack_t *stack) {
7357 return *stack & 1;
7358}
7359
7360static PRISM_INLINE void
7361pm_accepts_block_stack_push(pm_parser_t *parser, bool value) {
7362 // Use the negation of the value to prevent stack overflow.
7363 pm_state_stack_push(&parser->accepts_block_stack, !value);
7364}
7365
7366static PRISM_INLINE void
7367pm_accepts_block_stack_pop(pm_parser_t *parser) {
7368 pm_state_stack_pop(&parser->accepts_block_stack);
7369}
7370
7371static PRISM_INLINE bool
7372pm_accepts_block_stack_p(pm_parser_t *parser) {
7373 return !pm_state_stack_p(&parser->accepts_block_stack);
7374}
7375
7376static PRISM_INLINE void
7377pm_do_loop_stack_push(pm_parser_t *parser, bool value) {
7378 pm_state_stack_push(&parser->do_loop_stack, value);
7379}
7380
7381static PRISM_INLINE void
7382pm_do_loop_stack_pop(pm_parser_t *parser) {
7383 pm_state_stack_pop(&parser->do_loop_stack);
7384}
7385
7386static PRISM_INLINE bool
7387pm_do_loop_stack_p(pm_parser_t *parser) {
7388 return pm_state_stack_p(&parser->do_loop_stack);
7389}
7390
7420static PRISM_INLINE void
7421pm_enclosure_frame_push(pm_parser_t *parser) {
7422 pm_do_loop_stack_push(parser, false);
7423 pm_accepts_block_stack_push(parser, true);
7424}
7425
7426static PRISM_INLINE void
7427pm_enclosure_frame_pop(pm_parser_t *parser) {
7428 pm_do_loop_stack_pop(parser);
7429 pm_accepts_block_stack_pop(parser);
7430}
7431
7432/******************************************************************************/
7433/* Lexer check helpers */
7434/******************************************************************************/
7435
7440static PRISM_INLINE uint8_t
7441peek_at(const pm_parser_t *parser, const uint8_t *cursor) {
7442 if (cursor < parser->end) {
7443 return *cursor;
7444 } else {
7445 return '\0';
7446 }
7447}
7448
7454static PRISM_INLINE uint8_t
7455peek_offset(pm_parser_t *parser, ptrdiff_t offset) {
7456 return peek_at(parser, parser->current.end + offset);
7457}
7458
7463static PRISM_INLINE uint8_t
7464peek(const pm_parser_t *parser) {
7465 return peek_at(parser, parser->current.end);
7466}
7467
7472static PRISM_INLINE bool
7473match(pm_parser_t *parser, uint8_t value) {
7474 if (peek(parser) == value) {
7475 parser->current.end++;
7476 return true;
7477 }
7478 return false;
7479}
7480
7485static PRISM_INLINE size_t
7486match_eol_at(pm_parser_t *parser, const uint8_t *cursor) {
7487 if (peek_at(parser, cursor) == '\n') {
7488 return 1;
7489 }
7490 if (peek_at(parser, cursor) == '\r' && peek_at(parser, cursor + 1) == '\n') {
7491 return 2;
7492 }
7493 return 0;
7494}
7495
7501static PRISM_INLINE size_t
7502match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) {
7503 return match_eol_at(parser, parser->current.end + offset);
7504}
7505
7511static PRISM_INLINE size_t
7512match_eol(pm_parser_t *parser) {
7513 return match_eol_at(parser, parser->current.end);
7514}
7515
7519static PRISM_INLINE const uint8_t *
7520next_newline(const uint8_t *cursor, ptrdiff_t length) {
7521 assert(length >= 0);
7522
7523 // Note that it's okay for us to use memchr here to look for \n because none
7524 // of the encodings that we support have \n as a component of a multi-byte
7525 // character.
7526 return memchr(cursor, '\n', (size_t) length);
7527}
7528
7532static PRISM_INLINE bool
7533ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) {
7534 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));
7535}
7536
7541static bool
7542parser_lex_magic_comment_encoding_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
7543 const pm_encoding_t *encoding = pm_encoding_find(start, end);
7544
7545 if (encoding != NULL) {
7546 if (parser->encoding != encoding) {
7547 parser->encoding = encoding;
7548 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
7549 }
7550
7551 parser->encoding_changed = (encoding != PM_ENCODING_UTF_8_ENTRY);
7552 return true;
7553 }
7554
7555 return false;
7556}
7557
7562static void
7563parser_lex_magic_comment_encoding(pm_parser_t *parser) {
7564 const uint8_t *cursor = parser->current.start + 1;
7565 const uint8_t *end = parser->current.end;
7566
7567 bool separator = false;
7568 while (true) {
7569 if (end - cursor <= 6) return;
7570 switch (cursor[6]) {
7571 case 'C': case 'c': cursor += 6; continue;
7572 case 'O': case 'o': cursor += 5; continue;
7573 case 'D': case 'd': cursor += 4; continue;
7574 case 'I': case 'i': cursor += 3; continue;
7575 case 'N': case 'n': cursor += 2; continue;
7576 case 'G': case 'g': cursor += 1; continue;
7577 case '=': case ':':
7578 separator = true;
7579 cursor += 6;
7580 break;
7581 default:
7582 cursor += 6;
7583 if (pm_char_is_whitespace(*cursor)) break;
7584 continue;
7585 }
7586 if (pm_strncasecmp(cursor - 6, (const uint8_t *) "coding", 6) == 0) break;
7587 separator = false;
7588 }
7589
7590 while (true) {
7591 do {
7592 if (++cursor >= end) return;
7593 } while (pm_char_is_whitespace(*cursor));
7594
7595 if (separator) break;
7596 if (*cursor != '=' && *cursor != ':') return;
7597
7598 separator = true;
7599 cursor++;
7600 }
7601
7602 const uint8_t *value_start = cursor;
7603 while ((*cursor == '-' || *cursor == '_' || parser->encoding->alnum_char(cursor, 1)) && ++cursor < end);
7604
7605 if (!parser_lex_magic_comment_encoding_value(parser, value_start, cursor)) {
7606 // If we were unable to parse the encoding value, then we've got an
7607 // issue because we didn't understand the encoding that the user was
7608 // trying to use. In this case we'll keep using the default encoding but
7609 // add an error to the parser to indicate an unsuccessful parse.
7610 pm_parser_err(parser, U32(value_start - parser->start), U32(cursor - value_start), PM_ERR_INVALID_ENCODING_MAGIC_COMMENT);
7611 }
7612}
7613
7614typedef enum {
7615 PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE,
7616 PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE,
7617 PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID
7618} pm_magic_comment_boolean_value_t;
7619
7624static pm_magic_comment_boolean_value_t
7625parser_lex_magic_comment_boolean_value(const uint8_t *value_start, uint32_t value_length) {
7626 if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "true", 4) == 0) {
7627 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE;
7628 } else if (value_length == 5 && pm_strncasecmp(value_start, (const uint8_t *) "false", 5) == 0) {
7629 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE;
7630 } else {
7631 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID;
7632 }
7633}
7634
7635static PRISM_INLINE bool
7636pm_char_is_magic_comment_key_delimiter(const uint8_t b) {
7637 return b == '\'' || b == '"' || b == ':' || b == ';';
7638}
7639
7645static PRISM_INLINE const uint8_t *
7646parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor, const uint8_t *end) {
7647 // Scan for '*' as the middle character, since it is rarer than '-' in
7648 // typical comments and avoids repeated memchr calls for '-' that hit
7649 // dashes in words like "foo-bar".
7650 while ((cursor + 3 <= end) && (cursor = pm_memchr(cursor + 1, '*', (size_t) (end - cursor - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7651 if (cursor[-1] == '-' && cursor + 1 < end && cursor[1] == '-') {
7652 return cursor - 1;
7653 }
7654 }
7655 return NULL;
7656}
7657
7665static bool
7666parser_magic_comment_key_p(const uint8_t *key, size_t key_length, const char *name) {
7667 assert(name[key_length] == '\0');
7668
7669 for (size_t index = 0; index < key_length; index++) {
7670 uint8_t byte = key[index];
7671 if (byte == '-') byte = '_';
7672 if ('A' <= byte && byte <= 'Z') byte |= 0x20;
7673 if (byte != (uint8_t) name[index]) return false;
7674 }
7675
7676 return true;
7677}
7678
7689static PRISM_INLINE bool
7690parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
7691 bool result = true;
7692
7693 const uint8_t *start = parser->current.start + 1;
7694 const uint8_t *end = parser->current.end;
7695 if (end - start <= 7) return false;
7696
7697 const uint8_t *cursor;
7698 bool indicator = false;
7699
7700 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7701 start = cursor + 3;
7702
7703 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7704 end = cursor;
7705 indicator = true;
7706 } else {
7707 // If we have a start marker but not an end marker, then we cannot
7708 // have a magic comment.
7709 return false;
7710 }
7711 } else {
7712 // Non-emacs magic comments must contain a colon for `key: value`.
7713 // Reject early if there is no colon to avoid scanning the entire
7714 // comment character-by-character.
7715 if (pm_memchr(start, ':', (size_t) (end - start), parser->encoding_changed, parser->encoding) == NULL) {
7716 return false;
7717 }
7718
7719 // Advance start past leading whitespace so the main loop begins
7720 // directly at the key, avoiding a redundant whitespace scan.
7721 start += pm_strspn_whitespace(start, end - start);
7722 }
7723
7724 cursor = start;
7725 while (cursor < end) {
7726 if (indicator) {
7727 while (cursor < end && (pm_char_is_magic_comment_key_delimiter(*cursor) || pm_char_is_whitespace(*cursor))) cursor++;
7728 }
7729
7730 const uint8_t *key_start = cursor;
7731 while (cursor < end && (!pm_char_is_magic_comment_key_delimiter(*cursor) && !pm_char_is_whitespace(*cursor))) cursor++;
7732
7733 const uint8_t *key_end = cursor;
7734 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7735 if (cursor == end) break;
7736
7737 if (*cursor == ':') {
7738 cursor++;
7739 } else {
7740 if (!indicator) return false;
7741 continue;
7742 }
7743
7744 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7745 if (cursor == end) break;
7746
7747 const uint8_t *value_start;
7748 const uint8_t *value_end;
7749
7750 if (*cursor == '"') {
7751 value_start = ++cursor;
7752 for (; cursor < end && *cursor != '"'; cursor++) {
7753 if (*cursor == '\\' && (cursor + 1 < end)) cursor++;
7754 }
7755 value_end = cursor;
7756 if (cursor < end && *cursor == '"') cursor++;
7757 } else {
7758 value_start = cursor;
7759 while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++;
7760 value_end = cursor;
7761 }
7762
7763 if (indicator) {
7764 while (cursor < end && (*cursor == ';' || pm_char_is_whitespace(*cursor))) cursor++;
7765 } else {
7766 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7767 if (cursor != end) return false;
7768 }
7769
7770 /* Finally, we can start checking the key against the list of known
7771 * magic comment keys, and potentially change state based on that. */
7772 const size_t key_length = (size_t) (key_end - key_start);
7773 uint32_t value_length = (uint32_t) (value_end - value_start);
7774
7775 switch (key_length) {
7776 case 6:
7777 /* We only want to attempt to compare against encoding comments
7778 * if it is the first line in the file (or the second in the
7779 * case of a shebang). */
7780 if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7781 if (parser_magic_comment_key_p(key_start, key_length, "coding")) {
7782 result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7783 }
7784 }
7785 break;
7786 case 8:
7787 /* We only want to attempt to compare against encoding comments
7788 * if it is the first line in the file (or the second in the
7789 * case of a shebang). */
7790 if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7791 if (parser_magic_comment_key_p(key_start, key_length, "encoding")) {
7792 result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7793 }
7794 }
7795 break;
7796 case 11:
7797 if (parser_magic_comment_key_p(key_start, key_length, "warn_indent")) {
7798 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7799 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7800 PM_PARSER_WARN_TOKEN_FORMAT(
7801 parser,
7802 &parser->current,
7803 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7804 (int) key_length,
7805 "warn_indent",
7806 (int) value_length,
7807 (const char *) value_start
7808 );
7809 break;
7810 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7811 parser->warn_mismatched_indentation = false;
7812 break;
7813 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7814 parser->warn_mismatched_indentation = true;
7815 break;
7816 }
7817 }
7818 break;
7819 case 21:
7820 if (parser_magic_comment_key_p(key_start, key_length, "frozen_string_literal")) {
7821 /* We only want to handle frozen string literal comments if
7822 * it is before any semantic tokens have been seen. */
7823 if (semantic_token_seen) {
7824 pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
7825 } else {
7826 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7827 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7828 PM_PARSER_WARN_TOKEN_FORMAT(
7829 parser,
7830 &parser->current,
7831 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7832 (int) key_length,
7833 "frozen_string_literal",
7834 (int) value_length,
7835 (const char *) value_start
7836 );
7837 break;
7838 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7839 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
7840 break;
7841 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7842 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
7843 break;
7844 }
7845 }
7846 }
7847 break;
7848 case 24:
7849 if (parser_magic_comment_key_p(key_start, key_length, "shareable_constant_value")) {
7850 const uint8_t *cursor = parser->current.start;
7851 while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
7852
7853 if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
7854 pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
7855 } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
7856 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
7857 } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
7858 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
7859 } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
7860 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
7861 } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
7862 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
7863 } else {
7864 PM_PARSER_WARN_TOKEN_FORMAT(
7865 parser,
7866 &parser->current,
7867 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7868 (int) key_length,
7869 "shareable_constant_value",
7870 (int) value_length,
7871 (const char *) value_start
7872 );
7873 }
7874 }
7875 break;
7876 }
7877
7878 // Allocate a new magic comment node to append to the parser's list.
7879 pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_magic_comment_t), PRISM_ALIGNOF(pm_magic_comment_t));
7880 magic_comment->node.next = NULL;
7881 magic_comment->key = (pm_location_t) { .start = U32(key_start - parser->start), .length = U32(key_length) };
7882 magic_comment->value = (pm_location_t) { .start = U32(value_start - parser->start), .length = value_length };
7883 pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment);
7884 }
7885
7886 return result;
7887}
7888
7889/******************************************************************************/
7890/* Context manipulations */
7891/******************************************************************************/
7892
7893static const uint32_t context_terminators[] = {
7894 [PM_CONTEXT_NONE] = 0,
7895 [PM_CONTEXT_BEGIN] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7896 [PM_CONTEXT_BEGIN_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7897 [PM_CONTEXT_BEGIN_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7898 [PM_CONTEXT_BEGIN_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7899 [PM_CONTEXT_BLOCK_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7900 [PM_CONTEXT_BLOCK_KEYWORDS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7901 [PM_CONTEXT_BLOCK_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7902 [PM_CONTEXT_BLOCK_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7903 [PM_CONTEXT_BLOCK_PARAMETERS] = (1U << PM_TOKEN_PIPE),
7904 [PM_CONTEXT_BLOCK_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7905 [PM_CONTEXT_CASE_WHEN] = (1U << PM_TOKEN_KEYWORD_WHEN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7906 [PM_CONTEXT_CASE_IN] = (1U << PM_TOKEN_KEYWORD_IN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7907 [PM_CONTEXT_CLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7908 [PM_CONTEXT_CLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7909 [PM_CONTEXT_CLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7910 [PM_CONTEXT_CLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7911 [PM_CONTEXT_DEF] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7912 [PM_CONTEXT_DEF_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7913 [PM_CONTEXT_DEF_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7914 [PM_CONTEXT_DEF_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7915 [PM_CONTEXT_DEF_PARAMS] = (1U << PM_TOKEN_EOF),
7916 [PM_CONTEXT_DEFINED] = (1U << PM_TOKEN_EOF),
7917 [PM_CONTEXT_DEFAULT_PARAMS] = (1U << PM_TOKEN_COMMA) | (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7918 [PM_CONTEXT_ELSE] = (1U << PM_TOKEN_KEYWORD_END),
7919 [PM_CONTEXT_ELSIF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7920 [PM_CONTEXT_EMBEXPR] = (1U << PM_TOKEN_EMBEXPR_END),
7921 [PM_CONTEXT_FOR] = (1U << PM_TOKEN_KEYWORD_END),
7922 [PM_CONTEXT_FOR_INDEX] = (1U << PM_TOKEN_KEYWORD_IN),
7923 [PM_CONTEXT_IF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7924 [PM_CONTEXT_LAMBDA_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7925 [PM_CONTEXT_LAMBDA_DO_END] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7926 [PM_CONTEXT_LAMBDA_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7927 [PM_CONTEXT_LAMBDA_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7928 [PM_CONTEXT_LAMBDA_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7929 [PM_CONTEXT_LOOP_PREDICATE] = (1U << PM_TOKEN_KEYWORD_DO) | (1U << PM_TOKEN_KEYWORD_THEN),
7930 [PM_CONTEXT_MAIN] = (1U << PM_TOKEN_EOF),
7931 [PM_CONTEXT_MODULE] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7932 [PM_CONTEXT_MODULE_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7933 [PM_CONTEXT_MODULE_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7934 [PM_CONTEXT_MODULE_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7935 [PM_CONTEXT_MULTI_TARGET] = (1U << PM_TOKEN_EOF),
7936 [PM_CONTEXT_PARENS] = (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7937 [PM_CONTEXT_POSTEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7938 [PM_CONTEXT_PREDICATE] = (1U << PM_TOKEN_KEYWORD_THEN) | (1U << PM_TOKEN_NEWLINE) | (1U << PM_TOKEN_SEMICOLON),
7939 [PM_CONTEXT_PREEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7940 [PM_CONTEXT_RESCUE_MODIFIER] = (1U << PM_TOKEN_EOF),
7941 [PM_CONTEXT_SCLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7942 [PM_CONTEXT_SCLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7943 [PM_CONTEXT_SCLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7944 [PM_CONTEXT_SCLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7945 [PM_CONTEXT_TERNARY] = (1U << PM_TOKEN_EOF),
7946 [PM_CONTEXT_UNLESS] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7947 [PM_CONTEXT_UNTIL] = (1U << PM_TOKEN_KEYWORD_END),
7948 [PM_CONTEXT_WHILE] = (1U << PM_TOKEN_KEYWORD_END),
7949};
7950
7951static PRISM_INLINE bool
7952context_terminator(pm_context_t context, pm_token_t *token) {
7953 return token->type < 32 && (context_terminators[context] & (1U << token->type));
7954}
7955
7960static pm_context_t
7961context_recoverable(const pm_parser_t *parser, pm_token_t *token) {
7962 pm_context_node_t *context_node = parser->current_context;
7963
7964 while (context_node != NULL) {
7965 if (context_terminator(context_node->context, token)) return context_node->context;
7966 context_node = context_node->prev;
7967 }
7968
7969 return PM_CONTEXT_NONE;
7970}
7971
7972PM_STATIC_ASSERT(__LINE__, PM_CONTEXT_MAXIMUM <= 64, "Expected every context to fit in the context mask.");
7973
7974static bool
7975context_push(pm_parser_t *parser, pm_context_t context) {
7976 pm_context_node_t *context_node = parser->free_contexts;
7977 if (context_node != NULL) {
7978 parser->free_contexts = context_node->prev;
7979 } else {
7980 context_node = (pm_context_node_t *) pm_arena_alloc(&parser->scratch_arena, sizeof(pm_context_node_t), PRISM_ALIGNOF(pm_context_node_t));
7981 }
7982
7983 pm_context_node_t *previous = parser->current_context;
7984 *context_node = (pm_context_node_t) {
7985 .context = context,
7986 .prev = previous,
7987 .mask = (previous == NULL ? 0 : previous->mask) | ((uint64_t) 1 << context)
7988 };
7989
7990 parser->current_context = context_node;
7991 return true;
7992}
7993
7994static void
7995context_pop(pm_parser_t *parser) {
7996 pm_context_node_t *context_node = parser->current_context;
7997 parser->current_context = context_node->prev;
7998 context_node->prev = parser->free_contexts;
7999 parser->free_contexts = context_node;
8000}
8001
8002static bool
8003context_p(const pm_parser_t *parser, pm_context_t context) {
8004 return parser->current_context != NULL && (parser->current_context->mask & ((uint64_t) 1 << context)) != 0;
8005}
8006
8007static bool
8008context_def_p(const pm_parser_t *parser) {
8009 pm_context_node_t *context_node = parser->current_context;
8010
8011 while (context_node != NULL) {
8012 switch (context_node->context) {
8013 case PM_CONTEXT_DEF:
8014 case PM_CONTEXT_DEF_PARAMS:
8015 case PM_CONTEXT_DEF_ENSURE:
8016 case PM_CONTEXT_DEF_RESCUE:
8017 case PM_CONTEXT_DEF_ELSE:
8018 return true;
8019 case PM_CONTEXT_CLASS:
8020 case PM_CONTEXT_CLASS_ENSURE:
8021 case PM_CONTEXT_CLASS_RESCUE:
8022 case PM_CONTEXT_CLASS_ELSE:
8023 case PM_CONTEXT_MODULE:
8024 case PM_CONTEXT_MODULE_ENSURE:
8025 case PM_CONTEXT_MODULE_RESCUE:
8026 case PM_CONTEXT_MODULE_ELSE:
8027 case PM_CONTEXT_SCLASS:
8028 case PM_CONTEXT_SCLASS_ENSURE:
8029 case PM_CONTEXT_SCLASS_RESCUE:
8030 case PM_CONTEXT_SCLASS_ELSE:
8031 return false;
8032 default:
8033 context_node = context_node->prev;
8034 }
8035 }
8036
8037 return false;
8038}
8039
8044static const char *
8045context_human(pm_context_t context) {
8046 switch (context) {
8047 case PM_CONTEXT_NONE:
8048 case PM_CONTEXT_MAXIMUM:
8049 assert(false && "unreachable");
8050 return "";
8051 case PM_CONTEXT_BEGIN: return "begin statement";
8052 case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block";
8053 case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block";
8054 case PM_CONTEXT_BLOCK_PARAMETERS: return "'|'..'|' block parameter";
8055 case PM_CONTEXT_CASE_WHEN: return "'when' clause";
8056 case PM_CONTEXT_CASE_IN: return "'in' clause";
8057 case PM_CONTEXT_CLASS: return "class definition";
8058 case PM_CONTEXT_DEF: return "method definition";
8059 case PM_CONTEXT_DEF_PARAMS: return "method parameters";
8060 case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value";
8061 case PM_CONTEXT_DEFINED: return "'defined?' expression";
8062 case PM_CONTEXT_ELSE:
8063 case PM_CONTEXT_BEGIN_ELSE:
8064 case PM_CONTEXT_BLOCK_ELSE:
8065 case PM_CONTEXT_CLASS_ELSE:
8066 case PM_CONTEXT_DEF_ELSE:
8067 case PM_CONTEXT_LAMBDA_ELSE:
8068 case PM_CONTEXT_MODULE_ELSE:
8069 case PM_CONTEXT_SCLASS_ELSE: return "'else' clause";
8070 case PM_CONTEXT_ELSIF: return "'elsif' clause";
8071 case PM_CONTEXT_EMBEXPR: return "embedded expression";
8072 case PM_CONTEXT_BEGIN_ENSURE:
8073 case PM_CONTEXT_BLOCK_ENSURE:
8074 case PM_CONTEXT_CLASS_ENSURE:
8075 case PM_CONTEXT_DEF_ENSURE:
8076 case PM_CONTEXT_LAMBDA_ENSURE:
8077 case PM_CONTEXT_MODULE_ENSURE:
8078 case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause";
8079 case PM_CONTEXT_FOR: return "for loop";
8080 case PM_CONTEXT_FOR_INDEX: return "for loop index";
8081 case PM_CONTEXT_IF: return "if statement";
8082 case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block";
8083 case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block";
8084 case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate";
8085 case PM_CONTEXT_MAIN: return "top level context";
8086 case PM_CONTEXT_MODULE: return "module definition";
8087 case PM_CONTEXT_MULTI_TARGET: return "multiple targets";
8088 case PM_CONTEXT_PARENS: return "parentheses";
8089 case PM_CONTEXT_POSTEXE: return "'END' block";
8090 case PM_CONTEXT_PREDICATE: return "predicate";
8091 case PM_CONTEXT_PREEXE: return "'BEGIN' block";
8092 case PM_CONTEXT_BEGIN_RESCUE:
8093 case PM_CONTEXT_BLOCK_RESCUE:
8094 case PM_CONTEXT_CLASS_RESCUE:
8095 case PM_CONTEXT_DEF_RESCUE:
8096 case PM_CONTEXT_LAMBDA_RESCUE:
8097 case PM_CONTEXT_MODULE_RESCUE:
8098 case PM_CONTEXT_RESCUE_MODIFIER:
8099 case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause";
8100 case PM_CONTEXT_SCLASS: return "singleton class definition";
8101 case PM_CONTEXT_TERNARY: return "ternary expression";
8102 case PM_CONTEXT_UNLESS: return "unless statement";
8103 case PM_CONTEXT_UNTIL: return "until statement";
8104 case PM_CONTEXT_WHILE: return "while statement";
8105 }
8106
8107 assert(false && "unreachable");
8108 return "";
8109}
8110
8111/******************************************************************************/
8112/* Specific token lexers */
8113/******************************************************************************/
8114
8115static PRISM_INLINE void
8116pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *string, size_t length, const uint8_t *invalid) {
8117 if (invalid != NULL) {
8118 pm_diagnostic_id_t diag_id = (invalid == (string + length - 1)) ? PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING : PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER;
8119 pm_parser_err(parser, U32(invalid - parser->start), 1, diag_id);
8120 }
8121}
8122
8123static size_t
8124pm_strspn_binary_number_validate(pm_parser_t *parser, const uint8_t *string) {
8125 const uint8_t *invalid = NULL;
8126 size_t length = pm_strspn_binary_number(string, parser->end - string, &invalid);
8127 pm_strspn_number_validate(parser, string, length, invalid);
8128 return length;
8129}
8130
8131static size_t
8132pm_strspn_octal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8133 const uint8_t *invalid = NULL;
8134 size_t length = pm_strspn_octal_number(string, parser->end - string, &invalid);
8135 pm_strspn_number_validate(parser, string, length, invalid);
8136 return length;
8137}
8138
8139static size_t
8140pm_strspn_decimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8141 const uint8_t *invalid = NULL;
8142 size_t length = pm_strspn_decimal_number(string, parser->end - string, &invalid);
8143 pm_strspn_number_validate(parser, string, length, invalid);
8144 return length;
8145}
8146
8147static size_t
8148pm_strspn_hexadecimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8149 const uint8_t *invalid = NULL;
8150 size_t length = pm_strspn_hexadecimal_number(string, parser->end - string, &invalid);
8151 pm_strspn_number_validate(parser, string, length, invalid);
8152 return length;
8153}
8154
8155static pm_token_type_t
8156lex_optional_float_suffix(pm_parser_t *parser, bool* seen_e) {
8157 pm_token_type_t type = PM_TOKEN_INTEGER;
8158
8159 // Here we're going to attempt to parse the optional decimal portion of a
8160 // float. If it's not there, then it's okay and we'll just continue on.
8161 if (peek(parser) == '.') {
8162 if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8163 parser->current.end += 2;
8164 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8165 type = PM_TOKEN_FLOAT;
8166 } else {
8167 // If we had a . and then something else, then it's not a float
8168 // suffix on a number it's a method call or something else.
8169 return type;
8170 }
8171 }
8172
8173 // Here we're going to attempt to parse the optional exponent portion of a
8174 // float. If it's not there, it's okay and we'll just continue on.
8175 if ((peek(parser) == 'e') || (peek(parser) == 'E')) {
8176 if ((peek_offset(parser, 1) == '+') || (peek_offset(parser, 1) == '-')) {
8177 parser->current.end += 2;
8178
8179 if (pm_char_is_decimal_digit(peek(parser))) {
8180 parser->current.end++;
8181 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8182 } else {
8183 pm_parser_err_current(parser, PM_ERR_INVALID_FLOAT_EXPONENT);
8184 }
8185 } else if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8186 parser->current.end++;
8187 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8188 } else {
8189 return type;
8190 }
8191
8192 *seen_e = true;
8193 type = PM_TOKEN_FLOAT;
8194 }
8195
8196 return type;
8197}
8198
8199static pm_token_type_t
8200lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) {
8201 pm_token_type_t type = PM_TOKEN_INTEGER;
8202 *seen_e = false;
8203
8204 if (peek_offset(parser, -1) == '0') {
8205 switch (*parser->current.end) {
8206 // 0d1111 is a decimal number
8207 case 'd':
8208 case 'D':
8209 parser->current.end++;
8210 if (pm_char_is_decimal_digit(peek(parser))) {
8211 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8212 } else {
8213 match(parser, '_');
8214 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_DECIMAL);
8215 }
8216
8217 break;
8218
8219 // 0b1111 is a binary number
8220 case 'b':
8221 case 'B':
8222 parser->current.end++;
8223 if (pm_char_is_binary_digit(peek(parser))) {
8224 parser->current.end += pm_strspn_binary_number_validate(parser, parser->current.end);
8225 } else {
8226 match(parser, '_');
8227 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY);
8228 }
8229
8230 parser->integer.base = PM_INTEGER_BASE_FLAGS_BINARY;
8231 break;
8232
8233 // 0o1111 is an octal number
8234 case 'o':
8235 case 'O':
8236 parser->current.end++;
8237 if (pm_char_is_octal_digit(peek(parser))) {
8238 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8239 } else {
8240 match(parser, '_');
8241 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL);
8242 }
8243
8244 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8245 break;
8246
8247 // 01111 is an octal number
8248 case '_':
8249 case '0':
8250 case '1':
8251 case '2':
8252 case '3':
8253 case '4':
8254 case '5':
8255 case '6':
8256 case '7':
8257 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8258 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8259 break;
8260
8261 // 0x1111 is a hexadecimal number
8262 case 'x':
8263 case 'X':
8264 parser->current.end++;
8265 if (pm_char_is_hexadecimal_digit(peek(parser))) {
8266 parser->current.end += pm_strspn_hexadecimal_number_validate(parser, parser->current.end);
8267 } else {
8268 match(parser, '_');
8269 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL);
8270 }
8271
8272 parser->integer.base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL;
8273 break;
8274
8275 // 0.xxx is a float
8276 case '.': {
8277 type = lex_optional_float_suffix(parser, seen_e);
8278 break;
8279 }
8280
8281 // 0exxx is a float
8282 case 'e':
8283 case 'E': {
8284 type = lex_optional_float_suffix(parser, seen_e);
8285 break;
8286 }
8287 }
8288 } else {
8289 // If it didn't start with a 0, then we'll lex as far as we can into a
8290 // decimal number. We compute the integer value inline to avoid
8291 // re-scanning the digits later in pm_integer_parse.
8292 {
8293 const uint8_t *cursor = parser->current.end;
8294 const uint8_t *end = parser->end;
8295 uint64_t value = (uint64_t) (cursor[-1] - '0');
8296
8297 bool has_underscore = false;
8298 bool prev_underscore = false;
8299 const uint8_t *invalid = NULL;
8300
8301 while (cursor < end) {
8302 uint8_t c = *cursor;
8303 if (c >= '0' && c <= '9') {
8304 if (value <= UINT32_MAX) value = value * 10 + (uint64_t) (c - '0');
8305 prev_underscore = false;
8306 cursor++;
8307 } else if (c == '_') {
8308 has_underscore = true;
8309 if (prev_underscore && invalid == NULL) invalid = cursor;
8310 prev_underscore = true;
8311 cursor++;
8312 } else {
8313 break;
8314 }
8315 }
8316
8317 if (has_underscore) {
8318 if (prev_underscore && invalid == NULL) invalid = cursor - 1;
8319 pm_strspn_number_validate(parser, parser->current.end, (size_t) (cursor - parser->current.end), invalid);
8320 }
8321
8322 if (value <= UINT32_MAX) {
8323 parser->integer.value = (uint32_t) value;
8324 parser->integer.lexed = true;
8325 }
8326
8327 parser->current.end = cursor;
8328 }
8329
8330 // Afterward, we'll lex as far as we can into an optional float suffix.
8331 // Guard the function call: the vast majority of decimal numbers are
8332 // plain integers, so avoid the call when the next byte cannot start a
8333 // float suffix.
8334 {
8335 uint8_t next = peek(parser);
8336 if (next == '.' || next == 'e' || next == 'E') {
8337 type = lex_optional_float_suffix(parser, seen_e);
8338
8339 // If it turned out to be a float, the cached integer value is
8340 // invalid.
8341 if (type != PM_TOKEN_INTEGER) {
8342 parser->integer.lexed = false;
8343 }
8344 }
8345 }
8346 }
8347
8348 // At this point we have a completed number, but we want to provide the user
8349 // with a good experience if they put an additional .xxx fractional
8350 // component on the end, so we'll check for that here.
8351 if (peek_offset(parser, 0) == '.' && pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8352 const uint8_t *fraction_start = parser->current.end;
8353 const uint8_t *fraction_end = parser->current.end + 2;
8354 fraction_end += pm_strspn_decimal_digit(fraction_end, parser->end - fraction_end);
8355 pm_parser_err(parser, U32(fraction_start - parser->start), U32(fraction_end - fraction_start), PM_ERR_INVALID_NUMBER_FRACTION);
8356 }
8357
8358 return type;
8359}
8360
8361static pm_token_type_t
8362lex_numeric(pm_parser_t *parser) {
8363 pm_token_type_t type = PM_TOKEN_INTEGER;
8364 parser->integer.base = PM_INTEGER_BASE_FLAGS_DECIMAL;
8365 parser->integer.lexed = false;
8366
8367 if (parser->current.end < parser->end) {
8368 bool seen_e = false;
8369 type = lex_numeric_prefix(parser, &seen_e);
8370
8371 const uint8_t *end = parser->current.end;
8372 pm_token_type_t suffix_type = type;
8373
8374 if (type == PM_TOKEN_INTEGER) {
8375 if (match(parser, 'r')) {
8376 suffix_type = PM_TOKEN_INTEGER_RATIONAL;
8377
8378 if (match(parser, 'i')) {
8379 suffix_type = PM_TOKEN_INTEGER_RATIONAL_IMAGINARY;
8380 }
8381 } else if (match(parser, 'i')) {
8382 suffix_type = PM_TOKEN_INTEGER_IMAGINARY;
8383 }
8384 } else {
8385 if (!seen_e && match(parser, 'r')) {
8386 suffix_type = PM_TOKEN_FLOAT_RATIONAL;
8387
8388 if (match(parser, 'i')) {
8389 suffix_type = PM_TOKEN_FLOAT_RATIONAL_IMAGINARY;
8390 }
8391 } else if (match(parser, 'i')) {
8392 suffix_type = PM_TOKEN_FLOAT_IMAGINARY;
8393 }
8394 }
8395
8396 const uint8_t b = peek(parser);
8397 if (b != '\0' && (b >= 0x80 || ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) || b == '_')) {
8398 parser->current.end = end;
8399 } else {
8400 type = suffix_type;
8401 }
8402 }
8403
8404 return type;
8405}
8406
8407static pm_token_type_t
8408lex_global_variable(pm_parser_t *parser) {
8409 if (parser->current.end >= parser->end) {
8410 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8411 return PM_TOKEN_GLOBAL_VARIABLE;
8412 }
8413
8414 // True if multiple characters are allowed after the declaration of the
8415 // global variable. Not true when it starts with "$-".
8416 bool allow_multiple = true;
8417
8418 switch (*parser->current.end) {
8419 case '~': // $~: match-data
8420 case '*': // $*: argv
8421 case '$': // $$: pid
8422 case '?': // $?: last status
8423 case '!': // $!: error string
8424 case '@': // $@: error position
8425 case '/': // $/: input record separator
8426 case '\\': // $\: output record separator
8427 case ';': // $;: field separator
8428 case ',': // $,: output field separator
8429 case '.': // $.: last read line number
8430 case '=': // $=: ignorecase
8431 case ':': // $:: load path
8432 case '<': // $<: reading filename
8433 case '>': // $>: default output handle
8434 case '\"': // $": already loaded files
8435 parser->current.end++;
8436 return PM_TOKEN_GLOBAL_VARIABLE;
8437
8438 case '&': // $&: last match
8439 case '`': // $`: string before last match
8440 case '\'': // $': string after last match
8441 case '+': // $+: string matches last paren.
8442 parser->current.end++;
8443 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_BACK_REFERENCE;
8444
8445 case '0': {
8446 parser->current.end++;
8447 size_t width;
8448
8449 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8450 do {
8451 parser->current.end += width;
8452 } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8453
8454 // $0 isn't allowed to be followed by anything.
8455 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;
8456 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, diag_id);
8457 }
8458
8459 return PM_TOKEN_GLOBAL_VARIABLE;
8460 }
8461
8462 case '1':
8463 case '2':
8464 case '3':
8465 case '4':
8466 case '5':
8467 case '6':
8468 case '7':
8469 case '8':
8470 case '9':
8471 parser->current.end += pm_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end);
8472 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_NUMBERED_REFERENCE;
8473
8474 case '-':
8475 parser->current.end++;
8476 allow_multiple = false;
8478 default: {
8479 size_t width;
8480
8481 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8482 do {
8483 parser->current.end += width;
8484 } while (allow_multiple && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8485 } else if (pm_char_is_whitespace(peek(parser))) {
8486 // If we get here, then we have a $ followed by whitespace,
8487 // which is not allowed.
8488 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8489 } else {
8490 // If we get here, then we have a $ followed by something that
8491 // isn't recognized as a global variable.
8492 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;
8493 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
8494 PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), diag_id, (int) (PM_TOKEN_LENGTH(&parser->current) + U32(width)), (const char *) parser->current.start);
8495 }
8496
8497 return PM_TOKEN_GLOBAL_VARIABLE;
8498 }
8499 }
8500}
8501
8506typedef struct {
8507 /* The keyword itself. */
8508 const char *name;
8509
8510 /* The keyword's first, middle, and last bytes and its length, packed by
8511 * PM_KEYWORD_PACK. */
8512 uint32_t packed;
8513
8514 /* The state to transition to when the keyword is lexed. */
8515 pm_lex_state_t state;
8516
8517 /* The token type of the keyword. */
8518 pm_token_type_t type;
8519
8520 /* The token type of the keyword in modifier position, or PM_TOKEN_EOF if
8521 * the keyword has no modifier form. */
8522 pm_token_type_t modifier_type;
8523} pm_keyword_t;
8524
8528#define PM_KEYWORD_LENGTH_MAXIMUM 12
8529
8534#define PM_KEYWORD_PACK(first, middle, last, length) \
8535 ((uint32_t) (first) | ((uint32_t) (middle) << 8) | ((uint32_t) (last) << 16) | ((uint32_t) (length) << 24))
8536
8541#define PM_KEYWORD_SLOT_BITS 6
8542
8556#define PM_KEYWORD_SLOT(packed) ((uint32_t) ((uint32_t) (packed) * 0x75f796ddU) >> (32 - PM_KEYWORD_SLOT_BITS))
8557
8562#define PM_KEYWORD(name, first, middle, last, state, type, modifier_type) \
8563 [PM_KEYWORD_SLOT(PM_KEYWORD_PACK(first, middle, last, sizeof(name) - 1))] = { name, PM_KEYWORD_PACK(first, middle, last, sizeof(name) - 1), state, type, modifier_type }
8564
8570static const pm_keyword_t pm_keywords[1 << PM_KEYWORD_SLOT_BITS] = {
8571 PM_KEYWORD("do", 'd', 'o', 'o', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF),
8572 PM_KEYWORD("if", 'i', 'f', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER),
8573 PM_KEYWORD("in", 'i', 'n', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IN, PM_TOKEN_EOF),
8574 PM_KEYWORD("or", 'o', 'r', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_OR, PM_TOKEN_EOF),
8575 PM_KEYWORD("and", 'a', 'n', 'd', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_AND, PM_TOKEN_EOF),
8576 PM_KEYWORD("def", 'd', 'e', 'f', PM_LEX_STATE_FNAME, PM_TOKEN_KEYWORD_DEF, PM_TOKEN_EOF),
8577 PM_KEYWORD("end", 'e', 'n', 'd', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END, PM_TOKEN_EOF),
8578 PM_KEYWORD("END", 'E', 'N', 'D', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END_UPCASE, PM_TOKEN_EOF),
8579 PM_KEYWORD("for", 'f', 'o', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_FOR, PM_TOKEN_EOF),
8580 PM_KEYWORD("nil", 'n', 'i', 'l', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_NIL, PM_TOKEN_EOF),
8581 PM_KEYWORD("not", 'n', 'o', 't', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_NOT, PM_TOKEN_EOF),
8582 PM_KEYWORD("case", 'c', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_CASE, PM_TOKEN_EOF),
8583 PM_KEYWORD("else", 'e', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_EOF),
8584 PM_KEYWORD("next", 'n', 'x', 't', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_NEXT, PM_TOKEN_EOF),
8585 PM_KEYWORD("redo", 'r', 'd', 'o', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_REDO, PM_TOKEN_EOF),
8586 PM_KEYWORD("self", 's', 'l', 'f', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_SELF, PM_TOKEN_EOF),
8587 PM_KEYWORD("then", 't', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_EOF),
8588 PM_KEYWORD("true", 't', 'u', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_TRUE, PM_TOKEN_EOF),
8589 PM_KEYWORD("when", 'w', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_EOF),
8590 PM_KEYWORD("alias", 'a', 'i', 's', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_ALIAS, PM_TOKEN_EOF),
8591 PM_KEYWORD("begin", 'b', 'g', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_BEGIN, PM_TOKEN_EOF),
8592 PM_KEYWORD("BEGIN", 'B', 'G', 'N', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_BEGIN_UPCASE, PM_TOKEN_EOF),
8593 PM_KEYWORD("break", 'b', 'e', 'k', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_BREAK, PM_TOKEN_EOF),
8594 PM_KEYWORD("class", 'c', 'a', 's', PM_LEX_STATE_CLASS, PM_TOKEN_KEYWORD_CLASS, PM_TOKEN_EOF),
8595 PM_KEYWORD("elsif", 'e', 's', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_EOF),
8596 PM_KEYWORD("false", 'f', 'l', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_FALSE, PM_TOKEN_EOF),
8597 PM_KEYWORD("retry", 'r', 't', 'y', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_RETRY, PM_TOKEN_EOF),
8598 PM_KEYWORD("super", 's', 'p', 'r', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_SUPER, PM_TOKEN_EOF),
8599 PM_KEYWORD("undef", 'u', 'd', 'f', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_UNDEF, PM_TOKEN_EOF),
8600 PM_KEYWORD("until", 'u', 't', 'l', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNTIL, PM_TOKEN_KEYWORD_UNTIL_MODIFIER),
8601 PM_KEYWORD("while", 'w', 'i', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHILE, PM_TOKEN_KEYWORD_WHILE_MODIFIER),
8602 PM_KEYWORD("yield", 'y', 'e', 'd', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_YIELD, PM_TOKEN_EOF),
8603 PM_KEYWORD("ensure", 'e', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_EOF),
8604 PM_KEYWORD("module", 'm', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_MODULE, PM_TOKEN_EOF),
8605 PM_KEYWORD("rescue", 'r', 'c', 'e', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_RESCUE_MODIFIER),
8606 PM_KEYWORD("return", 'r', 'u', 'n', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RETURN, PM_TOKEN_EOF),
8607 PM_KEYWORD("unless", 'u', 'e', 's', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNLESS, PM_TOKEN_KEYWORD_UNLESS_MODIFIER),
8608 PM_KEYWORD("defined?", 'd', 'n', '?', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF),
8609 PM_KEYWORD("__LINE__", '_', 'N', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___LINE__, PM_TOKEN_EOF),
8610 PM_KEYWORD("__FILE__", '_', 'L', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___FILE__, PM_TOKEN_EOF),
8611 PM_KEYWORD("__ENCODING__", '_', 'D', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___ENCODING__, PM_TOKEN_EOF)
8612};
8613
8614#undef PM_KEYWORD
8615
8620static PRISM_INLINE const pm_keyword_t *
8621pm_keyword_find(const uint8_t *start, const uint8_t *end) {
8622 ptrdiff_t width = end - start;
8623 if (width < 2 || width > PM_KEYWORD_LENGTH_MAXIMUM) return NULL;
8624
8625 size_t uwidth = (size_t) width;
8626 uint32_t packed = PM_KEYWORD_PACK(start[0], start[uwidth >> 1], start[uwidth - 1], uwidth);
8627
8628 const pm_keyword_t *keyword = &pm_keywords[PM_KEYWORD_SLOT(packed)];
8629 return (keyword->packed == packed && memcmp(start + 1, keyword->name + 1, uwidth - 2) == 0) ? keyword : NULL;
8630}
8631
8636bool
8637pm_keyword_p(const uint8_t *start, const uint8_t *end) {
8638 return pm_keyword_find(start, end) != NULL;
8639}
8640
8641static pm_token_type_t
8642lex_identifier(pm_parser_t *parser, bool previous_command_start) {
8643 /* Lex as far as we can into the current identifier. */
8644 const uint8_t *end = parser->end;
8645 const uint8_t *current_start = parser->current.start;
8646 const uint8_t *current_end = parser->current.end;
8647 bool encoding_changed = parser->encoding_changed;
8648
8649 if (encoding_changed) {
8650 size_t width;
8651 while ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) {
8652 current_end += width;
8653 }
8654 } else {
8655 /* Fast path: scan ASCII identifier bytes using wide operations. */
8656 current_end += scan_identifier_ascii(current_end, end);
8657
8658 /* Byte-at-a-time fallback for the tail and any UTF-8 sequences. */
8659 size_t width;
8660 while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) {
8661 current_end += width;
8662 }
8663 }
8664
8665 parser->current.end = current_end;
8666
8667 /* Whether the identifier ends in ! or ?, which makes it a method name
8668 * unless it spells a keyword (defined?). */
8669 bool method_name = false;
8670
8671 if (current_end < end) {
8672 if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
8673 /* Extend the identifier by the ! or ?, so that the keyword lookup
8674 * below sees the whole name. */
8675 current_end++;
8676
8677 if (
8678 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8679 (peek(parser) == ':') && (peek_offset(parser, 1) != ':')
8680 ) {
8681 /* If we are in a position where we can accept a : at the end of
8682 * an identifier, then we will optionally accept it. */
8683 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8684 (void) match(parser, ':');
8685
8686 /* A label is a symbol lexed inline rather than through a lex
8687 * mode, so it clears the encoding here. */
8688 parser->explicit_encoding = NULL;
8689 return PM_TOKEN_LABEL;
8690 }
8691
8692 method_name = true;
8693 } else if (lex_state_p(parser, PM_LEX_STATE_FNAME) && peek_offset(parser, 1) != '~' && peek_offset(parser, 1) != '>' && (peek_offset(parser, 1) != '=' || peek_offset(parser, 2) == '>') && match(parser, '=')) {
8694 /* If we are in a position where we can accept a = at the end of an
8695 * identifier, then we will optionally accept it. */
8696 return PM_TOKEN_IDENTIFIER;
8697 } else if (
8698 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8699 peek(parser) == ':' && peek_offset(parser, 1) != ':'
8700 ) {
8701 /* If we are in a position where we can accept a : at the end of an
8702 * identifier, then we will optionally accept it. */
8703 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8704 (void) match(parser, ':');
8705
8706 /* A label is a symbol lexed inline rather than through a lex
8707 * mode, so it clears the encoding here. */
8708 parser->explicit_encoding = NULL;
8709 return PM_TOKEN_LABEL;
8710 }
8711 }
8712
8713 if (parser->lex_state != PM_LEX_STATE_DOT) {
8714 /* The lex state from before the keyword changes it, which decides
8715 * whether the keyword is in its modifier form and whether `do` is a
8716 * plain name below. */
8717 pm_lex_state_t previous_lex_state = parser->lex_state;
8718 const pm_keyword_t *keyword = pm_keyword_find(current_start, current_end);
8719
8720 if (keyword != NULL) {
8721 if (previous_lex_state & PM_LEX_STATE_FNAME) {
8722 lex_state_set(parser, PM_LEX_STATE_ENDFN);
8723 } else {
8724 lex_state_set(parser, keyword->state);
8725 if (keyword->state == PM_LEX_STATE_BEG) {
8726 parser->command_start = true;
8727 }
8728
8729 if ((keyword->modifier_type != PM_TOKEN_EOF) && !(previous_lex_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
8730 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
8731 return keyword->modifier_type;
8732 }
8733 }
8734
8735 if (keyword->type == PM_TOKEN_KEYWORD_DO) {
8736 /* In FNAME position (a symbol like `:do` or a method name like
8737 * `def do`), `do` is a plain name rather than a block, loop, or
8738 * lambda opener, so none of the discrimination below applies
8739 * and the lambda nesting is left untouched. */
8740 if (previous_lex_state & PM_LEX_STATE_FNAME) {
8741 return PM_TOKEN_KEYWORD_DO;
8742 }
8743 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
8744 /* At the bare nesting level of a lambda literal (no
8745 * delimiter opened since `->`), a `do` opens the lambda
8746 * body. This is a distinct token so that a command in a
8747 * parameter default cannot consume it as its own block
8748 * (`-> a = foo do end` is `->(a = foo) do end`).
8749 *
8750 * Clear the nesting so that no token within the `do`/`end`
8751 * body is considered to be at the beginning of a lambda;
8752 * the parser restores the enclosing value once the lambda
8753 * has been fully parsed. */
8754 parser->lambda_enclosure_nesting = -1;
8755 return PM_TOKEN_KEYWORD_DO_LAMBDA;
8756 }
8757 if (pm_do_loop_stack_p(parser)) {
8758 return PM_TOKEN_KEYWORD_DO_LOOP;
8759 }
8760 if (!pm_accepts_block_stack_p(parser)) {
8761 return PM_TOKEN_KEYWORD_DO_BLOCK;
8762 }
8763 return PM_TOKEN_KEYWORD_DO;
8764 }
8765
8766 return keyword->type;
8767 }
8768 }
8769
8770 if (method_name) {
8771 return PM_TOKEN_METHOD_NAME;
8772 }
8773
8774 if (encoding_changed) {
8775 return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8776 }
8777
8778 /* Identifiers usually start with an ASCII byte, for which the uppercase
8779 * check is a simple range comparison. This avoids the call into the
8780 * encoding module for every identifier. */
8781 if (*current_start < 0x80) {
8782 return (*current_start >= 'A' && *current_start <= 'Z') ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8783 }
8784 return pm_encoding_utf_8_isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8785}
8786
8791static bool
8792current_token_starts_line(pm_parser_t *parser) {
8793 return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n');
8794}
8795
8810static pm_token_type_t
8811lex_interpolation(pm_parser_t *parser, const uint8_t *pound) {
8812 // If there is no content following this #, then we're at the end of
8813 // the string and we can safely return string content.
8814 if (pound + 1 >= parser->end) {
8815 parser->current.end = pound + 1;
8816 return PM_TOKEN_STRING_CONTENT;
8817 }
8818
8819 // Now we'll check against the character that follows the #. If it
8820 // constitutes valid interplation, we'll handle that, otherwise we'll return
8821 // 0.
8822 switch (pound[1]) {
8823 case '@': {
8824 // In this case we may have hit an embedded instance or class variable.
8825 if (pound + 2 >= parser->end) {
8826 parser->current.end = pound + 1;
8827 return PM_TOKEN_STRING_CONTENT;
8828 }
8829
8830 // If we're looking at a @ and there's another @, then we'll skip past the
8831 // second @.
8832 const uint8_t *variable = pound + 2;
8833 if (*variable == '@' && pound + 3 < parser->end) variable++;
8834
8835 if (char_is_identifier_start(parser, variable, parser->end - variable)) {
8836 // At this point we're sure that we've either hit an embedded instance
8837 // or class variable. In this case we'll first need to check if we've
8838 // already consumed content.
8839 if (pound > parser->current.start) {
8840 parser->current.end = pound;
8841 return PM_TOKEN_STRING_CONTENT;
8842 }
8843
8844 // Otherwise we need to return the embedded variable token
8845 // and then switch to the embedded variable lex mode.
8846 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8847 parser->current.end = pound + 1;
8848 return PM_TOKEN_EMBVAR;
8849 }
8850
8851 // If we didn't get a valid interpolation, then this is just regular
8852 // string content. This is like if we get "#@-". In this case the caller
8853 // should keep lexing.
8854 parser->current.end = pound + 1;
8855 return 0;
8856 }
8857 case '$':
8858 // In this case we may have hit an embedded global variable. If there's
8859 // not enough room, then we'll just return string content.
8860 if (pound + 2 >= parser->end) {
8861 parser->current.end = pound + 1;
8862 return PM_TOKEN_STRING_CONTENT;
8863 }
8864
8865 // This is the character that we're going to check to see if it is the
8866 // start of an identifier that would indicate that this is a global
8867 // variable.
8868 const uint8_t *check = pound + 2;
8869
8870 if (pound[2] == '-') {
8871 if (pound + 3 >= parser->end) {
8872 parser->current.end = pound + 2;
8873 return PM_TOKEN_STRING_CONTENT;
8874 }
8875
8876 check++;
8877 }
8878
8879 // If the character that we're going to check is the start of an
8880 // identifier, or we don't have a - and the character is a decimal number
8881 // or a global name punctuation character, then we've hit an embedded
8882 // global variable.
8883 if (
8884 char_is_identifier_start(parser, check, parser->end - check) ||
8885 (pound[2] != '-' && (pm_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2])))
8886 ) {
8887 // In this case we've hit an embedded global variable. First check to
8888 // see if we've already consumed content. If we have, then we need to
8889 // return that content as string content first.
8890 if (pound > parser->current.start) {
8891 parser->current.end = pound;
8892 return PM_TOKEN_STRING_CONTENT;
8893 }
8894
8895 // Otherwise, we need to return the embedded variable token and switch
8896 // to the embedded variable lex mode.
8897 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8898 parser->current.end = pound + 1;
8899 return PM_TOKEN_EMBVAR;
8900 }
8901
8902 // In this case we've hit a #$ that does not indicate a global variable.
8903 // In this case we'll continue lexing past it.
8904 parser->current.end = pound + 1;
8905 return 0;
8906 case '{':
8907 // In this case it's the start of an embedded expression. If we have
8908 // already consumed content, then we need to return that content as string
8909 // content first.
8910 if (pound > parser->current.start) {
8911 parser->current.end = pound;
8912 return PM_TOKEN_STRING_CONTENT;
8913 }
8914
8915 parser->enclosure_nesting++;
8916
8917 // Otherwise we'll skip past the #{ and begin lexing the embedded
8918 // expression.
8919 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBEXPR });
8920 parser->current.end = pound + 2;
8921 parser->command_start = true;
8922 pm_enclosure_frame_push(parser);
8923 return PM_TOKEN_EMBEXPR_BEGIN;
8924 default:
8925 // In this case we've hit a # that doesn't constitute interpolation. We'll
8926 // mark that by returning the not provided token type. This tells the
8927 // consumer to keep lexing forward.
8928 parser->current.end = pound + 1;
8929 return 0;
8930 }
8931}
8932
8933static const uint8_t PM_ESCAPE_FLAG_NONE = 0x0;
8934static const uint8_t PM_ESCAPE_FLAG_CONTROL = 0x1;
8935static const uint8_t PM_ESCAPE_FLAG_META = 0x2;
8936static const uint8_t PM_ESCAPE_FLAG_SINGLE = 0x4;
8937static const uint8_t PM_ESCAPE_FLAG_REGEXP = 0x8;
8938
8942static const bool ascii_printable_chars[] = {
8943 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
8944 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8945 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8946 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8947 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8948 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
8949 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8950 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
8951};
8952
8953static PRISM_INLINE bool
8954char_is_ascii_printable(const uint8_t b) {
8955 return (b < 0x80) && ascii_printable_chars[b];
8956}
8957
8962static PRISM_INLINE uint8_t
8963escape_hexadecimal_digit(const uint8_t value) {
8964 return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9);
8965}
8966
8972static PRISM_INLINE uint32_t
8973escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location, const uint8_t flags) {
8974 uint32_t value = 0;
8975 for (size_t index = 0; index < length; index++) {
8976 if (index != 0) value <<= 4;
8977 value |= escape_hexadecimal_digit(string[index]);
8978 }
8979
8980 // Here we're going to verify that the value is actually a valid Unicode
8981 // codepoint and not a surrogate pair.
8982 if (value >= 0xD800 && value <= 0xDFFF) {
8983 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8984 // In regexp context, defer the error to regexp encoding
8985 // validation where we can produce a regexp-specific message.
8986 } else if (error_location != NULL) {
8987 pm_parser_err(parser, error_location->start, error_location->length, PM_ERR_ESCAPE_INVALID_UNICODE);
8988 } else {
8989 pm_parser_err(parser, U32(string - parser->start), U32(length), PM_ERR_ESCAPE_INVALID_UNICODE);
8990 }
8991 return 0xFFFD;
8992 }
8993
8994 return value;
8995}
8996
9000static PRISM_INLINE uint8_t
9001escape_byte(uint8_t value, const uint8_t flags) {
9002 if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x9f;
9003 if (flags & PM_ESCAPE_FLAG_META) value |= 0x80;
9004 return value;
9005}
9006
9010static PRISM_INLINE void
9011escape_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) {
9012 // \u escape sequences in string-like structures implicitly change the
9013 // encoding to UTF-8 if they are >= 0x80 or if they are used in a character
9014 // literal.
9015 if (value >= 0x80 || flags & PM_ESCAPE_FLAG_SINGLE) {
9016 if (parser->explicit_encoding != NULL && parser->explicit_encoding != PM_ENCODING_UTF_8_ENTRY) {
9017 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9018 // In regexp context, suppress this error — the regexp encoding
9019 // validation will produce a more specific error message.
9020 } else {
9021 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(end - start), PM_ERR_MIXED_ENCODING, parser->explicit_encoding->name);
9022 }
9023 }
9024
9025 parser->explicit_encoding = PM_ENCODING_UTF_8_ENTRY;
9026 }
9027
9028 if (!pm_buffer_append_unicode_codepoint(buffer, value)) {
9029 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9030 // In regexp context, defer the error to the regexp encoding
9031 // validation which produces a regexp-specific message.
9032 } else {
9033 pm_parser_err(parser, U32(start - parser->start), U32(end - start), PM_ERR_ESCAPE_INVALID_UNICODE);
9034 }
9035
9036 pm_buffer_append_byte(buffer, 0xEF);
9037 pm_buffer_append_byte(buffer, 0xBF);
9038 pm_buffer_append_byte(buffer, 0xBD);
9039 }
9040}
9041
9046static PRISM_INLINE void
9047escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t flags, uint8_t byte) {
9048 if (byte >= 0x80) {
9049 if (parser->explicit_encoding != NULL && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
9050 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9051 // In regexp context, suppress this error — the regexp encoding
9052 // validation will produce a more specific error message.
9053 } else {
9054 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_MIXED_ENCODING, parser->encoding->name);
9055 }
9056 }
9057
9058 parser->explicit_encoding = parser->encoding;
9059 }
9060
9061 pm_buffer_append_byte(buffer, byte);
9062}
9063
9079static PRISM_INLINE void
9080escape_write_byte(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags, uint8_t byte) {
9081 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9082 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", byte);
9083 }
9084
9085 escape_write_byte_encoded(parser, buffer, flags, byte);
9086}
9087
9091static PRISM_INLINE void
9092escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9093 size_t width;
9094 if (parser->encoding_changed) {
9095 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9096 } else {
9097 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9098 }
9099
9100 if (width == 1) {
9101 if (parser->heredoc_end == NULL && *parser->current.end == '\n') pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
9102 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(*parser->current.end++, flags));
9103 } else if (width > 1) {
9104 // Valid multibyte character. Just ignore escape.
9105 pm_buffer_t *b = (flags & PM_ESCAPE_FLAG_REGEXP) ? regular_expression_buffer : buffer;
9106 pm_buffer_append_bytes(b, parser->current.end, width);
9107 parser->current.end += width;
9108 } else {
9109 // Assume the next character wasn't meant to be part of this escape
9110 // sequence since it is invalid. Add an error and move on.
9111 parser->current.end++;
9112 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9113 }
9114}
9115
9121static void
9122escape_read_warn(pm_parser_t *parser, uint8_t flags, uint8_t flag, const char *type) {
9123#define FLAG(value) ((value & PM_ESCAPE_FLAG_CONTROL) ? "\\C-" : (value & PM_ESCAPE_FLAG_META) ? "\\M-" : "")
9124
9125 PM_PARSER_WARN_TOKEN_FORMAT(
9126 parser,
9127 &parser->current,
9128 PM_WARN_INVALID_CHARACTER,
9129 FLAG(flags),
9130 FLAG(flag),
9131 type
9132 );
9133
9134#undef FLAG
9135}
9136
9144static void
9145escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9146 uint8_t peeked;
9147
9148 escape_read_next:
9149 switch (peeked = peek(parser)) {
9150 case '\\': {
9151 parser->current.end++;
9152 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\\', flags));
9153 return;
9154 }
9155 case '\'': {
9156 parser->current.end++;
9157 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\'', flags));
9158 return;
9159 }
9160 case 'a': {
9161 parser->current.end++;
9162 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\a', flags));
9163 return;
9164 }
9165 case 'b': {
9166 parser->current.end++;
9167 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\b', flags));
9168 return;
9169 }
9170 case 'e': {
9171 parser->current.end++;
9172 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\033', flags));
9173 return;
9174 }
9175 case 'f': {
9176 parser->current.end++;
9177 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\f', flags));
9178 return;
9179 }
9180 case 'n': {
9181 parser->current.end++;
9182 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\n', flags));
9183 return;
9184 }
9185 case 'r': {
9186 parser->current.end++;
9187 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\r', flags));
9188 return;
9189 }
9190 case 's': {
9191 parser->current.end++;
9192 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(' ', flags));
9193 return;
9194 }
9195 case 't': {
9196 parser->current.end++;
9197 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\t', flags));
9198 return;
9199 }
9200 case 'v': {
9201 parser->current.end++;
9202 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\v', flags));
9203 return;
9204 }
9205 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': {
9206 uint8_t value = (uint8_t) (*parser->current.end - '0');
9207 parser->current.end++;
9208
9209 if (pm_char_is_octal_digit(peek(parser))) {
9210 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9211 parser->current.end++;
9212
9213 if (pm_char_is_octal_digit(peek(parser))) {
9214 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9215 parser->current.end++;
9216 }
9217 }
9218
9219 value = escape_byte(value, flags);
9220 escape_write_byte(parser, buffer, regular_expression_buffer, flags, value);
9221 return;
9222 }
9223 case 'x': {
9224 const uint8_t *start = parser->current.end - 1;
9225
9226 parser->current.end++;
9227 uint8_t byte = peek(parser);
9228
9229 if (pm_char_is_hexadecimal_digit(byte)) {
9230 uint8_t value = escape_hexadecimal_digit(byte);
9231 parser->current.end++;
9232
9233 byte = peek(parser);
9234 if (pm_char_is_hexadecimal_digit(byte)) {
9235 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(byte));
9236 parser->current.end++;
9237 }
9238
9239 value = escape_byte(value, flags);
9240 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9241 if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) {
9242 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", value);
9243 } else {
9244 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9245 }
9246 }
9247
9248 escape_write_byte_encoded(parser, buffer, flags, value);
9249 } else {
9250 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL);
9251 }
9252
9253 return;
9254 }
9255 case 'u': {
9256 const uint8_t *start = parser->current.end - 1;
9257 parser->current.end++;
9258
9259 if (parser->current.end == parser->end) {
9260 const uint8_t *start = parser->current.end - 2;
9261 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9262 } else if (peek(parser) == '{') {
9263 const uint8_t *unicode_codepoints_start = parser->current.end - 2;
9264 parser->current.end++;
9265
9266 size_t whitespace;
9267 while (true) {
9268 if ((whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end)) > 0) {
9269 parser->current.end += whitespace;
9270 } else if (peek(parser) == '\\' && peek_offset(parser, 1) == 'n') {
9271 // This is super hacky, but it gets us nicer error
9272 // messages because we can still pass it off to the
9273 // regular expression engine even if we hit an
9274 // unterminated regular expression.
9275 parser->current.end += 2;
9276 } else {
9277 break;
9278 }
9279 }
9280
9281 const uint8_t *extra_codepoints_start = NULL;
9282 int codepoints_count = 0;
9283
9284 while ((parser->current.end < parser->end) && (*parser->current.end != '}')) {
9285 const uint8_t *unicode_start = parser->current.end;
9286 size_t hexadecimal_length = pm_strspn_hexadecimal_digit(parser->current.end, parser->end - parser->current.end);
9287
9288 if (hexadecimal_length > 6) {
9289 // \u{nnnn} character literal allows only 1-6 hexadecimal digits
9290 pm_parser_err(parser, U32(unicode_start - parser->start), U32(hexadecimal_length), PM_ERR_ESCAPE_INVALID_UNICODE_LONG);
9291 } else if (hexadecimal_length == 0) {
9292 // there are not hexadecimal characters
9293
9294 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9295 // If this is a regular expression, we are going to
9296 // let the regular expression engine handle this
9297 // error instead of us because we don't know at this
9298 // point if we're inside a comment in /x mode.
9299 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9300 } else {
9301 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE);
9302 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9303 }
9304
9305 return;
9306 }
9307
9308 parser->current.end += hexadecimal_length;
9309 codepoints_count++;
9310 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count == 2) {
9311 extra_codepoints_start = unicode_start;
9312 }
9313
9314 uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL, flags);
9315 escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value);
9316
9317 parser->current.end += pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
9318 }
9319
9320 // ?\u{nnnn} character literal should contain only one codepoint
9321 // and cannot be like ?\u{nnnn mmmm}.
9322 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count > 1) {
9323 pm_parser_err(parser, U32(extra_codepoints_start - parser->start), U32(parser->current.end - 1 - extra_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL);
9324 }
9325
9326 if (parser->current.end == parser->end) {
9327 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_LIST, (int) (parser->current.end - start), start);
9328 } else if (peek(parser) == '}') {
9329 parser->current.end++;
9330 } else {
9331 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9332 // If this is a regular expression, we are going to let
9333 // the regular expression engine handle this error
9334 // instead of us because we don't know at this point if
9335 // we're inside a comment in /x mode.
9336 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9337 } else {
9338 pm_parser_err(parser, U32(unicode_codepoints_start - parser->start), U32(parser->current.end - unicode_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9339 }
9340 }
9341
9342 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9343 pm_buffer_append_bytes(regular_expression_buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start));
9344 }
9345 } else {
9346 size_t length = pm_strspn_hexadecimal_digit(parser->current.end, MIN(parser->end - parser->current.end, 4));
9347
9348 if (length == 0) {
9349 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9350 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9351 } else {
9352 const uint8_t *start = parser->current.end - 2;
9353 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9354 }
9355 } else if (length == 4) {
9356 uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL, flags);
9357
9358 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9359 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start));
9360 }
9361
9362 escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value);
9363 parser->current.end += 4;
9364 } else {
9365 parser->current.end += length;
9366
9367 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9368 // If this is a regular expression, we are going to let
9369 // the regular expression engine handle this error
9370 // instead of us.
9371 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9372 } else {
9373 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE);
9374 }
9375 }
9376 }
9377
9378 return;
9379 }
9380 case 'c': {
9381 parser->current.end++;
9382 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9383 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9384 }
9385
9386 if (parser->current.end == parser->end) {
9387 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9388 return;
9389 }
9390
9391 uint8_t peeked = peek(parser);
9392 switch (peeked) {
9393 case '?': {
9394 parser->current.end++;
9395 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9396 return;
9397 }
9398 case '\\':
9399 parser->current.end++;
9400
9401 if (match(parser, 'u') || match(parser, 'U')) {
9402 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9403 return;
9404 }
9405
9406 flags |= PM_ESCAPE_FLAG_CONTROL;
9407 goto escape_read_next;
9408 case ' ':
9409 parser->current.end++;
9410 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9411 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9412 return;
9413 case '\t':
9414 parser->current.end++;
9415 escape_read_warn(parser, flags, 0, "\\t");
9416 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9417 return;
9418 default: {
9419 if (!char_is_ascii_printable(peeked)) {
9420 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9421 return;
9422 }
9423
9424 if (parser->heredoc_end == NULL && peeked == '\n') pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
9425 parser->current.end++;
9426 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9427 return;
9428 }
9429 }
9430 }
9431 case 'C': {
9432 parser->current.end++;
9433 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9434 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9435 }
9436
9437 if (peek(parser) != '-') {
9438 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9439 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9440 return;
9441 }
9442
9443 parser->current.end++;
9444 if (parser->current.end == parser->end) {
9445 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9446 return;
9447 }
9448
9449 uint8_t peeked = peek(parser);
9450 switch (peeked) {
9451 case '?': {
9452 parser->current.end++;
9453 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9454 return;
9455 }
9456 case '\\':
9457 parser->current.end++;
9458
9459 if (match(parser, 'u') || match(parser, 'U')) {
9460 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9461 return;
9462 }
9463
9464 flags |= PM_ESCAPE_FLAG_CONTROL;
9465 goto escape_read_next;
9466 case ' ':
9467 parser->current.end++;
9468 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9469 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9470 return;
9471 case '\t':
9472 parser->current.end++;
9473 escape_read_warn(parser, flags, 0, "\\t");
9474 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9475 return;
9476 default: {
9477 if (!char_is_ascii_printable(peeked)) {
9478 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9479 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9480 return;
9481 }
9482
9483 if (parser->heredoc_end == NULL && peeked == '\n') pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
9484 parser->current.end++;
9485 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9486 return;
9487 }
9488 }
9489 }
9490 case 'M': {
9491 parser->current.end++;
9492 if (flags & PM_ESCAPE_FLAG_META) {
9493 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
9494 }
9495
9496 if (peek(parser) != '-') {
9497 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9498 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9499 return;
9500 }
9501
9502 parser->current.end++;
9503 if (parser->current.end == parser->end) {
9504 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META);
9505 return;
9506 }
9507
9508 uint8_t peeked = peek(parser);
9509 switch (peeked) {
9510 case '\\':
9511 parser->current.end++;
9512
9513 if (match(parser, 'u') || match(parser, 'U')) {
9514 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9515 return;
9516 }
9517
9518 flags |= PM_ESCAPE_FLAG_META;
9519 goto escape_read_next;
9520 case ' ':
9521 parser->current.end++;
9522 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_META, "\\s");
9523 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9524 return;
9525 case '\t':
9526 parser->current.end++;
9527 escape_read_warn(parser, flags & ((uint8_t) ~PM_ESCAPE_FLAG_CONTROL), PM_ESCAPE_FLAG_META, "\\t");
9528 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9529 return;
9530 default:
9531 if (!char_is_ascii_printable(peeked)) {
9532 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9533 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9534 return;
9535 }
9536
9537 if (parser->heredoc_end == NULL && peeked == '\n') pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
9538 parser->current.end++;
9539 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9540 return;
9541 }
9542 }
9543 case '\r': {
9544 if (peek_offset(parser, 1) == '\n') {
9545 if (parser->heredoc_end == NULL) pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 2);
9546 parser->current.end += 2;
9547 escape_write_byte_encoded(parser, buffer, flags, escape_byte('\n', flags));
9548 return;
9549 }
9551 }
9552 default: {
9553 if ((flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) && !char_is_ascii_printable(peeked)) {
9554 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9555 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9556 return;
9557 }
9558 if (parser->current.end < parser->end) {
9559 escape_write_escape_encoded(parser, buffer, regular_expression_buffer, flags);
9560 } else {
9561 pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER);
9562 }
9563 return;
9564 }
9565 }
9566}
9567
9593static pm_token_type_t
9594lex_question_mark(pm_parser_t *parser) {
9595 if (lex_state_end_p(parser)) {
9596 lex_state_set(parser, PM_LEX_STATE_BEG);
9597 return PM_TOKEN_QUESTION_MARK;
9598 }
9599
9600 /*
9601 * A literal takes its encoding from its own contents. Literals that push a
9602 * lex mode clear this in lex_mode_push_*; a character literal is lexed
9603 * inline, so it clears the encoding here.
9604 */
9605 parser->explicit_encoding = NULL;
9606
9607 if (parser->current.end >= parser->end) {
9608 pm_parser_err_current(parser, PM_ERR_INCOMPLETE_QUESTION_MARK);
9609 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9610 return PM_TOKEN_CHARACTER_LITERAL;
9611 }
9612
9613 if (pm_char_is_whitespace(*parser->current.end)) {
9614 lex_state_set(parser, PM_LEX_STATE_BEG);
9615 return PM_TOKEN_QUESTION_MARK;
9616 }
9617
9618 lex_state_set(parser, PM_LEX_STATE_BEG);
9619
9620 if (match(parser, '\\')) {
9621 lex_state_set(parser, PM_LEX_STATE_END);
9622
9623 pm_buffer_t buffer;
9624 pm_buffer_init(&buffer, 3);
9625
9626 escape_read(parser, &buffer, NULL, PM_ESCAPE_FLAG_SINGLE);
9627
9628 // Copy buffer data into the arena and free the heap buffer.
9629 void *arena_data = pm_arena_memdup(parser->arena, buffer.value, buffer.length, PRISM_ALIGNOF(uint8_t));
9630 pm_string_init(&parser->current_string, (const uint8_t *) arena_data, buffer.length);
9631 pm_buffer_cleanup(&buffer);
9632
9633 return PM_TOKEN_CHARACTER_LITERAL;
9634 } else {
9635 size_t encoding_width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9636
9637 // Ternary operators can have a ? immediately followed by an identifier
9638 // which starts with an underscore. We check for this case here.
9639 if (
9640 !(parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end) || peek(parser) == '_') ||
9641 (
9642 (parser->current.end + encoding_width >= parser->end) ||
9643 !char_is_identifier(parser, parser->current.end + encoding_width, parser->end - (parser->current.end + encoding_width))
9644 )
9645 ) {
9646 lex_state_set(parser, PM_LEX_STATE_END);
9647 parser->current.end += encoding_width;
9648 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9649 return PM_TOKEN_CHARACTER_LITERAL;
9650 }
9651 }
9652
9653 return PM_TOKEN_QUESTION_MARK;
9654}
9655
9660static pm_token_type_t
9661lex_at_variable(pm_parser_t *parser) {
9662 pm_token_type_t type = match(parser, '@') ? PM_TOKEN_CLASS_VARIABLE : PM_TOKEN_INSTANCE_VARIABLE;
9663 const uint8_t *end = parser->end;
9664
9665 size_t width;
9666 if ((width = char_is_identifier_start(parser, parser->current.end, end - parser->current.end)) > 0) {
9667 parser->current.end += width;
9668
9669 while ((width = char_is_identifier(parser, parser->current.end, end - parser->current.end)) > 0) {
9670 parser->current.end += width;
9671 }
9672 } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) {
9673 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
9674 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) {
9675 diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3;
9676 }
9677
9678 size_t width = parser->encoding->char_width(parser->current.end, end - parser->current.end);
9679 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start);
9680 } else {
9681 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_CLASS_VARIABLE_BARE : PM_ERR_INSTANCE_VARIABLE_BARE;
9682 pm_parser_err_token(parser, &parser->current, diag_id);
9683 }
9684
9685 // If we're lexing an embedded variable, then we need to pop back into the
9686 // parent lex context.
9687 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
9688 lex_mode_pop(parser);
9689 }
9690
9691 return type;
9692}
9693
9697static PRISM_INLINE void
9698parser_lex_callback(pm_parser_t *parser) {
9699 if (parser->lex_callback.callback) {
9700 parser->lex_callback.callback(parser, &parser->current, parser->lex_callback.data);
9701 }
9702}
9703
9708parser_comment(pm_parser_t *parser, pm_comment_type_t type) {
9709 pm_comment_t *comment = (pm_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_comment_t), PRISM_ALIGNOF(pm_comment_t));
9710
9711 *comment = (pm_comment_t) {
9712 .type = type,
9713 .location = TOK2LOC(parser, &parser->current)
9714 };
9715
9716 return comment;
9717}
9718
9724static pm_token_type_t
9725lex_embdoc(pm_parser_t *parser) {
9726 // First, lex out the EMBDOC_BEGIN token.
9727 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9728
9729 if (newline == NULL) {
9730 parser->current.end = parser->end;
9731 } else {
9732 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9733 parser->current.end = newline + 1;
9734 }
9735
9736 parser->current.type = PM_TOKEN_EMBDOC_BEGIN;
9737 parser_lex_callback(parser);
9738
9739 // Now, create a comment that is going to be attached to the parser.
9740 const uint8_t *comment_start = parser->current.start;
9741 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC);
9742
9743 // Now, loop until we find the end of the embedded documentation or the end
9744 // of the file.
9745 while (parser->current.end + 4 <= parser->end) {
9746 parser->current.start = parser->current.end;
9747
9748 // If we've hit the end of the embedded documentation then we'll return
9749 // that token here.
9750 if (
9751 (memcmp(parser->current.end, "=end", 4) == 0) &&
9752 (
9753 (parser->current.end + 4 == parser->end) || // end of file
9754 pm_char_is_whitespace(parser->current.end[4]) || // whitespace
9755 (parser->current.end[4] == '\0') || // NUL or end of script
9756 (parser->current.end[4] == '\004') || // ^D
9757 (parser->current.end[4] == '\032') // ^Z
9758 )
9759 ) {
9760 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9761
9762 if (newline == NULL) {
9763 parser->current.end = parser->end;
9764 } else {
9765 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9766 parser->current.end = newline + 1;
9767 }
9768
9769 parser->current.type = PM_TOKEN_EMBDOC_END;
9770 parser_lex_callback(parser);
9771
9772 comment->location.length = (uint32_t) (parser->current.end - comment_start);
9773 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9774
9775 return PM_TOKEN_EMBDOC_END;
9776 }
9777
9778 // Otherwise, we'll parse until the end of the line and return a line of
9779 // embedded documentation.
9780 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9781
9782 if (newline == NULL) {
9783 parser->current.end = parser->end;
9784 } else {
9785 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9786 parser->current.end = newline + 1;
9787 }
9788
9789 parser->current.type = PM_TOKEN_EMBDOC_LINE;
9790 parser_lex_callback(parser);
9791 }
9792
9793 pm_parser_err_current(parser, PM_ERR_EMBDOC_TERM);
9794
9795 comment->location.length = (uint32_t) (parser->current.end - comment_start);
9796 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9797
9798 return PM_TOKEN_EOF;
9799}
9800
9806static PRISM_INLINE void
9807parser_lex_ignored_newline(pm_parser_t *parser) {
9808 parser->current.type = PM_TOKEN_IGNORED_NEWLINE;
9809 parser_lex_callback(parser);
9810}
9811
9821static PRISM_INLINE void
9822parser_flush_heredoc_end(pm_parser_t *parser) {
9823 assert(parser->heredoc_end <= parser->end);
9824 parser->next_start = parser->heredoc_end;
9825 parser->heredoc_end = NULL;
9826}
9827
9831static bool
9832parser_end_of_line_p(const pm_parser_t *parser) {
9833 const uint8_t *cursor = parser->current.end;
9834
9835 while (cursor < parser->end && *cursor != '\n' && *cursor != '#') {
9836 if (!pm_char_is_inline_whitespace(*cursor++)) return false;
9837 }
9838
9839 return true;
9840}
9841
9860typedef struct {
9866
9871 const uint8_t *cursor;
9873
9893
9897static PRISM_INLINE void
9898pm_token_buffer_push_byte(pm_token_buffer_t *token_buffer, uint8_t byte) {
9899 pm_buffer_append_byte(&token_buffer->buffer, byte);
9900}
9901
9902static PRISM_INLINE void
9903pm_regexp_token_buffer_push_byte(pm_regexp_token_buffer_t *token_buffer, uint8_t byte) {
9904 pm_buffer_append_byte(&token_buffer->regexp_buffer, byte);
9905}
9906
9910static PRISM_INLINE size_t
9911parser_char_width(const pm_parser_t *parser) {
9912 size_t width;
9913 if (parser->encoding_changed) {
9914 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9915 } else {
9916 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9917 }
9918
9919 // TODO: If the character is invalid in the given encoding, then we'll just
9920 // push one byte into the buffer. This should actually be an error.
9921 return (width == 0 ? 1 : width);
9922}
9923
9927static void
9928pm_token_buffer_push_escaped(pm_token_buffer_t *token_buffer, pm_parser_t *parser) {
9929 size_t width = parser_char_width(parser);
9930 pm_buffer_append_bytes(&token_buffer->buffer, parser->current.end, width);
9931 parser->current.end += width;
9932}
9933
9934static void
9935pm_regexp_token_buffer_push_escaped(pm_regexp_token_buffer_t *token_buffer, pm_parser_t *parser) {
9936 size_t width = parser_char_width(parser);
9937 const uint8_t *start = parser->current.end;
9938 pm_buffer_append_bytes(&token_buffer->base.buffer, start, width);
9939 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, width);
9940 parser->current.end += width;
9941}
9942
9949static PRISM_INLINE void
9950pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9951 // Copy buffer data into the arena and free the heap buffer.
9952 size_t len = pm_buffer_length(&token_buffer->buffer);
9953 void *arena_data = pm_arena_memdup(parser->arena, pm_buffer_value(&token_buffer->buffer), len, PRISM_ALIGNOF(uint8_t));
9954 pm_string_init(&parser->current_string, (const uint8_t *) arena_data, len);
9955 pm_buffer_cleanup(&token_buffer->buffer);
9956}
9957
9958static PRISM_INLINE void
9959pm_regexp_token_buffer_copy(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9960 pm_token_buffer_copy(parser, &token_buffer->base);
9961 pm_buffer_cleanup(&token_buffer->regexp_buffer);
9962}
9963
9973static void
9974pm_token_buffer_flush(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9975 if (token_buffer->cursor == NULL) {
9976 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9977 } else {
9978 pm_buffer_append_bytes(&token_buffer->buffer, token_buffer->cursor, (size_t) (parser->current.end - token_buffer->cursor));
9979 pm_token_buffer_copy(parser, token_buffer);
9980 }
9981}
9982
9983static void
9984pm_regexp_token_buffer_flush(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9985 if (token_buffer->base.cursor == NULL) {
9986 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9987 } else {
9988 const uint8_t *cursor = token_buffer->base.cursor;
9989 size_t length = (size_t) (parser->current.end - cursor);
9990 pm_buffer_append_bytes(&token_buffer->base.buffer, cursor, length);
9991 pm_buffer_append_bytes(&token_buffer->regexp_buffer, cursor, length);
9992 pm_regexp_token_buffer_copy(parser, token_buffer);
9993 }
9994}
9995
9996#define PM_TOKEN_BUFFER_DEFAULT_SIZE 16
9997
10006static void
10007pm_token_buffer_escape(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
10008 const uint8_t *start;
10009 if (token_buffer->cursor == NULL) {
10010 pm_buffer_init(&token_buffer->buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
10011 start = parser->current.start;
10012 } else {
10013 start = token_buffer->cursor;
10014 }
10015
10016 const uint8_t *end = parser->current.end - 1;
10017 assert(end >= start);
10018 pm_buffer_append_bytes(&token_buffer->buffer, start, (size_t) (end - start));
10019
10020 token_buffer->cursor = end;
10021}
10022
10023static void
10024pm_regexp_token_buffer_escape(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
10025 const uint8_t *start;
10026 if (token_buffer->base.cursor == NULL) {
10027 pm_buffer_init(&token_buffer->base.buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
10028 pm_buffer_init(&token_buffer->regexp_buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
10029 start = parser->current.start;
10030 } else {
10031 start = token_buffer->base.cursor;
10032 }
10033
10034 const uint8_t *end = parser->current.end - 1;
10035 pm_buffer_append_bytes(&token_buffer->base.buffer, start, (size_t) (end - start));
10036 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, (size_t) (end - start));
10037
10038 token_buffer->base.cursor = end;
10039}
10040
10041#undef PM_TOKEN_BUFFER_DEFAULT_SIZE
10042
10047static PRISM_INLINE size_t
10048pm_heredoc_strspn_inline_whitespace(pm_parser_t *parser, const uint8_t **cursor, pm_heredoc_indent_t indent) {
10049 size_t whitespace = 0;
10050
10051 switch (indent) {
10052 case PM_HEREDOC_INDENT_NONE:
10053 // Do nothing, we can't match a terminator with
10054 // indentation and there's no need to calculate common
10055 // whitespace.
10056 break;
10057 case PM_HEREDOC_INDENT_DASH:
10058 // Skip past inline whitespace.
10059 *cursor += pm_strspn_inline_whitespace(*cursor, parser->end - *cursor);
10060 break;
10061 case PM_HEREDOC_INDENT_TILDE:
10062 // Skip past inline whitespace and calculate common
10063 // whitespace.
10064 while (*cursor < parser->end && pm_char_is_inline_whitespace(**cursor)) {
10065 if (**cursor == '\t') {
10066 whitespace = (whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
10067 } else {
10068 whitespace++;
10069 }
10070 (*cursor)++;
10071 }
10072
10073 break;
10074 }
10075
10076 return whitespace;
10077}
10078
10083static uint8_t
10084pm_lex_percent_delimiter(pm_parser_t *parser) {
10085 size_t eol_length = match_eol(parser);
10086
10087 if (eol_length) {
10088 if (parser->heredoc_end) {
10089 // If we have already lexed a heredoc, then the newline has already
10090 // been added to the list. In this case we want to just flush the
10091 // heredoc end.
10092 parser_flush_heredoc_end(parser);
10093 } else {
10094 // Otherwise, we'll add the newline to the list of newlines.
10095 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + U32(eol_length));
10096 }
10097
10098 uint8_t delimiter = *parser->current.end;
10099
10100 // If our delimiter is \r\n, we want to treat it as if it's \n.
10101 // For example, %\r\nfoo\r\n should be "foo"
10102 if (eol_length == 2) {
10103 delimiter = *(parser->current.end + 1);
10104 }
10105
10106 parser->current.end += eol_length;
10107 return delimiter;
10108 }
10109
10110 return *parser->current.end++;
10111}
10112
10117#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return
10118
10125static void
10126parser_lex(pm_parser_t *parser) {
10127 assert(parser->current.end <= parser->end);
10128 parser->previous = parser->current;
10129
10130 // This value mirrors cmd_state from CRuby.
10131 bool previous_command_start = parser->command_start;
10132 parser->command_start = false;
10133
10134 // This is used to communicate to the newline lexing function that we've
10135 // already seen a comment.
10136 bool lexed_comment = false;
10137
10138 // Here we cache the current value of the semantic token seen flag. This is
10139 // used to reset it in case we find a token that shouldn't flip this flag.
10140 unsigned int semantic_token_seen = parser->semantic_token_seen;
10141 parser->semantic_token_seen = true;
10142
10143 // We'll jump to this label when we are about to encounter an EOF.
10144 // If we still have lex_modes on the stack, we pop them so that cleanup
10145 // can happen. For example, we should still continue parsing after a heredoc
10146 // identifier, even if the heredoc body was syntax invalid.
10147 switch_lex_modes:
10148
10149 switch (parser->lex_modes.current->mode) {
10150 case PM_LEX_DEFAULT:
10151 case PM_LEX_EMBEXPR:
10152 case PM_LEX_EMBVAR:
10153
10154 // We have a specific named label here because we are going to jump back to
10155 // this location in the event that we have lexed a token that should not be
10156 // returned to the parser. This includes comments, ignored newlines, and
10157 // invalid tokens of some form.
10158 lex_next_token: {
10159 // If we have the special next_start pointer set, then we're going to jump
10160 // to that location and start lexing from there.
10161 if (parser->next_start != NULL) {
10162 parser->current.end = parser->next_start;
10163 parser->next_start = NULL;
10164 }
10165
10166 // This value mirrors space_seen from CRuby. It tracks whether or not
10167 // space has been eaten before the start of the next token.
10168 bool space_seen = false;
10169
10170 // First, we're going to skip past any whitespace at the front of the next
10171 // token. Skip runs of inline whitespace in bulk to avoid per-character
10172 // stores back to parser->current.end.
10173 bool chomping = true;
10174 while (chomping) {
10175 /* Skip the run of inline whitespace in bulk, then decide what
10176 * to do based on the first byte after it. Handling both in a
10177 * single pass avoids re-entering the scan when the run was
10178 * non-empty, which is the common case. */
10179 {
10180 static const uint8_t inline_whitespace[256] = {
10181 [' '] = 1, ['\t'] = 1, ['\f'] = 1, ['\v'] = 1
10182 };
10183 const uint8_t *scan = parser->current.end;
10184 while (scan < parser->end && inline_whitespace[*scan]) scan++;
10185 if (scan > parser->current.end) {
10186 parser->current.end = scan;
10187 space_seen = true;
10188 }
10189 if (scan >= parser->end) break;
10190 }
10191
10192 switch (*parser->current.end) {
10193 case '\r':
10194 if (match_eol_offset(parser, 1)) {
10195 chomping = false;
10196 } else {
10197 pm_parser_warn(parser, PM_TOKEN_END(parser, &parser->current), 1, PM_WARN_UNEXPECTED_CARRIAGE_RETURN);
10198 parser->current.end++;
10199 space_seen = true;
10200 }
10201 break;
10202 case '\\': {
10203 size_t eol_length = match_eol_offset(parser, 1);
10204 if (eol_length) {
10205 if (parser->heredoc_end) {
10206 parser->current.end = parser->heredoc_end;
10207 parser->heredoc_end = NULL;
10208 } else {
10209 parser->current.end += eol_length + 1;
10210 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10211 space_seen = true;
10212 }
10213 } else if (pm_char_is_inline_whitespace(*parser->current.end)) {
10214 parser->current.end += 2;
10215 } else {
10216 chomping = false;
10217 }
10218
10219 break;
10220 }
10221 default:
10222 chomping = false;
10223 break;
10224 }
10225 }
10226
10227 // Next, we'll set to start of this token to be the current end.
10228 parser->current.start = parser->current.end;
10229
10230 // We'll check if we're at the end of the file. If we are, then we
10231 // need to return the EOF token.
10232 if (parser->current.end >= parser->end) {
10233 // We may be missing closing tokens. We should pop modes one by one
10234 // to do the appropriate cleanup like moving next_start for heredocs.
10235 // Only when no mode is remaining will we actually emit the EOF token.
10236 if (parser->lex_modes.current->mode != PM_LEX_DEFAULT) {
10237 lex_mode_pop(parser);
10238 goto switch_lex_modes;
10239 }
10240
10241 // If we hit EOF, but the EOF came immediately after a newline,
10242 // set the start of the token to the newline. This way any EOF
10243 // errors will be reported as happening on that line rather than
10244 // a line after. For example "foo(\n" should report an error
10245 // on line 1 even though EOF technically occurs on line 2.
10246 if (parser->current.start > parser->start && (*(parser->current.start - 1) == '\n')) {
10247 parser->current.start -= 1;
10248 }
10249 LEX(PM_TOKEN_EOF);
10250 }
10251
10252 // Finally, we'll check the current character to determine the next
10253 // token.
10254 switch (*parser->current.end++) {
10255 case '\0': // NUL or end of script
10256 case '\004': // ^D
10257 case '\032': // ^Z
10258 parser->current.end--;
10259 LEX(PM_TOKEN_EOF);
10260
10261 case '#': { // comments
10262 const uint8_t *ending = next_newline(parser->current.end, parser->end - parser->current.end);
10263 parser->current.end = ending == NULL ? parser->end : ending;
10264
10265 // If we found a comment while lexing, then we're going to
10266 // add it to the list of comments in the file and keep
10267 // lexing.
10268 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE);
10269 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
10270
10271 parser->current.type = PM_TOKEN_COMMENT;
10272 parser_lex_callback(parser);
10273
10274 // Here, parse the comment to see if it's a magic comment
10275 // and potentially change state on the parser.
10276 if (!parser_lex_magic_comment(parser, semantic_token_seen) && (parser->current.start == parser->encoding_comment_start)) {
10277 ptrdiff_t length = parser->current.end - parser->current.start;
10278
10279 // If we didn't find a magic comment within the first
10280 // pass and we're at the start of the file, then we need
10281 // to do another pass to potentially find other patterns
10282 // for encoding comments.
10283 if (length >= 10 && !parser->encoding_locked) {
10284 parser_lex_magic_comment_encoding(parser);
10285 }
10286 }
10287
10288 /* The comment does not include its terminating newline,
10289 * which lexes through the newline handling below as its
10290 * own token. A comment that ends the file has no newline,
10291 * so the newline handling runs without one to emit. */
10292 if (ending == NULL) {
10293 lexed_comment = true;
10294 } else {
10295 parser->current.start = ending;
10296 parser->current.end = ending + 1;
10297 }
10298 }
10300 case '\r':
10301 case '\n': {
10302 parser->semantic_token_seen = semantic_token_seen & 0x1;
10303 size_t eol_length = match_eol_at(parser, parser->current.end - 1);
10304
10305 if (eol_length) {
10306 // The only way you can have carriage returns in this
10307 // particular loop is if you have a carriage return
10308 // followed by a newline. In that case we'll just skip
10309 // over the carriage return and continue lexing, in
10310 // order to make it so that the newline token
10311 // encapsulates both the carriage return and the
10312 // newline. Note that we need to check that we haven't
10313 // already lexed a comment here because that falls
10314 // through into here as well.
10315 if (!lexed_comment) {
10316 parser->current.end += eol_length - 1; // skip CR
10317 }
10318
10319 if (parser->heredoc_end == NULL) {
10320 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10321 }
10322 }
10323
10324 if (parser->heredoc_end) {
10325 parser_flush_heredoc_end(parser);
10326 }
10327
10328 // If this is an ignored newline, then we can continue lexing after
10329 // calling the callback with the ignored newline token.
10330 switch (lex_state_ignored_p(parser)) {
10331 case PM_IGNORED_NEWLINE_NONE:
10332 break;
10333 case PM_IGNORED_NEWLINE_PATTERN:
10334 if (parser->pattern_matching_newlines || parser->in_keyword_arg) {
10335 if (!lexed_comment) {
10336 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10337 parser_lex_callback(parser);
10338 }
10339
10340 lex_state_set(parser, PM_LEX_STATE_BEG);
10341 parser->command_start = true;
10342 parser->current.type = PM_TOKEN_NEWLINE;
10343 return;
10344 }
10346 case PM_IGNORED_NEWLINE_ALL:
10347 if (!lexed_comment) parser_lex_ignored_newline(parser);
10348 lexed_comment = false;
10349 goto lex_next_token;
10350 }
10351
10352 // Here we need to look ahead and see if there is a call operator
10353 // (either . or &.) that starts the next line. If there is, then this
10354 // is going to become an ignored newline and we're going to instead
10355 // return the call operator.
10356 const uint8_t *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start;
10357 next_content += pm_strspn_inline_whitespace(next_content, parser->end - next_content);
10358
10359 if (next_content < parser->end) {
10360 // If we hit a comment after a newline, then we're going to check
10361 // if it's ignored or if it's followed by a method call ('.').
10362 // If it is, then we're going to call the
10363 // callback with an ignored newline and then continue lexing.
10364 // Otherwise we'll return a regular newline.
10365 if (next_content[0] == '#') {
10366 // Here we look for a "." or "&." following a "\n".
10367 const uint8_t *following = next_newline(next_content, parser->end - next_content);
10368
10369 while (following && (following + 1 < parser->end)) {
10370 following++;
10371 following += pm_strspn_inline_whitespace(following, parser->end - following);
10372
10373 // If this is not followed by a comment, then we can break out
10374 // of this loop.
10375 if (peek_at(parser, following) != '#') break;
10376
10377 // If there is a comment, then we need to find the end of the
10378 // comment and continue searching from there.
10379 following = next_newline(following, parser->end - following);
10380 }
10381
10382 // If the lex state was ignored, we will lex the
10383 // ignored newline.
10384 if (lex_state_ignored_p(parser)) {
10385 if (!lexed_comment) parser_lex_ignored_newline(parser);
10386 lexed_comment = false;
10387 goto lex_next_token;
10388 }
10389
10390 // If we hit a '.' or a '&.' we will lex the ignored
10391 // newline.
10392 if (following && (
10393 (peek_at(parser, following) == '.') ||
10394 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10395 )) {
10396 if (!lexed_comment) parser_lex_ignored_newline(parser);
10397 lexed_comment = false;
10398 goto lex_next_token;
10399 }
10400
10401
10402 // If we are parsing as CRuby 4.0 or later and we
10403 // hit a '&&' or a '||' then we will lex the ignored
10404 // newline.
10405 if (
10406 (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) &&
10407 following && (
10408 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
10409 (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
10410 (
10411 peek_at(parser, following) == 'a' &&
10412 peek_at(parser, following + 1) == 'n' &&
10413 peek_at(parser, following + 2) == 'd' &&
10414 peek_at(parser, next_content + 3) != '!' &&
10415 peek_at(parser, next_content + 3) != '?' &&
10416 !char_is_identifier(parser, following + 3, parser->end - (following + 3))
10417 ) ||
10418 (
10419 peek_at(parser, following) == 'o' &&
10420 peek_at(parser, following + 1) == 'r' &&
10421 peek_at(parser, next_content + 2) != '!' &&
10422 peek_at(parser, next_content + 2) != '?' &&
10423 !char_is_identifier(parser, following + 2, parser->end - (following + 2))
10424 )
10425 )
10426 ) {
10427 if (!lexed_comment) parser_lex_ignored_newline(parser);
10428 lexed_comment = false;
10429 goto lex_next_token;
10430 }
10431 }
10432
10433 // If we hit a . after a newline, then we're in a call chain and
10434 // we need to return the call operator.
10435 if (next_content[0] == '.') {
10436 /* A beginless range on the next line means this
10437 * newline terminates the statement rather than
10438 * continuing a method chain. */
10439 if (peek_at(parser, next_content + 1) == '.') {
10440 if (!lexed_comment) {
10441 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10442 parser_lex_callback(parser);
10443 }
10444
10445 lex_state_set(parser, PM_LEX_STATE_BEG);
10446 parser->command_start = true;
10447 parser->current.type = PM_TOKEN_NEWLINE;
10448 return;
10449 }
10450
10451 if (!lexed_comment) parser_lex_ignored_newline(parser);
10452 lex_state_set(parser, PM_LEX_STATE_DOT);
10453 parser->current.start = next_content;
10454 parser->current.end = next_content + 1;
10455 parser->next_start = NULL;
10456 LEX(PM_TOKEN_DOT);
10457 }
10458
10459 // If we hit a &. after a newline, then we're in a call chain and
10460 // we need to return the call operator.
10461 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '.') {
10462 if (!lexed_comment) parser_lex_ignored_newline(parser);
10463 lex_state_set(parser, PM_LEX_STATE_DOT);
10464 parser->current.start = next_content;
10465 parser->current.end = next_content + 2;
10466 parser->next_start = NULL;
10467 LEX(PM_TOKEN_AMPERSAND_DOT);
10468 }
10469
10470 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
10471 // If we hit an && then we are in a logical chain
10472 // and we need to return the logical operator.
10473 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
10474 if (!lexed_comment) parser_lex_ignored_newline(parser);
10475 lex_state_set(parser, PM_LEX_STATE_BEG);
10476 parser->current.start = next_content;
10477 parser->current.end = next_content + 2;
10478 parser->next_start = NULL;
10479 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10480 }
10481
10482 // If we hit a || then we are in a logical chain and
10483 // we need to return the logical operator.
10484 if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
10485 if (!lexed_comment) parser_lex_ignored_newline(parser);
10486 lex_state_set(parser, PM_LEX_STATE_BEG);
10487 parser->current.start = next_content;
10488 parser->current.end = next_content + 2;
10489 parser->next_start = NULL;
10490 LEX(PM_TOKEN_PIPE_PIPE);
10491 }
10492
10493 // If we hit an 'and' then we are in a logical chain
10494 // and we need to return the logical operator.
10495 if (
10496 peek_at(parser, next_content) == 'a' &&
10497 peek_at(parser, next_content + 1) == 'n' &&
10498 peek_at(parser, next_content + 2) == 'd' &&
10499 peek_at(parser, next_content + 3) != '!' &&
10500 peek_at(parser, next_content + 3) != '?' &&
10501 !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
10502 ) {
10503 if (!lexed_comment) parser_lex_ignored_newline(parser);
10504 lex_state_set(parser, PM_LEX_STATE_BEG);
10505 parser->current.start = next_content;
10506 parser->current.end = next_content + 3;
10507 parser->next_start = NULL;
10508 parser->command_start = true;
10509 LEX(PM_TOKEN_KEYWORD_AND);
10510 }
10511
10512 // If we hit a 'or' then we are in a logical chain
10513 // and we need to return the logical operator.
10514 if (
10515 peek_at(parser, next_content) == 'o' &&
10516 peek_at(parser, next_content + 1) == 'r' &&
10517 peek_at(parser, next_content + 2) != '!' &&
10518 peek_at(parser, next_content + 2) != '?' &&
10519 !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
10520 ) {
10521 if (!lexed_comment) parser_lex_ignored_newline(parser);
10522 lex_state_set(parser, PM_LEX_STATE_BEG);
10523 parser->current.start = next_content;
10524 parser->current.end = next_content + 2;
10525 parser->next_start = NULL;
10526 parser->command_start = true;
10527 LEX(PM_TOKEN_KEYWORD_OR);
10528 }
10529 }
10530 }
10531
10532 // At this point we know this is a regular newline, and we can set the
10533 // necessary state and return the token.
10534 lex_state_set(parser, PM_LEX_STATE_BEG);
10535 parser->command_start = true;
10536 parser->current.type = PM_TOKEN_NEWLINE;
10537 if (!lexed_comment) parser_lex_callback(parser);
10538 return;
10539 }
10540
10541 // ,
10542 case ',':
10543 if ((parser->previous.type == PM_TOKEN_COMMA) && (parser->enclosure_nesting > 0)) {
10544 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
10545 }
10546
10547 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10548 LEX(PM_TOKEN_COMMA);
10549
10550 // (
10551 case '(': {
10552 /* A parenthesis scanned at the beginning of an expression
10553 * groups the expression it wraps, while one scanned in
10554 * argument position with a preceding space wraps a command
10555 * argument. Everything else opens an argument list. */
10556 pm_token_type_t type = PM_TOKEN_PARENTHESIS_LEFT;
10557
10558 if (lex_state_beg_p(parser)) {
10559 type = PM_TOKEN_PARENTHESIS_LEFT_GROUPING;
10560 } else if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (PM_LEX_STATE_END | PM_LEX_STATE_LABEL))) {
10561 type = PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES;
10562 }
10563
10564 parser->enclosure_nesting++;
10565 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10566 pm_enclosure_frame_push(parser);
10567 LEX(type);
10568 }
10569
10570 // )
10571 case ')':
10572 parser->enclosure_nesting--;
10573 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10574 pm_enclosure_frame_pop(parser);
10575 LEX(PM_TOKEN_PARENTHESIS_RIGHT);
10576
10577 // ;
10578 case ';':
10579 lex_state_set(parser, PM_LEX_STATE_BEG);
10580 parser->command_start = true;
10581 LEX(PM_TOKEN_SEMICOLON);
10582
10583 // [ [] []=
10584 case '[':
10585 parser->enclosure_nesting++;
10586 pm_token_type_t type = PM_TOKEN_BRACKET_LEFT;
10587
10588 if (lex_state_operator_p(parser)) {
10589 if (match(parser, ']')) {
10590 parser->enclosure_nesting--;
10591 lex_state_set(parser, PM_LEX_STATE_ARG);
10592 LEX(match(parser, '=') ? PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : PM_TOKEN_BRACKET_LEFT_RIGHT);
10593 }
10594
10595 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABEL);
10596 LEX(type);
10597 }
10598
10599 if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, PM_LEX_STATE_LABELED)))) {
10600 type = PM_TOKEN_BRACKET_LEFT_ARRAY;
10601 }
10602
10603 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10604 pm_enclosure_frame_push(parser);
10605 LEX(type);
10606
10607 // ]
10608 case ']':
10609 parser->enclosure_nesting--;
10610 lex_state_set(parser, PM_LEX_STATE_END);
10611 pm_enclosure_frame_pop(parser);
10612 LEX(PM_TOKEN_BRACKET_RIGHT);
10613
10614 // {
10615 case '{': {
10616 pm_token_type_t type = PM_TOKEN_BRACE_LEFT;
10617
10618 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
10619 /* This { begins a lambda */
10620 parser->command_start = true;
10621 lex_state_set(parser, PM_LEX_STATE_BEG);
10622 type = PM_TOKEN_LAMBDA_BEGIN;
10623 } else if (lex_state_p(parser, PM_LEX_STATE_LABELED)) {
10624 /* This { begins a hash literal */
10625 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10626 type = PM_TOKEN_BRACE_LEFT_HASH;
10627 } else if (lex_state_p(parser, PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_END | PM_LEX_STATE_ENDFN)) {
10628 /* This { begins a block */
10629 parser->command_start = true;
10630 lex_state_set(parser, PM_LEX_STATE_BEG);
10631 } else if (lex_state_p(parser, PM_LEX_STATE_ENDARG)) {
10632 /* This { begins a block following a parenthesized
10633 * command argument */
10634 parser->command_start = true;
10635 lex_state_set(parser, PM_LEX_STATE_BEG);
10636 type = PM_TOKEN_BRACE_LEFT_ARGUMENT;
10637 } else {
10638 /* This { begins a hash literal */
10639 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10640 type = PM_TOKEN_BRACE_LEFT_HASH;
10641 }
10642
10643 parser->enclosure_nesting++;
10644 parser->brace_nesting++;
10645 pm_enclosure_frame_push(parser);
10646
10647 LEX(type);
10648 }
10649
10650 // }
10651 case '}':
10652 parser->enclosure_nesting--;
10653 pm_enclosure_frame_pop(parser);
10654
10655 if ((parser->lex_modes.current->mode == PM_LEX_EMBEXPR) && (parser->brace_nesting == 0)) {
10656 lex_mode_pop(parser);
10657 LEX(PM_TOKEN_EMBEXPR_END);
10658 }
10659
10660 parser->brace_nesting--;
10661 lex_state_set(parser, PM_LEX_STATE_END);
10662 LEX(PM_TOKEN_BRACE_RIGHT);
10663
10664 // * ** **= *=
10665 case '*': {
10666 if (match(parser, '*')) {
10667 if (match(parser, '=')) {
10668 lex_state_set(parser, PM_LEX_STATE_BEG);
10669 LEX(PM_TOKEN_STAR_STAR_EQUAL);
10670 }
10671
10672 pm_token_type_t type = PM_TOKEN_STAR_STAR;
10673
10674 if (lex_state_spcarg_p(parser, space_seen)) {
10675 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR);
10676 type = PM_TOKEN_USTAR_STAR;
10677 } else if (lex_state_beg_p(parser)) {
10678 type = PM_TOKEN_USTAR_STAR;
10679 } else if (ambiguous_operator_p(parser, space_seen)) {
10680 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "**", "argument prefix");
10681 }
10682
10683 if (lex_state_operator_p(parser)) {
10684 lex_state_set(parser, PM_LEX_STATE_ARG);
10685 } else {
10686 lex_state_set(parser, PM_LEX_STATE_BEG);
10687 }
10688
10689 LEX(type);
10690 }
10691
10692 if (match(parser, '=')) {
10693 lex_state_set(parser, PM_LEX_STATE_BEG);
10694 LEX(PM_TOKEN_STAR_EQUAL);
10695 }
10696
10697 pm_token_type_t type = PM_TOKEN_STAR;
10698
10699 if (lex_state_spcarg_p(parser, space_seen)) {
10700 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR);
10701 type = PM_TOKEN_USTAR;
10702 } else if (lex_state_beg_p(parser)) {
10703 type = PM_TOKEN_USTAR;
10704 } else if (ambiguous_operator_p(parser, space_seen)) {
10705 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "*", "argument prefix");
10706 }
10707
10708 if (lex_state_operator_p(parser)) {
10709 lex_state_set(parser, PM_LEX_STATE_ARG);
10710 } else {
10711 lex_state_set(parser, PM_LEX_STATE_BEG);
10712 }
10713
10714 LEX(type);
10715 }
10716
10717 // ! != !~ !@
10718 case '!':
10719 if (lex_state_operator_p(parser)) {
10720 lex_state_set(parser, PM_LEX_STATE_ARG);
10721 if (match(parser, '@')) {
10722 LEX(PM_TOKEN_BANG);
10723 }
10724 } else {
10725 lex_state_set(parser, PM_LEX_STATE_BEG);
10726 }
10727
10728 if (match(parser, '=')) {
10729 LEX(PM_TOKEN_BANG_EQUAL);
10730 }
10731
10732 if (match(parser, '~')) {
10733 LEX(PM_TOKEN_BANG_TILDE);
10734 }
10735
10736 LEX(PM_TOKEN_BANG);
10737
10738 // = => =~ == === =begin
10739 case '=':
10740 if (
10741 current_token_starts_line(parser) &&
10742 (parser->current.end + 5 <= parser->end) &&
10743 memcmp(parser->current.end, "begin", 5) == 0 &&
10744 (pm_char_is_whitespace(peek_offset(parser, 5)) || (peek_offset(parser, 5) == '\0'))
10745 ) {
10746 pm_token_type_t type = lex_embdoc(parser);
10747 if (type == PM_TOKEN_EOF) {
10748 LEX(type);
10749 }
10750
10751 goto lex_next_token;
10752 }
10753
10754 if (lex_state_operator_p(parser)) {
10755 lex_state_set(parser, PM_LEX_STATE_ARG);
10756 } else {
10757 lex_state_set(parser, PM_LEX_STATE_BEG);
10758 }
10759
10760 if (match(parser, '>')) {
10761 LEX(PM_TOKEN_EQUAL_GREATER);
10762 }
10763
10764 if (match(parser, '~')) {
10765 LEX(PM_TOKEN_EQUAL_TILDE);
10766 }
10767
10768 if (match(parser, '=')) {
10769 LEX(match(parser, '=') ? PM_TOKEN_EQUAL_EQUAL_EQUAL : PM_TOKEN_EQUAL_EQUAL);
10770 }
10771
10772 LEX(PM_TOKEN_EQUAL);
10773
10774 // < << <<= <= <=>
10775 case '<':
10776 if (match(parser, '<')) {
10777 if (
10778 !lex_state_p(parser, PM_LEX_STATE_DOT | PM_LEX_STATE_CLASS) &&
10779 !lex_state_end_p(parser) &&
10780 (!lex_state_p(parser, PM_LEX_STATE_ARG_ANY) || lex_state_p(parser, PM_LEX_STATE_LABELED) || space_seen)
10781 ) {
10782 const uint8_t *end = parser->current.end;
10783
10784 pm_heredoc_quote_t quote = PM_HEREDOC_QUOTE_NONE;
10785 pm_heredoc_indent_t indent = PM_HEREDOC_INDENT_NONE;
10786
10787 if (match(parser, '-')) {
10788 indent = PM_HEREDOC_INDENT_DASH;
10789 }
10790 else if (match(parser, '~')) {
10791 indent = PM_HEREDOC_INDENT_TILDE;
10792 }
10793
10794 if (match(parser, '`')) {
10795 quote = PM_HEREDOC_QUOTE_BACKTICK;
10796 }
10797 else if (match(parser, '"')) {
10798 quote = PM_HEREDOC_QUOTE_DOUBLE;
10799 }
10800 else if (match(parser, '\'')) {
10801 quote = PM_HEREDOC_QUOTE_SINGLE;
10802 }
10803
10804 const uint8_t *ident_start = parser->current.end;
10805 size_t width = 0;
10806
10807 if (parser->current.end >= parser->end) {
10808 parser->current.end = end;
10809 } else if (quote == PM_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) == 0) {
10810 parser->current.end = end;
10811 } else {
10812 if (quote == PM_HEREDOC_QUOTE_NONE) {
10813 parser->current.end += width;
10814
10815 while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end))) {
10816 parser->current.end += width;
10817 }
10818 } else {
10819 // If we have quotes, then we're going to go until we find the
10820 // end quote.
10821 while ((parser->current.end < parser->end) && quote != (pm_heredoc_quote_t) (*parser->current.end)) {
10822 if (*parser->current.end == '\r' || *parser->current.end == '\n') break;
10823 parser->current.end++;
10824 }
10825 }
10826
10827 size_t ident_length = (size_t) (parser->current.end - ident_start);
10828 bool ident_error = false;
10829
10830 if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) {
10831 pm_parser_err(parser, U32(ident_start - parser->start), U32(ident_length), PM_ERR_HEREDOC_IDENTIFIER);
10832 ident_error = true;
10833 }
10834
10835 parser->explicit_encoding = NULL;
10836 lex_mode_push(parser, (pm_lex_mode_t) {
10837 .mode = PM_LEX_HEREDOC,
10838 .as.heredoc = {
10839 .base = {
10840 .ident_start = ident_start,
10841 .ident_length = ident_length,
10842 .quote = quote,
10843 .indent = indent
10844 },
10845 .next_start = parser->current.end,
10846 .common_whitespace = NULL,
10847 .line_continuation = false
10848 }
10849 });
10850
10851 if (parser->heredoc_end == NULL) {
10852 const uint8_t *body_start = next_newline(parser->current.end, parser->end - parser->current.end);
10853
10854 if (body_start == NULL) {
10855 // If there is no newline after the heredoc identifier, then
10856 // this is not a valid heredoc declaration. In this case we
10857 // will add an error, but we will still return a heredoc
10858 // start.
10859 if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length);
10860 body_start = parser->end;
10861 } else {
10862 // Otherwise, we want to indicate that the body of the
10863 // heredoc starts on the character after the next newline.
10864 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(body_start - parser->start + 1));
10865 body_start++;
10866 }
10867
10868 parser->next_start = body_start;
10869 } else {
10870 parser->next_start = parser->heredoc_end;
10871 }
10872
10873 LEX(PM_TOKEN_HEREDOC_START);
10874 }
10875 }
10876
10877 if (match(parser, '=')) {
10878 lex_state_set(parser, PM_LEX_STATE_BEG);
10879 LEX(PM_TOKEN_LESS_LESS_EQUAL);
10880 }
10881
10882 if (ambiguous_operator_p(parser, space_seen)) {
10883 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "<<", "here document");
10884 }
10885
10886 if (lex_state_operator_p(parser)) {
10887 lex_state_set(parser, PM_LEX_STATE_ARG);
10888 } else {
10889 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10890 lex_state_set(parser, PM_LEX_STATE_BEG);
10891 }
10892
10893 LEX(PM_TOKEN_LESS_LESS);
10894 }
10895
10896 if (lex_state_operator_p(parser)) {
10897 lex_state_set(parser, PM_LEX_STATE_ARG);
10898 } else {
10899 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10900 lex_state_set(parser, PM_LEX_STATE_BEG);
10901 }
10902
10903 if (match(parser, '=')) {
10904 if (match(parser, '>')) {
10905 LEX(PM_TOKEN_LESS_EQUAL_GREATER);
10906 }
10907
10908 LEX(PM_TOKEN_LESS_EQUAL);
10909 }
10910
10911 LEX(PM_TOKEN_LESS);
10912
10913 // > >> >>= >=
10914 case '>':
10915 if (match(parser, '>')) {
10916 if (lex_state_operator_p(parser)) {
10917 lex_state_set(parser, PM_LEX_STATE_ARG);
10918 } else {
10919 lex_state_set(parser, PM_LEX_STATE_BEG);
10920 }
10921 LEX(match(parser, '=') ? PM_TOKEN_GREATER_GREATER_EQUAL : PM_TOKEN_GREATER_GREATER);
10922 }
10923
10924 if (lex_state_operator_p(parser)) {
10925 lex_state_set(parser, PM_LEX_STATE_ARG);
10926 } else {
10927 lex_state_set(parser, PM_LEX_STATE_BEG);
10928 }
10929
10930 LEX(match(parser, '=') ? PM_TOKEN_GREATER_EQUAL : PM_TOKEN_GREATER);
10931
10932 // double-quoted string literal
10933 case '"': {
10934 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10935 lex_mode_push_string(parser, true, label_allowed, '\0', '"');
10936 LEX(PM_TOKEN_STRING_BEGIN);
10937 }
10938
10939 // xstring literal
10940 case '`': {
10941 if (lex_state_p(parser, PM_LEX_STATE_FNAME)) {
10942 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10943 LEX(PM_TOKEN_BACKTICK);
10944 }
10945
10946 if (lex_state_p(parser, PM_LEX_STATE_DOT)) {
10947 if (previous_command_start) {
10948 lex_state_set(parser, PM_LEX_STATE_CMDARG);
10949 } else {
10950 lex_state_set(parser, PM_LEX_STATE_ARG);
10951 }
10952
10953 LEX(PM_TOKEN_BACKTICK);
10954 }
10955
10956 lex_mode_push_string(parser, true, false, '\0', '`');
10957 LEX(PM_TOKEN_XSTRING_BEGIN);
10958 }
10959
10960 // single-quoted string literal
10961 case '\'': {
10962 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10963 lex_mode_push_string(parser, false, label_allowed, '\0', '\'');
10964 LEX(PM_TOKEN_STRING_BEGIN);
10965 }
10966
10967 // ? character literal
10968 case '?':
10969 LEX(lex_question_mark(parser));
10970
10971 // & && &&= &=
10972 case '&': {
10973 if (match(parser, '&')) {
10974 lex_state_set(parser, PM_LEX_STATE_BEG);
10975
10976 if (match(parser, '=')) {
10977 LEX(PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
10978 }
10979
10980 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10981 }
10982
10983 if (match(parser, '=')) {
10984 lex_state_set(parser, PM_LEX_STATE_BEG);
10985 LEX(PM_TOKEN_AMPERSAND_EQUAL);
10986 }
10987
10988 if (match(parser, '.')) {
10989 lex_state_set(parser, PM_LEX_STATE_DOT);
10990 LEX(PM_TOKEN_AMPERSAND_DOT);
10991 }
10992
10993 pm_token_type_t type = PM_TOKEN_AMPERSAND;
10994 if (lex_state_spcarg_p(parser, space_seen)) {
10995 if ((peek(parser) != ':') || (peek_offset(parser, 1) == '\0')) {
10996 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
10997 } else {
10998 const uint8_t delim = peek_offset(parser, 1);
10999
11000 if ((delim != '\'') && (delim != '"') && !char_is_identifier(parser, parser->current.end + 1, parser->end - (parser->current.end + 1))) {
11001 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
11002 }
11003 }
11004
11005 type = PM_TOKEN_UAMPERSAND;
11006 } else if (lex_state_beg_p(parser)) {
11007 type = PM_TOKEN_UAMPERSAND;
11008 } else if (ambiguous_operator_p(parser, space_seen)) {
11009 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "&", "argument prefix");
11010 }
11011
11012 if (lex_state_operator_p(parser)) {
11013 lex_state_set(parser, PM_LEX_STATE_ARG);
11014 } else {
11015 lex_state_set(parser, PM_LEX_STATE_BEG);
11016 }
11017
11018 LEX(type);
11019 }
11020
11021 // | || ||= |=
11022 case '|':
11023 if (match(parser, '|')) {
11024 if (match(parser, '=')) {
11025 lex_state_set(parser, PM_LEX_STATE_BEG);
11026 LEX(PM_TOKEN_PIPE_PIPE_EQUAL);
11027 }
11028
11029 if (lex_state_p(parser, PM_LEX_STATE_BEG)) {
11030 parser->current.end--;
11031 LEX(PM_TOKEN_PIPE);
11032 }
11033
11034 lex_state_set(parser, PM_LEX_STATE_BEG);
11035 LEX(PM_TOKEN_PIPE_PIPE);
11036 }
11037
11038 if (match(parser, '=')) {
11039 lex_state_set(parser, PM_LEX_STATE_BEG);
11040 LEX(PM_TOKEN_PIPE_EQUAL);
11041 }
11042
11043 if (lex_state_operator_p(parser)) {
11044 lex_state_set(parser, PM_LEX_STATE_ARG);
11045 } else {
11046 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
11047 }
11048
11049 LEX(PM_TOKEN_PIPE);
11050
11051 // + += +@
11052 case '+': {
11053 if (lex_state_operator_p(parser)) {
11054 lex_state_set(parser, PM_LEX_STATE_ARG);
11055
11056 if (match(parser, '@')) {
11057 LEX(PM_TOKEN_UPLUS);
11058 }
11059
11060 LEX(PM_TOKEN_PLUS);
11061 }
11062
11063 if (match(parser, '=')) {
11064 lex_state_set(parser, PM_LEX_STATE_BEG);
11065 LEX(PM_TOKEN_PLUS_EQUAL);
11066 }
11067
11068 if (
11069 lex_state_beg_p(parser) ||
11070 (lex_state_spcarg_p(parser, space_seen) ? (pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS), true) : false)
11071 ) {
11072 lex_state_set(parser, PM_LEX_STATE_BEG);
11073
11074 if (pm_char_is_decimal_digit(peek(parser))) {
11075 parser->current.end++;
11076 pm_token_type_t type = lex_numeric(parser);
11077 lex_state_set(parser, PM_LEX_STATE_END);
11078 LEX(type);
11079 }
11080
11081 LEX(PM_TOKEN_UPLUS);
11082 }
11083
11084 if (ambiguous_operator_p(parser, space_seen)) {
11085 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "+", "unary operator");
11086 }
11087
11088 lex_state_set(parser, PM_LEX_STATE_BEG);
11089 LEX(PM_TOKEN_PLUS);
11090 }
11091
11092 // - -= -@
11093 case '-': {
11094 if (lex_state_operator_p(parser)) {
11095 lex_state_set(parser, PM_LEX_STATE_ARG);
11096
11097 if (match(parser, '@')) {
11098 LEX(PM_TOKEN_UMINUS);
11099 }
11100
11101 LEX(PM_TOKEN_MINUS);
11102 }
11103
11104 if (match(parser, '=')) {
11105 lex_state_set(parser, PM_LEX_STATE_BEG);
11106 LEX(PM_TOKEN_MINUS_EQUAL);
11107 }
11108
11109 if (match(parser, '>')) {
11110 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11111 LEX(PM_TOKEN_MINUS_GREATER);
11112 }
11113
11114 bool spcarg = lex_state_spcarg_p(parser, space_seen);
11115 bool is_beg = lex_state_beg_p(parser);
11116 if (!is_beg && spcarg) {
11117 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS);
11118 }
11119
11120 if (is_beg || spcarg) {
11121 lex_state_set(parser, PM_LEX_STATE_BEG);
11122 LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS);
11123 }
11124
11125 if (ambiguous_operator_p(parser, space_seen)) {
11126 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "-", "unary operator");
11127 }
11128
11129 lex_state_set(parser, PM_LEX_STATE_BEG);
11130 LEX(PM_TOKEN_MINUS);
11131 }
11132
11133 // . .. ...
11134 case '.': {
11135 bool beg_p = lex_state_beg_p(parser);
11136
11137 if (match(parser, '.')) {
11138 if (match(parser, '.')) {
11139 // If we're _not_ inside a range within default parameters
11140 if (!context_p(parser, PM_CONTEXT_DEFAULT_PARAMS) && context_p(parser, PM_CONTEXT_DEF_PARAMS)) {
11141 if (lex_state_p(parser, PM_LEX_STATE_END)) {
11142 lex_state_set(parser, PM_LEX_STATE_BEG);
11143 } else {
11144 lex_state_set(parser, PM_LEX_STATE_ENDARG);
11145 }
11146 LEX(PM_TOKEN_UDOT_DOT_DOT);
11147 }
11148
11149 if (parser->enclosure_nesting == 0 && parser_end_of_line_p(parser)) {
11150 pm_parser_warn_token(parser, &parser->current, PM_WARN_DOT_DOT_DOT_EOL);
11151 }
11152
11153 lex_state_set(parser, PM_LEX_STATE_BEG);
11154 LEX(beg_p ? PM_TOKEN_UDOT_DOT_DOT : PM_TOKEN_DOT_DOT_DOT);
11155 }
11156
11157 lex_state_set(parser, PM_LEX_STATE_BEG);
11158 LEX(beg_p ? PM_TOKEN_UDOT_DOT : PM_TOKEN_DOT_DOT);
11159 }
11160
11161 lex_state_set(parser, PM_LEX_STATE_DOT);
11162 LEX(PM_TOKEN_DOT);
11163 }
11164
11165 // integer
11166 case '0':
11167 case '1':
11168 case '2':
11169 case '3':
11170 case '4':
11171 case '5':
11172 case '6':
11173 case '7':
11174 case '8':
11175 case '9': {
11176 pm_token_type_t type = lex_numeric(parser);
11177 lex_state_set(parser, PM_LEX_STATE_END);
11178 LEX(type);
11179 }
11180
11181 // :: symbol
11182 case ':':
11183 if (match(parser, ':')) {
11184 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)) {
11185 lex_state_set(parser, PM_LEX_STATE_BEG);
11186 LEX(PM_TOKEN_UCOLON_COLON);
11187 }
11188
11189 lex_state_set(parser, PM_LEX_STATE_DOT);
11190 LEX(PM_TOKEN_COLON_COLON);
11191 }
11192
11193 if (lex_state_end_p(parser) || pm_char_is_whitespace(peek(parser)) || peek(parser) == '#') {
11194 lex_state_set(parser, PM_LEX_STATE_BEG);
11195 LEX(PM_TOKEN_COLON);
11196 }
11197
11198 if (peek(parser) == '"' || peek(parser) == '\'') {
11199 lex_mode_push_string(parser, peek(parser) == '"', false, '\0', *parser->current.end);
11200 parser->current.end++;
11201 } else {
11202 /*
11203 * A quoted symbol clears its encoding by pushing a lex
11204 * mode above. A bare symbol is lexed inline, so it
11205 * clears the encoding here.
11206 */
11207 parser->explicit_encoding = NULL;
11208 }
11209
11210 lex_state_set(parser, PM_LEX_STATE_FNAME);
11211 LEX(PM_TOKEN_SYMBOL_BEGIN);
11212
11213 // / /=
11214 case '/':
11215 if (lex_state_beg_p(parser)) {
11216 lex_mode_push_regexp(parser, '\0', '/');
11217 LEX(PM_TOKEN_REGEXP_BEGIN);
11218 }
11219
11220 if (match(parser, '=')) {
11221 lex_state_set(parser, PM_LEX_STATE_BEG);
11222 LEX(PM_TOKEN_SLASH_EQUAL);
11223 }
11224
11225 if (lex_state_spcarg_p(parser, space_seen)) {
11226 // https://bugs.ruby-lang.org/issues/21994
11227 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_4_0) {
11228 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_SLASH);
11229 }
11230 lex_mode_push_regexp(parser, '\0', '/');
11231 LEX(PM_TOKEN_REGEXP_BEGIN);
11232 }
11233
11234 if (ambiguous_operator_p(parser, space_seen)) {
11235 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "/", "regexp literal");
11236 }
11237
11238 if (lex_state_operator_p(parser)) {
11239 lex_state_set(parser, PM_LEX_STATE_ARG);
11240 } else {
11241 lex_state_set(parser, PM_LEX_STATE_BEG);
11242 }
11243
11244 LEX(PM_TOKEN_SLASH);
11245
11246 // ^ ^=
11247 case '^':
11248 if (lex_state_operator_p(parser)) {
11249 lex_state_set(parser, PM_LEX_STATE_ARG);
11250 } else {
11251 lex_state_set(parser, PM_LEX_STATE_BEG);
11252 }
11253 LEX(match(parser, '=') ? PM_TOKEN_CARET_EQUAL : PM_TOKEN_CARET);
11254
11255 // ~ ~@
11256 case '~':
11257 if (lex_state_operator_p(parser)) {
11258 (void) match(parser, '@');
11259 lex_state_set(parser, PM_LEX_STATE_ARG);
11260 } else {
11261 lex_state_set(parser, PM_LEX_STATE_BEG);
11262 }
11263
11264 LEX(PM_TOKEN_TILDE);
11265
11266 // % %= %i %I %q %Q %w %W
11267 case '%': {
11268 // If there is no subsequent character then we have an
11269 // invalid token. We're going to say it's the percent
11270 // operator because we don't want to move into the string
11271 // lex mode unnecessarily.
11272 if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) {
11273 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT_EOF);
11274 LEX(PM_TOKEN_PERCENT);
11275 }
11276
11277 if (!lex_state_beg_p(parser) && match(parser, '=')) {
11278 lex_state_set(parser, PM_LEX_STATE_BEG);
11279 LEX(PM_TOKEN_PERCENT_EQUAL);
11280 } else if (
11281 lex_state_beg_p(parser) ||
11282 (lex_state_p(parser, PM_LEX_STATE_FITEM) && (peek(parser) == 's')) ||
11283 lex_state_spcarg_p(parser, space_seen)
11284 ) {
11285 if (!parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end)) {
11286 if (*parser->current.end >= 0x80) {
11287 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11288 goto lex_next_token;
11289 }
11290
11291 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11292 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11293 LEX(PM_TOKEN_STRING_BEGIN);
11294 }
11295
11296 // Delimiters for %-literals cannot be alphanumeric. We
11297 // validate that here.
11298 uint8_t delimiter = peek_offset(parser, 1);
11299 if (delimiter >= 0x80 || parser->encoding->alnum_char(&delimiter, 1)) {
11300 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11301 goto lex_next_token;
11302 }
11303
11304 switch (peek(parser)) {
11305 case 'i': {
11306 parser->current.end++;
11307
11308 if (parser->current.end < parser->end) {
11309 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11310 } else {
11311 lex_mode_push_list_eof(parser);
11312 }
11313
11314 LEX(PM_TOKEN_PERCENT_LOWER_I);
11315 }
11316 case 'I': {
11317 parser->current.end++;
11318
11319 if (parser->current.end < parser->end) {
11320 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11321 } else {
11322 lex_mode_push_list_eof(parser);
11323 }
11324
11325 LEX(PM_TOKEN_PERCENT_UPPER_I);
11326 }
11327 case 'r': {
11328 parser->current.end++;
11329
11330 if (parser->current.end < parser->end) {
11331 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11332 lex_mode_push_regexp(parser, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11333 } else {
11334 lex_mode_push_regexp(parser, '\0', '\0');
11335 }
11336
11337 LEX(PM_TOKEN_REGEXP_BEGIN);
11338 }
11339 case 'q': {
11340 parser->current.end++;
11341
11342 if (parser->current.end < parser->end) {
11343 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11344 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11345 } else {
11346 lex_mode_push_string_eof(parser);
11347 }
11348
11349 LEX(PM_TOKEN_STRING_BEGIN);
11350 }
11351 case 'Q': {
11352 parser->current.end++;
11353
11354 if (parser->current.end < parser->end) {
11355 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11356 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11357 } else {
11358 lex_mode_push_string_eof(parser);
11359 }
11360
11361 LEX(PM_TOKEN_STRING_BEGIN);
11362 }
11363 case 's': {
11364 parser->current.end++;
11365
11366 if (parser->current.end < parser->end) {
11367 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11368 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11369 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
11370 } else {
11371 lex_mode_push_string_eof(parser);
11372 }
11373
11374 LEX(PM_TOKEN_SYMBOL_BEGIN);
11375 }
11376 case 'w': {
11377 parser->current.end++;
11378
11379 if (parser->current.end < parser->end) {
11380 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11381 } else {
11382 lex_mode_push_list_eof(parser);
11383 }
11384
11385 LEX(PM_TOKEN_PERCENT_LOWER_W);
11386 }
11387 case 'W': {
11388 parser->current.end++;
11389
11390 if (parser->current.end < parser->end) {
11391 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11392 } else {
11393 lex_mode_push_list_eof(parser);
11394 }
11395
11396 LEX(PM_TOKEN_PERCENT_UPPER_W);
11397 }
11398 case 'x': {
11399 parser->current.end++;
11400
11401 if (parser->current.end < parser->end) {
11402 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11403 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11404 } else {
11405 lex_mode_push_string_eof(parser);
11406 }
11407
11408 LEX(PM_TOKEN_PERCENT_LOWER_X);
11409 }
11410 default:
11411 // If we get to this point, then we have a % that is completely
11412 // unparsable. In this case we'll just drop it from the parser
11413 // and skip past it and hope that the next token is something
11414 // that we can parse.
11415 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11416 goto lex_next_token;
11417 }
11418 }
11419
11420 if (ambiguous_operator_p(parser, space_seen)) {
11421 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "%", "string literal");
11422 }
11423
11424 lex_state_set(parser, lex_state_operator_p(parser) ? PM_LEX_STATE_ARG : PM_LEX_STATE_BEG);
11425 LEX(PM_TOKEN_PERCENT);
11426 }
11427
11428 // global variable
11429 case '$': {
11430 pm_token_type_t type = lex_global_variable(parser);
11431
11432 // If we're lexing an embedded variable, then we need to pop back into
11433 // the parent lex context.
11434 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
11435 lex_mode_pop(parser);
11436 }
11437
11438 lex_state_set(parser, PM_LEX_STATE_END);
11439 LEX(type);
11440 }
11441
11442 // instance variable, class variable
11443 case '@':
11444 lex_state_set(parser, parser->lex_state & PM_LEX_STATE_FNAME ? PM_LEX_STATE_ENDFN : PM_LEX_STATE_END);
11445 LEX(lex_at_variable(parser));
11446
11447 default: {
11448 if (*parser->current.start != '_') {
11449 size_t width = char_is_identifier_start(parser, parser->current.start, parser->end - parser->current.start);
11450
11451 // If this isn't the beginning of an identifier, then
11452 // it's an invalid token as we've exhausted all of the
11453 // other options. We'll skip past it and return the next
11454 // token after adding an appropriate error message.
11455 if (!width) {
11456 if (*parser->current.start >= 0x80) {
11457 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *parser->current.start);
11458 } else if (*parser->current.start == '\\') {
11459 switch (peek_at(parser, parser->current.start + 1)) {
11460 case ' ':
11461 parser->current.end++;
11462 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped space");
11463 break;
11464 case '\f':
11465 parser->current.end++;
11466 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped form feed");
11467 break;
11468 case '\t':
11469 parser->current.end++;
11470 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped horizontal tab");
11471 break;
11472 case '\v':
11473 parser->current.end++;
11474 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped vertical tab");
11475 break;
11476 case '\r':
11477 if (peek_at(parser, parser->current.start + 2) != '\n') {
11478 parser->current.end++;
11479 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped carriage return");
11480 break;
11481 }
11483 default:
11484 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "backslash");
11485 break;
11486 }
11487 } else if (char_is_ascii_printable(*parser->current.start)) {
11488 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_PRINTABLE_CHARACTER, *parser->current.start);
11489 } else {
11490 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_CHARACTER, *parser->current.start);
11491 }
11492
11493 goto lex_next_token;
11494 }
11495
11496 parser->current.end = parser->current.start + width;
11497 }
11498
11499 pm_token_type_t type = lex_identifier(parser, previous_command_start);
11500
11501 // If we've hit a __END__ and it was at the start of the
11502 // line or the start of the file and it is followed by
11503 // either a \n or a \r\n, then this is the last token of the
11504 // file.
11505 if (
11506 ((parser->current.end - parser->current.start) == 7) &&
11507 current_token_starts_line(parser) &&
11508 (memcmp(parser->current.start, "__END__", 7) == 0) &&
11509 (parser->current.end == parser->end || match_eol(parser))
11510 ) {
11511 // Since we know we're about to add an __END__ comment,
11512 // we know we need to add all of the newlines to get the
11513 // correct column information for it.
11514 const uint8_t *cursor = parser->current.end;
11515 while ((cursor = next_newline(cursor, parser->end - cursor)) != NULL) {
11516 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(++cursor - parser->start));
11517 }
11518
11519 parser->current.end = parser->end;
11520 parser->current.type = PM_TOKEN___END__;
11521 parser_lex_callback(parser);
11522
11523 parser->data_loc.start = PM_TOKEN_START(parser, &parser->current);
11524 parser->data_loc.length = PM_TOKEN_LENGTH(&parser->current);
11525
11526 LEX(PM_TOKEN_EOF);
11527 }
11528
11529 pm_lex_state_t last_state = parser->lex_state;
11530
11531 if (type == PM_TOKEN_IDENTIFIER || type == PM_TOKEN_CONSTANT || type == PM_TOKEN_METHOD_NAME) {
11532 if (lex_state_p(parser, PM_LEX_STATE_BEG_ANY | PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_DOT)) {
11533 if (previous_command_start) {
11534 lex_state_set(parser, PM_LEX_STATE_CMDARG);
11535 } else {
11536 lex_state_set(parser, PM_LEX_STATE_ARG);
11537 }
11538 } else if (parser->lex_state == PM_LEX_STATE_FNAME) {
11539 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11540 } else {
11541 lex_state_set(parser, PM_LEX_STATE_END);
11542 }
11543 }
11544
11545 if (
11546 !(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) &&
11547 (type == PM_TOKEN_IDENTIFIER) &&
11548 ((pm_parser_local_depth(parser, &parser->current) != -1) ||
11549 pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)))
11550 ) {
11551 lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL);
11552 }
11553
11554 LEX(type);
11555 }
11556 }
11557 }
11558 case PM_LEX_LIST: {
11559 if (parser->next_start != NULL) {
11560 parser->current.end = parser->next_start;
11561 parser->next_start = NULL;
11562 }
11563
11564 // First we'll set the beginning of the token.
11565 parser->current.start = parser->current.end;
11566
11567 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11568
11569 // If there's any whitespace at the start of the list, then we're
11570 // going to trim it off the beginning and create a new token.
11571 size_t whitespace;
11572
11573 if (parser->heredoc_end) {
11574 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11575 if (peek_offset(parser, (ptrdiff_t)whitespace) == '\n') {
11576 whitespace += 1;
11577 }
11578 } else if (lex_mode->as.list.terminator == '\n') {
11579 // When the list delimiter is a newline (e.g. `%w` followed by a
11580 // newline), the newline is the terminator rather than a word
11581 // separator. We only trim inline whitespace here so that the
11582 // terminating newline is left for the terminator handling below.
11583 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11584 } else {
11585 whitespace = pm_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11586 }
11587
11588 if (whitespace > 0) {
11589 parser->current.end += whitespace;
11590 if (peek_offset(parser, -1) == '\n') {
11591 // mutates next_start
11592 parser_flush_heredoc_end(parser);
11593 }
11594
11595 lex_mode->as.list.started = true;
11596 lex_mode->as.list.separated = true;
11597 LEX(PM_TOKEN_WORDS_SEP);
11598 }
11599
11600 // We'll check if we're at the end of the file. If we are, then we
11601 // need to return the EOF token.
11602 if (parser->current.end >= parser->end) {
11603 LEX(PM_TOKEN_EOF);
11604 }
11605
11606 /* A word separator delimits the opener from the first word (or
11607 * from the terminator when the list is empty). The parser accepts
11608 * the words without it, so when the list does not start with
11609 * whitespace the implicit separator goes to the lex callback alone
11610 * and lexing continues on to the token the parser receives. */
11611 if (!lex_mode->as.list.started) {
11612 lex_mode->as.list.started = true;
11613 lex_mode->as.list.separated = true;
11614 parser->current.type = PM_TOKEN_WORDS_SEP_IMPLICIT;
11615 parser_lex_callback(parser);
11616 }
11617
11618 // Here we'll get a list of the places where strpbrk should break,
11619 // and then find the first one.
11620 const uint8_t *breakpoints = lex_mode->as.list.breakpoints;
11621 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11622
11623 // If we haven't found an escape yet, then this buffer will be
11624 // unallocated since we can refer directly to the source string.
11625 pm_token_buffer_t token_buffer = { 0 };
11626
11627 while (breakpoint != NULL) {
11628 // If we hit whitespace, then we must have received content by
11629 // now, so we can return an element of the list. A whitespace
11630 // character that is also the terminator (e.g. a newline
11631 // delimiter) is handled by the terminator check below, not here.
11632 if (pm_char_is_whitespace(*breakpoint) && *breakpoint != lex_mode->as.list.terminator) {
11633 parser->current.end = breakpoint;
11634 pm_token_buffer_flush(parser, &token_buffer);
11635 lex_mode->as.list.separated = false;
11636 LEX(PM_TOKEN_STRING_CONTENT);
11637 }
11638
11639 // If we hit the terminator, we need to check which token to
11640 // return.
11641 if (*breakpoint == lex_mode->as.list.terminator) {
11642 // If this terminator doesn't actually close the list, then
11643 // we need to continue on past it.
11644 if (lex_mode->as.list.nesting > 0) {
11645 parser->current.end = breakpoint + 1;
11646 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11647 lex_mode->as.list.nesting--;
11648 continue;
11649 }
11650
11651 // If we've hit the terminator and we've already skipped
11652 // past content, then we can return a list node.
11653 if (breakpoint > parser->current.start) {
11654 parser->current.end = breakpoint;
11655 pm_token_buffer_flush(parser, &token_buffer);
11656 lex_mode->as.list.separated = false;
11657 LEX(PM_TOKEN_STRING_CONTENT);
11658 }
11659
11660 /* A word separator delimits the last word from the
11661 * terminator. The parser accepts the terminator without
11662 * it, so when the list does not end with whitespace the
11663 * implicit separator goes to the lex callback alone and
11664 * lexing continues on to the terminator. */
11665 if (!lex_mode->as.list.separated) {
11666 lex_mode->as.list.separated = true;
11667 parser->current.end = breakpoint;
11668 parser->current.type = PM_TOKEN_WORDS_SEP_IMPLICIT;
11669 parser_lex_callback(parser);
11670 }
11671
11672 // Otherwise, switch back to the default state and return
11673 // the end of the list.
11674 parser->current.end = breakpoint + 1;
11675
11676 // If the terminator is a newline (i.e. the list delimiter
11677 // was a newline), then we need to record it so that line
11678 // numbers after the list remain accurate.
11679 if (*breakpoint == '\n') {
11680 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11681 }
11682
11683 lex_mode_pop(parser);
11684 lex_state_set(parser, PM_LEX_STATE_END);
11685 LEX(PM_TOKEN_STRING_END);
11686 }
11687
11688 // If we hit a null byte, skip directly past it.
11689 if (*breakpoint == '\0') {
11690 breakpoint = pm_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1), true);
11691 continue;
11692 }
11693
11694 // If we hit escapes, then we need to treat the next token
11695 // literally. In this case we'll skip past the next character
11696 // and find the next breakpoint.
11697 if (*breakpoint == '\\') {
11698 parser->current.end = breakpoint + 1;
11699
11700 // If we've hit the end of the file, then break out of the
11701 // loop by setting the breakpoint to NULL.
11702 if (parser->current.end == parser->end) {
11703 breakpoint = NULL;
11704 continue;
11705 }
11706
11707 pm_token_buffer_escape(parser, &token_buffer);
11708 uint8_t peeked = peek(parser);
11709
11710 switch (peeked) {
11711 case ' ':
11712 case '\f':
11713 case '\t':
11714 case '\v':
11715 case '\\':
11716 pm_token_buffer_push_byte(&token_buffer, peeked);
11717 parser->current.end++;
11718 break;
11719 case '\r':
11720 parser->current.end++;
11721 if (peek(parser) != '\n') {
11722 pm_token_buffer_push_byte(&token_buffer, '\r');
11723 break;
11724 }
11726 case '\n':
11727 pm_token_buffer_push_byte(&token_buffer, '\n');
11728
11729 if (parser->heredoc_end) {
11730 // ... if we are on the same line as a heredoc,
11731 // flush the heredoc and continue parsing after
11732 // heredoc_end.
11733 parser_flush_heredoc_end(parser);
11734 pm_token_buffer_copy(parser, &token_buffer);
11735 LEX(PM_TOKEN_STRING_CONTENT);
11736 } else {
11737 // ... else track the newline.
11738 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11739 }
11740
11741 parser->current.end++;
11742 break;
11743 default:
11744 if (peeked == lex_mode->as.list.incrementor || peeked == lex_mode->as.list.terminator) {
11745 pm_token_buffer_push_byte(&token_buffer, peeked);
11746 parser->current.end++;
11747 } else if (lex_mode->as.list.interpolation) {
11748 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
11749 } else {
11750 pm_token_buffer_push_byte(&token_buffer, '\\');
11751 pm_token_buffer_push_escaped(&token_buffer, parser);
11752 }
11753
11754 break;
11755 }
11756
11757 token_buffer.cursor = parser->current.end;
11758 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11759 continue;
11760 }
11761
11762 // If we hit a #, then we will attempt to lex interpolation.
11763 if (*breakpoint == '#') {
11764 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11765
11766 if (!type) {
11767 // If we haven't returned at this point then we had something
11768 // that looked like an interpolated class or instance variable
11769 // like "#@" but wasn't actually. In this case we'll just skip
11770 // to the next breakpoint.
11771 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11772 continue;
11773 }
11774
11775 if (type == PM_TOKEN_STRING_CONTENT) {
11776 pm_token_buffer_flush(parser, &token_buffer);
11777 }
11778
11779 lex_mode->as.list.separated = false;
11780 LEX(type);
11781 }
11782
11783 // If we've hit the incrementor, then we need to skip past it
11784 // and find the next breakpoint.
11785 assert(*breakpoint == lex_mode->as.list.incrementor);
11786 parser->current.end = breakpoint + 1;
11787 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11788 lex_mode->as.list.nesting++;
11789 continue;
11790 }
11791
11792 if (parser->current.end > parser->current.start) {
11793 pm_token_buffer_flush(parser, &token_buffer);
11794 LEX(PM_TOKEN_STRING_CONTENT);
11795 }
11796
11797 // If we were unable to find a breakpoint, then this token hits the
11798 // end of the file.
11799 parser->current.end = parser->end;
11800 pm_token_buffer_flush(parser, &token_buffer);
11801 LEX(PM_TOKEN_STRING_CONTENT);
11802 }
11803 case PM_LEX_REGEXP: {
11804 // First, we'll set to start of this token to be the current end.
11805 if (parser->next_start == NULL) {
11806 parser->current.start = parser->current.end;
11807 } else {
11808 parser->current.start = parser->next_start;
11809 parser->current.end = parser->next_start;
11810 parser->next_start = NULL;
11811 }
11812
11813 // We'll check if we're at the end of the file. If we are, then we
11814 // need to return the EOF token.
11815 if (parser->current.end >= parser->end) {
11816 LEX(PM_TOKEN_EOF);
11817 }
11818
11819 // Get a reference to the current mode.
11820 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11821
11822 // These are the places where we need to split up the content of the
11823 // regular expression. We'll use strpbrk to find the first of these
11824 // characters.
11825 const uint8_t *breakpoints = lex_mode->as.regexp.breakpoints;
11826 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11827 pm_regexp_token_buffer_t token_buffer = { 0 };
11828
11829 while (breakpoint != NULL) {
11830 uint8_t term = lex_mode->as.regexp.terminator;
11831 bool is_terminator = (*breakpoint == term);
11832
11833 // If the terminator is newline, we need to consider \r\n _also_ a newline
11834 // For example: `%\nfoo\r\n`
11835 // The string should be "foo", not "foo\r"
11836 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
11837 if (term == '\n') {
11838 is_terminator = true;
11839 }
11840
11841 // If the terminator is a CR, but we see a CRLF, we need to
11842 // treat the CRLF as a newline, meaning this is _not_ the
11843 // terminator
11844 if (term == '\r') {
11845 is_terminator = false;
11846 }
11847 }
11848
11849 // If we hit the terminator, we need to determine what kind of
11850 // token to return.
11851 if (is_terminator) {
11852 if (lex_mode->as.regexp.nesting > 0) {
11853 parser->current.end = breakpoint + 1;
11854 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11855 lex_mode->as.regexp.nesting--;
11856 continue;
11857 }
11858
11859 // Here we've hit the terminator. If we have already consumed
11860 // content then we need to return that content as string content
11861 // first.
11862 if (breakpoint > parser->current.start) {
11863 parser->current.end = breakpoint;
11864 pm_regexp_token_buffer_flush(parser, &token_buffer);
11865 LEX(PM_TOKEN_STRING_CONTENT);
11866 }
11867
11868 // Check here if we need to track the newline.
11869 size_t eol_length = match_eol_at(parser, breakpoint);
11870 if (eol_length) {
11871 parser->current.end = breakpoint + eol_length;
11872
11873 // Track the newline if we're not in a heredoc that
11874 // would have already have added the newline to the
11875 // list.
11876 if (parser->heredoc_end == NULL) {
11877 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11878 }
11879 } else {
11880 parser->current.end = breakpoint + 1;
11881 }
11882
11883 // Since we've hit the terminator of the regular expression,
11884 // we now need to parse the options.
11885 parser->current.end += pm_strspn_regexp_option(parser->current.end, parser->end - parser->current.end);
11886
11887 lex_mode_pop(parser);
11888 lex_state_set(parser, PM_LEX_STATE_END);
11889 LEX(PM_TOKEN_REGEXP_END);
11890 }
11891
11892 // If we've hit the incrementor, then we need to skip past it
11893 // and find the next breakpoint.
11894 if (*breakpoint && *breakpoint == lex_mode->as.regexp.incrementor) {
11895 parser->current.end = breakpoint + 1;
11896 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11897 lex_mode->as.regexp.nesting++;
11898 continue;
11899 }
11900
11901 switch (*breakpoint) {
11902 case '\0':
11903 // If we hit a null byte, skip directly past it.
11904 parser->current.end = breakpoint + 1;
11905 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11906 break;
11907 case '\r':
11908 if (peek_at(parser, breakpoint + 1) != '\n') {
11909 parser->current.end = breakpoint + 1;
11910 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11911 break;
11912 }
11913
11914 breakpoint++;
11915 parser->current.end = breakpoint;
11916 pm_regexp_token_buffer_escape(parser, &token_buffer);
11917 token_buffer.base.cursor = breakpoint;
11918
11920 case '\n':
11921 // If we've hit a newline, then we need to track that in
11922 // the list of newlines.
11923 if (parser->heredoc_end == NULL) {
11924 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
11925 parser->current.end = breakpoint + 1;
11926 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11927 break;
11928 }
11929
11930 parser->current.end = breakpoint + 1;
11931 parser_flush_heredoc_end(parser);
11932 pm_regexp_token_buffer_flush(parser, &token_buffer);
11933 LEX(PM_TOKEN_STRING_CONTENT);
11934 case '\\': {
11935 // If we hit escapes, then we need to treat the next
11936 // token literally. In this case we'll skip past the
11937 // next character and find the next breakpoint.
11938 parser->current.end = breakpoint + 1;
11939
11940 // If we've hit the end of the file, then break out of
11941 // the loop by setting the breakpoint to NULL.
11942 if (parser->current.end == parser->end) {
11943 breakpoint = NULL;
11944 break;
11945 }
11946
11947 pm_regexp_token_buffer_escape(parser, &token_buffer);
11948 uint8_t peeked = peek(parser);
11949
11950 switch (peeked) {
11951 case '\r':
11952 parser->current.end++;
11953 if (peek(parser) != '\n') {
11954 if (lex_mode->as.regexp.terminator != '\r') {
11955 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11956 }
11957 pm_regexp_token_buffer_push_byte(&token_buffer, '\r');
11958 pm_token_buffer_push_byte(&token_buffer.base, '\r');
11959 break;
11960 }
11962 case '\n':
11963 if (parser->heredoc_end) {
11964 // ... if we are on the same line as a heredoc,
11965 // flush the heredoc and continue parsing after
11966 // heredoc_end.
11967 parser_flush_heredoc_end(parser);
11968 pm_regexp_token_buffer_copy(parser, &token_buffer);
11969 LEX(PM_TOKEN_STRING_CONTENT);
11970 } else {
11971 // ... else track the newline.
11972 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11973 }
11974
11975 parser->current.end++;
11976 break;
11977 case 'c':
11978 case 'C':
11979 case 'M':
11980 case 'u':
11981 case 'x':
11982 escape_read(parser, &token_buffer.regexp_buffer, &token_buffer.base.buffer, PM_ESCAPE_FLAG_REGEXP);
11983 break;
11984 default:
11985 if (lex_mode->as.regexp.terminator == peeked) {
11986 // Some characters when they are used as the
11987 // terminator also receive an escape. They are
11988 // enumerated here.
11989 switch (peeked) {
11990 case '$': case ')': case '*': case '+':
11991 case '.': case '>': case '?': case ']':
11992 case '^': case '|': case '}':
11993 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11994 break;
11995 default:
11996 break;
11997 }
11998
11999 pm_regexp_token_buffer_push_byte(&token_buffer, peeked);
12000 pm_token_buffer_push_byte(&token_buffer.base, peeked);
12001 parser->current.end++;
12002 break;
12003 }
12004
12005 if (peeked < 0x80) pm_token_buffer_push_byte(&token_buffer.base, '\\');
12006 pm_regexp_token_buffer_push_escaped(&token_buffer, parser);
12007 break;
12008 }
12009
12010 token_buffer.base.cursor = parser->current.end;
12011 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
12012 break;
12013 }
12014 case '#': {
12015 // If we hit a #, then we will attempt to lex
12016 // interpolation.
12017 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12018
12019 if (!type) {
12020 // If we haven't returned at this point then we had
12021 // something that looked like an interpolated class or
12022 // instance variable like "#@" but wasn't actually. In
12023 // this case we'll just skip to the next breakpoint.
12024 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
12025 break;
12026 }
12027
12028 if (type == PM_TOKEN_STRING_CONTENT) {
12029 pm_regexp_token_buffer_flush(parser, &token_buffer);
12030 }
12031
12032 LEX(type);
12033 }
12034 default:
12035 assert(false && "unreachable");
12036 break;
12037 }
12038 }
12039
12040 if (parser->current.end > parser->current.start) {
12041 pm_regexp_token_buffer_flush(parser, &token_buffer);
12042 LEX(PM_TOKEN_STRING_CONTENT);
12043 }
12044
12045 // If we were unable to find a breakpoint, then this token hits the
12046 // end of the file.
12047 parser->current.end = parser->end;
12048 pm_regexp_token_buffer_flush(parser, &token_buffer);
12049 LEX(PM_TOKEN_STRING_CONTENT);
12050 }
12051 case PM_LEX_STRING: {
12052 // First, we'll set to start of this token to be the current end.
12053 if (parser->next_start == NULL) {
12054 parser->current.start = parser->current.end;
12055 } else {
12056 parser->current.start = parser->next_start;
12057 parser->current.end = parser->next_start;
12058 parser->next_start = NULL;
12059 }
12060
12061 // We'll check if we're at the end of the file. If we are, then we need to
12062 // return the EOF token.
12063 if (parser->current.end >= parser->end) {
12064 LEX(PM_TOKEN_EOF);
12065 }
12066
12067 // These are the places where we need to split up the content of the
12068 // string. We'll use strpbrk to find the first of these characters.
12069 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12070 const uint8_t *breakpoints = lex_mode->as.string.breakpoints;
12071 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12072
12073 // If we haven't found an escape yet, then this buffer will be
12074 // unallocated since we can refer directly to the source string.
12075 pm_token_buffer_t token_buffer = { 0 };
12076
12077 while (breakpoint != NULL) {
12078 // If we hit the incrementor, then we'll increment then nesting and
12079 // continue lexing.
12080 if (lex_mode->as.string.incrementor != '\0' && *breakpoint == lex_mode->as.string.incrementor) {
12081 lex_mode->as.string.nesting++;
12082 parser->current.end = breakpoint + 1;
12083 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12084 continue;
12085 }
12086
12087 uint8_t term = lex_mode->as.string.terminator;
12088 bool is_terminator = (*breakpoint == term);
12089
12090 // If the terminator is newline, we need to consider \r\n _also_ a newline
12091 // For example: `%r\nfoo\r\n`
12092 // The string should be /foo/, not /foo\r/
12093 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
12094 if (term == '\n') {
12095 is_terminator = true;
12096 }
12097
12098 // If the terminator is a CR, but we see a CRLF, we need to
12099 // treat the CRLF as a newline, meaning this is _not_ the
12100 // terminator
12101 if (term == '\r') {
12102 is_terminator = false;
12103 }
12104 }
12105
12106 // Note that we have to check the terminator here first because we could
12107 // potentially be parsing a % string that has a # character as the
12108 // terminator.
12109 if (is_terminator) {
12110 // If this terminator doesn't actually close the string, then we need
12111 // to continue on past it.
12112 if (lex_mode->as.string.nesting > 0) {
12113 parser->current.end = breakpoint + 1;
12114 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12115 lex_mode->as.string.nesting--;
12116 continue;
12117 }
12118
12119 // Here we've hit the terminator. If we have already consumed content
12120 // then we need to return that content as string content first.
12121 if (breakpoint > parser->current.start) {
12122 parser->current.end = breakpoint;
12123 pm_token_buffer_flush(parser, &token_buffer);
12124 LEX(PM_TOKEN_STRING_CONTENT);
12125 }
12126
12127 // Otherwise we need to switch back to the parent lex mode and
12128 // return the end of the string.
12129 size_t eol_length = match_eol_at(parser, breakpoint);
12130 if (eol_length) {
12131 parser->current.end = breakpoint + eol_length;
12132
12133 // Track the newline if we're not in a heredoc that
12134 // would have already have added the newline to the
12135 // list.
12136 if (parser->heredoc_end == NULL) {
12137 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
12138 }
12139 } else {
12140 parser->current.end = breakpoint + 1;
12141 }
12142
12143 if (lex_mode->as.string.label_allowed && (peek(parser) == ':') && (peek_offset(parser, 1) != ':')) {
12144 parser->current.end++;
12145 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
12146 lex_mode_pop(parser);
12147 LEX(PM_TOKEN_LABEL_END);
12148 }
12149
12150 // When the delimiter itself is a newline, we won't
12151 // get a chance to flush heredocs in the usual places since
12152 // the newline is already consumed.
12153 if (term == '\n' && parser->heredoc_end) {
12154 parser_flush_heredoc_end(parser);
12155 }
12156
12157 lex_state_set(parser, PM_LEX_STATE_END);
12158 lex_mode_pop(parser);
12159 LEX(PM_TOKEN_STRING_END);
12160 }
12161
12162 switch (*breakpoint) {
12163 case '\0':
12164 // Skip directly past the null character.
12165 parser->current.end = breakpoint + 1;
12166 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12167 break;
12168 case '\r':
12169 if (peek_at(parser, breakpoint + 1) != '\n') {
12170 parser->current.end = breakpoint + 1;
12171 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12172 break;
12173 }
12174
12175 // If we hit a \r\n sequence, then we need to treat it
12176 // as a newline.
12177 breakpoint++;
12178 parser->current.end = breakpoint;
12179 pm_token_buffer_escape(parser, &token_buffer);
12180 token_buffer.cursor = breakpoint;
12181
12183 case '\n':
12184 // When we hit a newline, we need to flush any potential
12185 // heredocs. Note that this has to happen after we check
12186 // for the terminator in case the terminator is a
12187 // newline character.
12188 if (parser->heredoc_end == NULL) {
12189 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12190 parser->current.end = breakpoint + 1;
12191 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12192 break;
12193 }
12194
12195 parser->current.end = breakpoint + 1;
12196 parser_flush_heredoc_end(parser);
12197 pm_token_buffer_flush(parser, &token_buffer);
12198 LEX(PM_TOKEN_STRING_CONTENT);
12199 case '\\': {
12200 // Here we hit escapes.
12201 parser->current.end = breakpoint + 1;
12202
12203 // If we've hit the end of the file, then break out of
12204 // the loop by setting the breakpoint to NULL.
12205 if (parser->current.end == parser->end) {
12206 breakpoint = NULL;
12207 continue;
12208 }
12209
12210 pm_token_buffer_escape(parser, &token_buffer);
12211 uint8_t peeked = peek(parser);
12212
12213 switch (peeked) {
12214 case '\\':
12215 pm_token_buffer_push_byte(&token_buffer, '\\');
12216 parser->current.end++;
12217 break;
12218 case '\r':
12219 parser->current.end++;
12220 if (peek(parser) != '\n') {
12221 if (!lex_mode->as.string.interpolation) {
12222 pm_token_buffer_push_byte(&token_buffer, '\\');
12223 }
12224 pm_token_buffer_push_byte(&token_buffer, '\r');
12225 break;
12226 }
12228 case '\n':
12229 if (!lex_mode->as.string.interpolation) {
12230 pm_token_buffer_push_byte(&token_buffer, '\\');
12231 pm_token_buffer_push_byte(&token_buffer, '\n');
12232 }
12233
12234 if (parser->heredoc_end) {
12235 // ... if we are on the same line as a heredoc,
12236 // flush the heredoc and continue parsing after
12237 // heredoc_end.
12238 parser_flush_heredoc_end(parser);
12239 pm_token_buffer_copy(parser, &token_buffer);
12240 LEX(PM_TOKEN_STRING_CONTENT);
12241 } else {
12242 // ... else track the newline.
12243 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
12244 }
12245
12246 parser->current.end++;
12247 break;
12248 default:
12249 if (lex_mode->as.string.incrementor != '\0' && peeked == lex_mode->as.string.incrementor) {
12250 pm_token_buffer_push_byte(&token_buffer, peeked);
12251 parser->current.end++;
12252 } else if (lex_mode->as.string.terminator != '\0' && peeked == lex_mode->as.string.terminator) {
12253 pm_token_buffer_push_byte(&token_buffer, peeked);
12254 parser->current.end++;
12255 } else if (lex_mode->as.string.interpolation) {
12256 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12257 } else {
12258 pm_token_buffer_push_byte(&token_buffer, '\\');
12259 pm_token_buffer_push_escaped(&token_buffer, parser);
12260 }
12261
12262 break;
12263 }
12264
12265 token_buffer.cursor = parser->current.end;
12266 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12267 break;
12268 }
12269 case '#': {
12270 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12271
12272 if (!type) {
12273 // If we haven't returned at this point then we had something that
12274 // looked like an interpolated class or instance variable like "#@"
12275 // but wasn't actually. In this case we'll just skip to the next
12276 // breakpoint.
12277 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12278 break;
12279 }
12280
12281 if (type == PM_TOKEN_STRING_CONTENT) {
12282 pm_token_buffer_flush(parser, &token_buffer);
12283 }
12284
12285 LEX(type);
12286 }
12287 default:
12288 assert(false && "unreachable");
12289 }
12290 }
12291
12292 if (parser->current.end > parser->current.start) {
12293 pm_token_buffer_flush(parser, &token_buffer);
12294 LEX(PM_TOKEN_STRING_CONTENT);
12295 }
12296
12297 // If we've hit the end of the string, then this is an unterminated
12298 // string. In that case we'll return a string content token.
12299 parser->current.end = parser->end;
12300 pm_token_buffer_flush(parser, &token_buffer);
12301 LEX(PM_TOKEN_STRING_CONTENT);
12302 }
12303 case PM_LEX_HEREDOC: {
12304 // First, we'll set to start of this token.
12305 if (parser->next_start == NULL) {
12306 parser->current.start = parser->current.end;
12307 } else {
12308 parser->current.start = parser->next_start;
12309 parser->current.end = parser->next_start;
12310 parser->heredoc_end = NULL;
12311 parser->next_start = NULL;
12312 }
12313
12314 // Now let's grab the information about the identifier off of the
12315 // current lex mode.
12316 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12317 pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base;
12318
12319 bool line_continuation = lex_mode->as.heredoc.line_continuation;
12320 lex_mode->as.heredoc.line_continuation = false;
12321
12322 // We'll check if we're at the end of the file. If we are, then we
12323 // will add an error (because we weren't able to find the
12324 // terminator) but still continue parsing so that content after the
12325 // declaration of the heredoc can be parsed.
12326 if (parser->current.end >= parser->end) {
12327 pm_parser_err_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length);
12328 parser->next_start = lex_mode->as.heredoc.next_start;
12329 parser->heredoc_end = parser->current.end;
12330 lex_state_set(parser, PM_LEX_STATE_END);
12331 lex_mode_pop(parser);
12332 LEX(PM_TOKEN_HEREDOC_END);
12333 }
12334
12335 const uint8_t *ident_start = heredoc_lex_mode->ident_start;
12336 size_t ident_length = heredoc_lex_mode->ident_length;
12337
12338 // If we are immediately following a newline and we have hit the
12339 // terminator, then we need to return the ending of the heredoc.
12340 if (current_token_starts_line(parser)) {
12341 const uint8_t *start = parser->current.start;
12342
12343 if (!line_continuation && (start + ident_length <= parser->end)) {
12344 const uint8_t *newline = next_newline(start, parser->end - start);
12345 const uint8_t *ident_end = newline;
12346 const uint8_t *terminator_end = newline;
12347
12348 if (newline == NULL) {
12349 terminator_end = parser->end;
12350 ident_end = parser->end;
12351 } else {
12352 terminator_end++;
12353 if (newline[-1] == '\r') {
12354 ident_end--; // Remove \r
12355 }
12356 }
12357
12358 /*
12359 * The line on which we are looking for the terminator runs
12360 * from `start` to `ident_end`. If that line is shorter than
12361 * the identifier, the terminator cannot fit and we skip the
12362 * match. This also keeps `terminator_start` from pointing
12363 * before `start`.
12364 */
12365 if ((size_t) (ident_end - start) >= ident_length) {
12366 const uint8_t *terminator_start = ident_end - ident_length;
12367 const uint8_t *cursor = start;
12368
12369 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12370 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12371 cursor++;
12372 }
12373 }
12374
12375 if (
12376 (cursor == terminator_start) &&
12377 (memcmp(terminator_start, ident_start, ident_length) == 0)
12378 ) {
12379 if (newline != NULL) {
12380 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
12381 }
12382
12383 parser->current.end = terminator_end;
12384 if (*lex_mode->as.heredoc.next_start == '\\') {
12385 parser->next_start = NULL;
12386 } else {
12387 parser->next_start = lex_mode->as.heredoc.next_start;
12388 parser->heredoc_end = parser->current.end;
12389 }
12390
12391 lex_state_set(parser, PM_LEX_STATE_END);
12392 lex_mode_pop(parser);
12393 LEX(PM_TOKEN_HEREDOC_END);
12394 }
12395 }
12396 }
12397
12398 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent);
12399 if (
12400 heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE &&
12401 lex_mode->as.heredoc.common_whitespace != NULL &&
12402 (*lex_mode->as.heredoc.common_whitespace > whitespace) &&
12403 peek_at(parser, start) != '\n'
12404 ) {
12405 *lex_mode->as.heredoc.common_whitespace = whitespace;
12406 }
12407 }
12408
12409 // Otherwise we'll be parsing string content. These are the places
12410 // where we need to split up the content of the heredoc. We'll use
12411 // strpbrk to find the first of these characters.
12412 uint8_t breakpoints[PM_STRPBRK_CACHE_SIZE] = "\r\n\\#";
12413
12414 pm_heredoc_quote_t quote = heredoc_lex_mode->quote;
12415 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12416 breakpoints[3] = '\0';
12417 }
12418
12419 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12420 pm_token_buffer_t token_buffer = { 0 };
12421 bool was_line_continuation = false;
12422
12423 while (breakpoint != NULL) {
12424 switch (*breakpoint) {
12425 case '\0':
12426 // Skip directly past the null character.
12427 parser->current.end = breakpoint + 1;
12428 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12429 break;
12430 case '\r':
12431 parser->current.end = breakpoint + 1;
12432
12433 if (peek_at(parser, breakpoint + 1) != '\n') {
12434 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12435 break;
12436 }
12437
12438 // If we hit a \r\n sequence, then we want to replace it
12439 // with a single \n character in the final string.
12440 breakpoint++;
12441 pm_token_buffer_escape(parser, &token_buffer);
12442 token_buffer.cursor = breakpoint;
12443
12445 case '\n': {
12446 if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) {
12447 parser_flush_heredoc_end(parser);
12448 parser->current.end = breakpoint + 1;
12449 pm_token_buffer_flush(parser, &token_buffer);
12450 LEX(PM_TOKEN_STRING_CONTENT);
12451 }
12452
12453 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12454
12455 // If we have a - or ~ heredoc, then we can match after
12456 // some leading whitespace.
12457 const uint8_t *start = breakpoint + 1;
12458
12459 if (!was_line_continuation && (start + ident_length <= parser->end)) {
12460 // We want to match the terminator starting from the end of the line in case
12461 // there is whitespace in the ident such as <<-' DOC' or <<~' DOC'.
12462 const uint8_t *newline = next_newline(start, parser->end - start);
12463
12464 if (newline == NULL) {
12465 newline = parser->end;
12466 } else if (newline[-1] == '\r') {
12467 newline--; // Remove \r
12468 }
12469
12470 /*
12471 * The line on which we are looking for the
12472 * terminator runs from `start` to `newline`. If
12473 * that line is shorter than the identifier the
12474 * terminator cannot fit and we skip the match.
12475 * This also keeps `terminator_start` from pointing
12476 * before `start`.
12477 */
12478 if ((size_t) (newline - start) >= ident_length) {
12479 // Start of a possible terminator.
12480 const uint8_t *terminator_start = newline - ident_length;
12481
12482 // Cursor to check for the leading whitespace. We skip the
12483 // leading whitespace if we have a - or ~ heredoc.
12484 const uint8_t *cursor = start;
12485
12486 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12487 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12488 cursor++;
12489 }
12490 }
12491
12492 if (
12493 cursor == terminator_start &&
12494 (memcmp(terminator_start, ident_start, ident_length) == 0)
12495 ) {
12496 parser->current.end = breakpoint + 1;
12497 pm_token_buffer_flush(parser, &token_buffer);
12498 LEX(PM_TOKEN_STRING_CONTENT);
12499 }
12500 }
12501 }
12502
12503 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.base.indent);
12504
12505 // If we have hit a newline that is followed by a valid
12506 // terminator, then we need to return the content of the
12507 // heredoc here as string content. Then, the next time a
12508 // token is lexed, it will match again and return the
12509 // end of the heredoc.
12510 if (lex_mode->as.heredoc.base.indent == PM_HEREDOC_INDENT_TILDE) {
12511 if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
12512 *lex_mode->as.heredoc.common_whitespace = whitespace;
12513 }
12514
12515 parser->current.end = breakpoint + 1;
12516 pm_token_buffer_flush(parser, &token_buffer);
12517 LEX(PM_TOKEN_STRING_CONTENT);
12518 }
12519
12520 // Otherwise we hit a newline and it wasn't followed by
12521 // a terminator, so we can continue parsing.
12522 parser->current.end = breakpoint + 1;
12523 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12524 break;
12525 }
12526 case '\\': {
12527 // If we hit an escape, then we need to skip past
12528 // however many characters the escape takes up. However
12529 // it's important that if \n or \r\n are escaped, we
12530 // stop looping before the newline and not after the
12531 // newline so that we can still potentially find the
12532 // terminator of the heredoc.
12533 parser->current.end = breakpoint + 1;
12534
12535 // If we've hit the end of the file, then break out of
12536 // the loop by setting the breakpoint to NULL.
12537 if (parser->current.end == parser->end) {
12538 breakpoint = NULL;
12539 continue;
12540 }
12541
12542 pm_token_buffer_escape(parser, &token_buffer);
12543 uint8_t peeked = peek(parser);
12544
12545 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12546 switch (peeked) {
12547 case '\r':
12548 parser->current.end++;
12549 if (peek(parser) != '\n') {
12550 pm_token_buffer_push_byte(&token_buffer, '\\');
12551 pm_token_buffer_push_byte(&token_buffer, '\r');
12552 break;
12553 }
12555 case '\n':
12556 pm_token_buffer_push_byte(&token_buffer, '\\');
12557 pm_token_buffer_push_byte(&token_buffer, '\n');
12558 token_buffer.cursor = parser->current.end + 1;
12559 breakpoint = parser->current.end;
12560 continue;
12561 default:
12562 pm_token_buffer_push_byte(&token_buffer, '\\');
12563 pm_token_buffer_push_escaped(&token_buffer, parser);
12564 break;
12565 }
12566 } else {
12567 switch (peeked) {
12568 case '\r':
12569 parser->current.end++;
12570 if (peek(parser) != '\n') {
12571 pm_token_buffer_push_byte(&token_buffer, '\r');
12572 break;
12573 }
12575 case '\n':
12576 // If we are in a tilde here, we should
12577 // break out of the loop and return the
12578 // string content.
12579 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12580 const uint8_t *end = parser->current.end;
12581
12582 if (parser->heredoc_end == NULL) {
12583 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(end - parser->start + 1));
12584 }
12585
12586 // Here we want the buffer to only
12587 // include up to the backslash.
12588 parser->current.end = breakpoint;
12589 pm_token_buffer_flush(parser, &token_buffer);
12590
12591 // Now we can advance the end of the
12592 // token past the newline.
12593 parser->current.end = end + 1;
12594 lex_mode->as.heredoc.line_continuation = true;
12595 LEX(PM_TOKEN_STRING_CONTENT);
12596 }
12597
12598 was_line_continuation = true;
12599 token_buffer.cursor = parser->current.end + 1;
12600 breakpoint = parser->current.end;
12601 continue;
12602 default:
12603 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12604 break;
12605 }
12606 }
12607
12608 token_buffer.cursor = parser->current.end;
12609 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12610 break;
12611 }
12612 case '#': {
12613 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12614
12615 if (!type) {
12616 // If we haven't returned at this point then we had
12617 // something that looked like an interpolated class
12618 // or instance variable like "#@" but wasn't
12619 // actually. In this case we'll just skip to the
12620 // next breakpoint.
12621 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12622 break;
12623 }
12624
12625 if (type == PM_TOKEN_STRING_CONTENT) {
12626 pm_token_buffer_flush(parser, &token_buffer);
12627 }
12628
12629 LEX(type);
12630 }
12631 default:
12632 assert(false && "unreachable");
12633 }
12634
12635 was_line_continuation = false;
12636 }
12637
12638 if (parser->current.end > parser->current.start) {
12639 parser->current.end = parser->end;
12640 pm_token_buffer_flush(parser, &token_buffer);
12641 LEX(PM_TOKEN_STRING_CONTENT);
12642 }
12643
12644 // If we've hit the end of the string, then this is an unterminated
12645 // heredoc. In that case we'll return a string content token.
12646 parser->current.end = parser->end;
12647 pm_token_buffer_flush(parser, &token_buffer);
12648 LEX(PM_TOKEN_STRING_CONTENT);
12649 }
12650 }
12651
12652 assert(false && "unreachable");
12653}
12654
12655#undef LEX
12656
12657/******************************************************************************/
12658/* Parse functions */
12659/******************************************************************************/
12660
12669typedef enum {
12670 PM_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator
12671 PM_BINDING_POWER_STATEMENT = 2,
12672 PM_BINDING_POWER_MODIFIER_RESCUE = 4, // rescue
12673 PM_BINDING_POWER_MODIFIER = 6, // if unless until while
12674 PM_BINDING_POWER_COMPOSITION = 8, // and or
12675 PM_BINDING_POWER_NOT = 10, // not
12676 PM_BINDING_POWER_MATCH = 12, // => in
12677 PM_BINDING_POWER_DEFINED = 14, // defined?
12678 PM_BINDING_POWER_MULTI_ASSIGNMENT = 16, // =
12679 PM_BINDING_POWER_ASSIGNMENT = 18, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **=
12680 PM_BINDING_POWER_TERNARY = 20, // ?:
12681 PM_BINDING_POWER_RANGE = 22, // .. ...
12682 PM_BINDING_POWER_LOGICAL_OR = 24, // ||
12683 PM_BINDING_POWER_LOGICAL_AND = 26, // &&
12684 PM_BINDING_POWER_EQUALITY = 28, // <=> == === != =~ !~
12685 PM_BINDING_POWER_COMPARISON = 30, // > >= < <=
12686 PM_BINDING_POWER_BITWISE_OR = 32, // | ^
12687 PM_BINDING_POWER_BITWISE_AND = 34, // &
12688 PM_BINDING_POWER_SHIFT = 36, // << >>
12689 PM_BINDING_POWER_TERM = 38, // + -
12690 PM_BINDING_POWER_FACTOR = 40, // * / %
12691 PM_BINDING_POWER_UMINUS = 42, // -@
12692 PM_BINDING_POWER_EXPONENT = 44, // **
12693 PM_BINDING_POWER_UNARY = 46, // ! ~ +@
12694 PM_BINDING_POWER_INDEX = 48, // [] []=
12695 PM_BINDING_POWER_CALL = 50, // :: .
12696 PM_BINDING_POWER_MAX = 52
12697} pm_binding_power_t;
12698
12703typedef struct {
12705 pm_binding_power_t left;
12706
12708 pm_binding_power_t right;
12709
12712
12719
12720#define BINDING_POWER_ASSIGNMENT { PM_BINDING_POWER_UNARY, PM_BINDING_POWER_ASSIGNMENT, true, false }
12721#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, false }
12722#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true, false }
12723#define NON_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, true }
12724#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false }
12725
12726pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
12727 // rescue
12728 [PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = { PM_BINDING_POWER_MODIFIER_RESCUE, PM_BINDING_POWER_COMPOSITION, true, false },
12729
12730 // if unless until while
12731 [PM_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12732 [PM_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12733 [PM_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12734 [PM_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12735
12736 // and or
12737 [PM_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12738 [PM_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12739
12740 // => in
12741 [PM_TOKEN_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12742 [PM_TOKEN_KEYWORD_IN] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12743
12744 // &&= &= ^= = >>= <<= -= %= |= ||= += /= *= **=
12745 [PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12746 [PM_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12747 [PM_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT,
12748 [PM_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT,
12749 [PM_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT,
12750 [PM_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12751 [PM_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12752 [PM_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT,
12753 [PM_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12754 [PM_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12755 [PM_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12756 [PM_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT,
12757 [PM_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12758 [PM_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12759
12760 // ?:
12761 [PM_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_TERNARY),
12762
12763 // .. ...
12764 [PM_TOKEN_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12765 [PM_TOKEN_DOT_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12766 [PM_TOKEN_UDOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12767 [PM_TOKEN_UDOT_DOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12768
12769 // ||
12770 [PM_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_OR),
12771
12772 // &&
12773 [PM_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_AND),
12774
12775 // != !~ == === =~ <=>
12776 [PM_TOKEN_BANG_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12777 [PM_TOKEN_BANG_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12778 [PM_TOKEN_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12779 [PM_TOKEN_EQUAL_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12780 [PM_TOKEN_EQUAL_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12781 [PM_TOKEN_LESS_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12782
12783 // > >= < <=
12784 [PM_TOKEN_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12785 [PM_TOKEN_GREATER_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12786 [PM_TOKEN_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12787 [PM_TOKEN_LESS_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12788
12789 // ^ |
12790 [PM_TOKEN_CARET] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12791 [PM_TOKEN_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12792
12793 // &
12794 [PM_TOKEN_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_AND),
12795
12796 // >> <<
12797 [PM_TOKEN_GREATER_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12798 [PM_TOKEN_LESS_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12799
12800 // - +
12801 [PM_TOKEN_MINUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12802 [PM_TOKEN_PLUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12803
12804 // % / *
12805 [PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12806 [PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12807 [PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12808 [PM_TOKEN_USTAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_FACTOR),
12809
12810 // -@
12811 [PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS),
12812 [PM_TOKEN_UMINUS_NUM] = { PM_BINDING_POWER_UMINUS, PM_BINDING_POWER_MAX, false, false },
12813
12814 // **
12815 [PM_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_EXPONENT),
12816 [PM_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12817
12818 // ! ~ +@
12819 [PM_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12820 [PM_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12821 [PM_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12822
12823 // [
12824 [PM_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_INDEX),
12825
12826 // :: . &.
12827 [PM_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12828 [PM_TOKEN_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12829 [PM_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL)
12830};
12831
12832#undef BINDING_POWER_ASSIGNMENT
12833#undef LEFT_ASSOCIATIVE
12834#undef RIGHT_ASSOCIATIVE
12835#undef RIGHT_ASSOCIATIVE_UNARY
12836
12840static PRISM_INLINE bool
12841match1(const pm_parser_t *parser, pm_token_type_t type) {
12842 return parser->current.type == type;
12843}
12844
12848static PRISM_INLINE bool
12849match2(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12850 return match1(parser, type1) || match1(parser, type2);
12851}
12852
12856static PRISM_INLINE bool
12857match3(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) {
12858 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3);
12859}
12860
12864static PRISM_INLINE bool
12865match4(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) {
12866 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4);
12867}
12868
12872static PRISM_INLINE bool
12873match5(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) {
12874 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5);
12875}
12876
12880static PRISM_INLINE bool
12881match6(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) {
12882 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6);
12883}
12884
12888static PRISM_INLINE bool
12889match8(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) {
12890 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);
12891}
12892
12899static bool
12900accept1(pm_parser_t *parser, pm_token_type_t type) {
12901 if (match1(parser, type)) {
12902 parser_lex(parser);
12903 return true;
12904 }
12905 return false;
12906}
12907
12912static PRISM_INLINE bool
12913accept2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12914 if (match2(parser, type1, type2)) {
12915 parser_lex(parser);
12916 return true;
12917 }
12918 return false;
12919}
12920
12932static void
12933expect1(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id) {
12934 if (accept1(parser, type)) return;
12935
12936 const uint8_t *location = parser->previous.end;
12937 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12938
12939 parser->previous.start = location;
12940 parser->previous.type = 0;
12941}
12942
12947static void
12948expect2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_diagnostic_id_t diag_id) {
12949 if (accept2(parser, type1, type2)) return;
12950
12951 const uint8_t *location = parser->previous.end;
12952 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12953
12954 parser->previous.start = location;
12955 parser->previous.type = 0;
12956}
12957
12962static void
12963expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
12964 if (match1(parser, PM_TOKEN_HEREDOC_END)) {
12965 parser_lex(parser);
12966 } else {
12967 pm_parser_err_heredoc_term(parser, ident_start, ident_length);
12968 parser->previous.start = parser->previous.end;
12969 parser->previous.type = 0;
12970 }
12971}
12972
12979static void
12980expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id, const pm_token_t *opening) {
12981 if (accept1(parser, type)) return;
12982
12983 const uint8_t *start = opening->start;
12984 pm_parser_err(parser, U32(start - parser->start), U32(opening->end - start), diag_id);
12985
12986 parser->previous.start = parser->previous.end;
12987 parser->previous.type = 0;
12988}
12989
12991#define PM_PARSE_ACCEPTS_COMMAND_CALL ((uint8_t) 0x1)
12992#define PM_PARSE_ACCEPTS_LABEL ((uint8_t) 0x2)
12993#define PM_PARSE_ACCEPTS_DO_BLOCK ((uint8_t) 0x4)
12994#define PM_PARSE_IN_ENDLESS_DEF ((uint8_t) 0x8)
12995
13005#define PM_PARSE_ACCEPTS_STATEMENT ((uint8_t) 0x10)
13006
13018#define PM_PARSE_ACCEPTS_COMMAND_DEF ((uint8_t) 0x20)
13019
13020static pm_node_t *
13021parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
13022
13027static pm_node_t *
13028parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
13029 pm_node_t *node = parse_expression(parser, binding_power, flags, diag_id, depth);
13030 pm_assert_value_expression(parser, node);
13031 return node;
13032}
13033
13052static PRISM_INLINE bool
13053token_begins_expression_p(pm_token_type_t type) {
13054 switch (type) {
13055 case PM_TOKEN_EQUAL_GREATER:
13056 case PM_TOKEN_KEYWORD_IN:
13057 // We need to special case this because it is a binary operator that
13058 // should not be marked as beginning an expression.
13059 return false;
13060 case PM_TOKEN_BRACE_RIGHT:
13061 case PM_TOKEN_BRACKET_RIGHT:
13062 case PM_TOKEN_COLON:
13063 case PM_TOKEN_COMMA:
13064 case PM_TOKEN_EMBEXPR_END:
13065 case PM_TOKEN_EOF:
13066 case PM_TOKEN_LAMBDA_BEGIN:
13067 case PM_TOKEN_KEYWORD_DO:
13068 case PM_TOKEN_KEYWORD_DO_BLOCK:
13069 case PM_TOKEN_KEYWORD_DO_LAMBDA:
13070 case PM_TOKEN_KEYWORD_DO_LOOP:
13071 case PM_TOKEN_KEYWORD_END:
13072 case PM_TOKEN_KEYWORD_ELSE:
13073 case PM_TOKEN_KEYWORD_ELSIF:
13074 case PM_TOKEN_KEYWORD_ENSURE:
13075 case PM_TOKEN_KEYWORD_THEN:
13076 case PM_TOKEN_KEYWORD_RESCUE:
13077 case PM_TOKEN_KEYWORD_WHEN:
13078 case PM_TOKEN_NEWLINE:
13079 case PM_TOKEN_PARENTHESIS_RIGHT:
13080 case PM_TOKEN_SEMICOLON:
13081 // The reason we need this short-circuit is because we're using the
13082 // binding powers table to tell us if the subsequent token could
13083 // potentially be the start of an expression. If there _is_ a binding
13084 // power for one of these tokens, then we should remove it from this list
13085 // and let it be handled by the default case below.
13086 assert(pm_binding_powers[type].left == PM_BINDING_POWER_UNSET);
13087 return false;
13088 case PM_TOKEN_UAMPERSAND:
13089 // This is a special case because this unary operator cannot appear
13090 // as a general operator, it only appears in certain circumstances.
13091 return false;
13092 case PM_TOKEN_UCOLON_COLON:
13093 case PM_TOKEN_UMINUS:
13094 case PM_TOKEN_UMINUS_NUM:
13095 case PM_TOKEN_UPLUS:
13096 case PM_TOKEN_BANG:
13097 case PM_TOKEN_TILDE:
13098 case PM_TOKEN_UDOT_DOT:
13099 case PM_TOKEN_UDOT_DOT_DOT:
13100 // These unary tokens actually do have binding power associated with them
13101 // so that we can correctly place them into the precedence order. But we
13102 // want them to be marked as beginning an expression, so we need to
13103 // special case them here.
13104 return true;
13105 default:
13106 return pm_binding_powers[type].left == PM_BINDING_POWER_UNSET;
13107 }
13108}
13109
13125static PRISM_INLINE bool
13126token_begins_pattern_p(pm_token_type_t type) {
13127 return (
13128 token_begins_expression_p(type) ||
13129 type == PM_TOKEN_USTAR ||
13130 type == PM_TOKEN_USTAR_STAR ||
13131 type == PM_TOKEN_CARET
13132 );
13133}
13134
13142static PRISM_INLINE bool
13143token_begins_argument_p(pm_token_type_t type) {
13144 return (
13145 token_begins_expression_p(type) ||
13146 type == PM_TOKEN_UAMPERSAND ||
13147 type == PM_TOKEN_USTAR ||
13148 type == PM_TOKEN_USTAR_STAR
13149 );
13150}
13151
13156static pm_node_t *
13157parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
13158 if (accept1(parser, PM_TOKEN_USTAR)) {
13159 pm_token_t operator = parser->previous;
13160 pm_node_t *expression = parse_value_expression(parser, binding_power, (uint8_t) (flags & PM_PARSE_ACCEPTS_DO_BLOCK), PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
13161 return UP(pm_splat_node_create(parser, &operator, expression));
13162 }
13163
13164 return parse_value_expression(parser, binding_power, flags, diag_id, depth);
13165}
13166
13167static pm_visit_result_t
13168pm_node_unreference_each(const pm_node_t *node, void *data) {
13169 switch (PM_NODE_TYPE(node)) {
13170 /* When we are about to destroy a set of nodes that could potentially
13171 * contain block exits for the current scope, we need to check if they
13172 * are contained in the list of block exits and remove them if they are.
13173 */
13174 case PM_BREAK_NODE:
13175 case PM_NEXT_NODE:
13176 case PM_REDO_NODE: {
13177 pm_parser_t *parser = (pm_parser_t *) data;
13178 size_t index = 0;
13179
13180 while (index < parser->current_block_exits->size) {
13181 pm_node_t *block_exit = parser->current_block_exits->nodes[index];
13182
13183 if (block_exit == node) {
13184 if (index + 1 < parser->current_block_exits->size) {
13185 memmove(
13186 &parser->current_block_exits->nodes[index],
13187 &parser->current_block_exits->nodes[index + 1],
13188 (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *)
13189 );
13190 }
13191 parser->current_block_exits->size--;
13192
13193 /* Note descending here because these nodes could have
13194 * arguments that are themselves block exits. */
13195 return PM_VISIT_DESCEND;
13196 }
13197
13198 index++;
13199 }
13200
13201 return PM_VISIT_DESCEND;
13202 }
13203 /* When an implicit local variable is written to or targeted, it becomes
13204 * a regular, named local variable. This branch removes it from the list
13205 * of implicit parameters when that happens. */
13206 case PM_LOCAL_VARIABLE_READ_NODE:
13207 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13208 pm_parser_t *parser = (pm_parser_t *) data;
13209 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
13210
13211 for (size_t index = 0; index < implicit_parameters->size; index++) {
13212 if (implicit_parameters->nodes[index] == node) {
13213 /* If the node is not the last one in the list, we need to
13214 * shift the remaining nodes down to fill the gap. This is
13215 * extremely unlikely to happen. */
13216 if (index != implicit_parameters->size - 1) {
13217 memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *));
13218 }
13219
13220 implicit_parameters->size--;
13221 break;
13222 }
13223 }
13224
13225 return PM_VISIT_SKIP;
13226 }
13227 default:
13228 return PM_VISIT_DESCEND;
13229 }
13230}
13231
13237static void
13238pm_node_unreference(pm_parser_t *parser, const pm_node_t *node) {
13239 pm_visit_node(node, pm_node_unreference_each, parser);
13240}
13241
13246static void
13247parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) {
13248 // The method name needs to change. If we previously had
13249 // foo, we now need foo=. In this case we'll allocate a new
13250 // owned string, copy the previous method name in, and
13251 // append an =.
13252 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *name_field);
13253 size_t length = constant->length;
13254 uint8_t *name = (uint8_t *) pm_arena_alloc(parser->arena, length + 1, 1);
13255
13256 memcpy(name, constant->start, length);
13257 name[length] = '=';
13258
13259 *name_field = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, name, length + 1);
13260}
13261
13268static pm_node_t *
13269parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) {
13270 switch (PM_NODE_TYPE(target)) {
13271 case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13272 case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13273 case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13274 case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13275 case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13276 case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13277 case PM_TRUE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13278 default: break;
13279 }
13280
13281 pm_constant_id_t name = pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target));
13282 pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0);
13283
13284 return UP(result);
13285}
13286
13295static pm_node_t *
13296parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_parent) {
13297 switch (PM_NODE_TYPE(target)) {
13298 case PM_ERROR_RECOVERY_NODE:
13299 return target;
13300 case PM_SOURCE_ENCODING_NODE:
13301 case PM_FALSE_NODE:
13302 case PM_SOURCE_FILE_NODE:
13303 case PM_SOURCE_LINE_NODE:
13304 case PM_NIL_NODE:
13305 case PM_SELF_NODE:
13306 case PM_TRUE_NODE: {
13307 // In these special cases, we have specific error messages and we
13308 // will replace them with local variable writes.
13309 return parse_unwriteable_target(parser, target);
13310 }
13311 case PM_CLASS_VARIABLE_READ_NODE:
13313 target->type = PM_CLASS_VARIABLE_TARGET_NODE;
13314 return target;
13315 case PM_CONSTANT_PATH_NODE:
13316 if (context_def_p(parser)) {
13317 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13318 }
13319
13321 target->type = PM_CONSTANT_PATH_TARGET_NODE;
13322
13323 return target;
13324 case PM_CONSTANT_READ_NODE:
13325 if (context_def_p(parser)) {
13326 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13327 }
13328
13329 assert(sizeof(pm_constant_target_node_t) == sizeof(pm_constant_read_node_t));
13330 target->type = PM_CONSTANT_TARGET_NODE;
13331
13332 return target;
13333 case PM_BACK_REFERENCE_READ_NODE:
13334 case PM_NUMBERED_REFERENCE_READ_NODE:
13335 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13336 return UP(pm_error_recovery_node_create_unexpected(parser, target));
13337 case PM_GLOBAL_VARIABLE_READ_NODE:
13339 target->type = PM_GLOBAL_VARIABLE_TARGET_NODE;
13340 return target;
13341 case PM_LOCAL_VARIABLE_READ_NODE: {
13342 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13343 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(target));
13344 pm_node_unreference(parser, target);
13345 }
13346
13348 uint32_t name = cast->name;
13349 uint32_t depth = cast->depth;
13350 pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name);
13351
13353 target->type = PM_LOCAL_VARIABLE_TARGET_NODE;
13354
13355 return target;
13356 }
13357 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13358 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13359 pm_node_t *node = UP(pm_local_variable_target_node_create(parser, &target->location, name, 0));
13360
13361 pm_node_unreference(parser, target);
13362
13363 return node;
13364 }
13365 case PM_INSTANCE_VARIABLE_READ_NODE:
13367 target->type = PM_INSTANCE_VARIABLE_TARGET_NODE;
13368 return target;
13369 case PM_MULTI_TARGET_NODE:
13370 if (splat_parent) {
13371 // Multi target is not accepted in all positions. If this is one
13372 // of them, then we need to add an error.
13373 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13374 }
13375
13376 return target;
13377 case PM_SPLAT_NODE: {
13378 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13379
13380 if (splat->expression != NULL) {
13381 splat->expression = parse_target(parser, splat->expression, multiple, true);
13382 }
13383
13384 return UP(splat);
13385 }
13386 case PM_CALL_NODE: {
13387 pm_call_node_t *call = (pm_call_node_t *) target;
13388
13389 // If we have no arguments to the call node and we need this to be a
13390 // target then this is either a method call or a local variable
13391 // write.
13392 if (
13393 (call->message_loc.length > 0) &&
13394 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13395 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13396 (call->opening_loc.length == 0) &&
13397 (call->arguments == NULL) &&
13398 (call->block == NULL)
13399 ) {
13400 if (call->receiver == NULL) {
13401 // When we get here, we have a local variable write, because it
13402 // was previously marked as a method call but now we have an =.
13403 // This looks like:
13404 //
13405 // foo = 1
13406 //
13407 // When it was parsed in the prefix position, foo was seen as a
13408 // method call with no receiver and no arguments. Now we have an
13409 // =, so we know it's a local variable write.
13410 pm_location_t message_loc = call->message_loc;
13411 pm_constant_id_t name = pm_parser_local_add_location(parser, &message_loc, 0);
13412
13413 return UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0));
13414 }
13415
13416 if (peek_at(parser, parser->start + call->message_loc.start) == '_' || parser->encoding->alnum_char(parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)) {
13417 if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
13418 pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION);
13419 }
13420
13421 parse_write_name(parser, &call->name);
13422 return UP(pm_call_target_node_create(parser, call));
13423 }
13424 }
13425
13426 // If there is no call operator and the message is "[]" then this is
13427 // an aref expression, and we can transform it into an aset
13428 // expression.
13429 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13430 return UP(pm_index_target_node_create(parser, call));
13431 }
13432 }
13434 default:
13435 // In this case we have a node that we don't know how to convert
13436 // into a target. We need to treat it as an error. For now, we'll
13437 // mark it as an error and just skip right past it.
13438 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13439 return target;
13440 }
13441}
13442
13447static pm_node_t *
13448parse_target_validate(pm_parser_t *parser, pm_node_t *target, bool multiple) {
13449 pm_node_t *result = parse_target(parser, target, multiple, false);
13450
13451 // Ensure that we have one of an =, an 'in' in for indexes, and a ')' in
13452 // parens after the targets.
13453 if (
13454 !match1(parser, PM_TOKEN_EQUAL) &&
13455 !(context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) &&
13456 !(context_p(parser, PM_CONTEXT_PARENS) && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT))
13457 ) {
13458 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13459 }
13460
13461 return result;
13462}
13463
13468static pm_node_t *
13469parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) {
13470 pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser);
13471
13472 if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) {
13473 return UP(pm_shareable_constant_node_create(parser, write, shareable_constant));
13474 }
13475
13476 return write;
13477}
13478
13482static pm_node_t *
13483parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_node_t *value) {
13484 switch (PM_NODE_TYPE(target)) {
13485 case PM_ERROR_RECOVERY_NODE:
13486 return target;
13487 case PM_CLASS_VARIABLE_READ_NODE: {
13488 pm_class_variable_write_node_t *node = pm_class_variable_write_node_create(parser, (pm_class_variable_read_node_t *) target, operator, value);
13489 return UP(node);
13490 }
13491 case PM_CONSTANT_PATH_NODE: {
13492 pm_node_t *node = UP(pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value));
13493
13494 if (context_def_p(parser)) {
13495 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13496 }
13497
13498 return parse_shareable_constant_write(parser, node);
13499 }
13500 case PM_CONSTANT_READ_NODE: {
13501 pm_node_t *node = UP(pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value));
13502
13503 if (context_def_p(parser)) {
13504 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13505 }
13506
13507 return parse_shareable_constant_write(parser, node);
13508 }
13509 case PM_BACK_REFERENCE_READ_NODE:
13510 case PM_NUMBERED_REFERENCE_READ_NODE:
13511 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13513 case PM_GLOBAL_VARIABLE_READ_NODE: {
13514 pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value);
13515 return UP(node);
13516 }
13517 case PM_LOCAL_VARIABLE_READ_NODE: {
13519
13520 pm_location_t location = target->location;
13521 pm_constant_id_t name = local_read->name;
13522 uint32_t depth = local_read->depth;
13523 pm_scope_t *scope = pm_parser_scope_find(parser, depth);
13524
13525 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13526 pm_diagnostic_id_t diag_id = (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED;
13527 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), diag_id, parser->start + PM_NODE_START(target));
13528 pm_node_unreference(parser, target);
13529 }
13530
13531 pm_locals_unread(&scope->locals, name);
13532
13533 return UP(pm_local_variable_write_node_create(parser, name, depth, value, &location, operator));
13534 }
13535 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13536 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13537 pm_node_t *node = UP(pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator));
13538
13539 pm_node_unreference(parser, target);
13540
13541 return node;
13542 }
13543 case PM_INSTANCE_VARIABLE_READ_NODE: {
13544 pm_node_t *write_node = UP(pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value));
13545 return write_node;
13546 }
13547 case PM_MULTI_TARGET_NODE:
13548 return UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value));
13549 case PM_SPLAT_NODE: {
13550 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13551
13552 if (splat->expression != NULL) {
13553 splat->expression = parse_write(parser, splat->expression, operator, value);
13554 }
13555
13556 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
13557 pm_multi_target_node_targets_append(parser, multi_target, UP(splat));
13558
13559 return UP(pm_multi_write_node_create(parser, multi_target, operator, value));
13560 }
13561 case PM_CALL_NODE: {
13562 pm_call_node_t *call = (pm_call_node_t *) target;
13563
13564 // If we have no arguments to the call node and we need this to be a
13565 // target then this is either a method call or a local variable
13566 // write.
13567 if (
13568 (call->message_loc.length > 0) &&
13569 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13570 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13571 (call->opening_loc.length == 0) &&
13572 (call->arguments == NULL) &&
13573 (call->block == NULL)
13574 ) {
13575 if (call->receiver == NULL) {
13576 // When we get here, we have a local variable write, because it
13577 // was previously marked as a method call but now we have an =.
13578 // This looks like:
13579 //
13580 // foo = 1
13581 //
13582 // When it was parsed in the prefix position, foo was seen as a
13583 // method call with no receiver and no arguments. Now we have an
13584 // =, so we know it's a local variable write.
13585 pm_location_t message_loc = call->message_loc;
13586
13587 pm_refute_numbered_parameter(parser, message_loc.start, message_loc.length);
13588 pm_parser_local_add_location(parser, &message_loc, 0);
13589
13590 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, parser->start + PM_LOCATION_START(&message_loc), parser->start + PM_LOCATION_END(&message_loc));
13591 target = UP(pm_local_variable_write_node_create(parser, constant_id, 0, value, &message_loc, operator));
13592
13593 return target;
13594 }
13595
13596 if (char_is_identifier_start(parser, parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)) {
13597 // When we get here, we have a method call, because it was
13598 // previously marked as a method call but now we have an =. This
13599 // looks like:
13600 //
13601 // foo.bar = 1
13602 //
13603 // When it was parsed in the prefix position, foo.bar was seen as a
13604 // method call with no arguments. Now we have an =, so we know it's
13605 // a method call with an argument. In this case we will create the
13606 // arguments node, parse the argument, and add it to the list.
13607 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
13608 call->arguments = arguments;
13609
13610 pm_arguments_node_arguments_append(parser->arena, arguments, value);
13611 PM_NODE_LENGTH_SET_NODE(call, arguments);
13612 call->equal_loc = TOK2LOC(parser, operator);
13613
13614 parse_write_name(parser, &call->name);
13615 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13616
13617 return UP(call);
13618 }
13619 }
13620
13621 // If there is no call operator and the message is "[]" then this is
13622 // an aref expression, and we can transform it into an aset
13623 // expression.
13624 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13625 if (call->arguments == NULL) {
13626 call->arguments = pm_arguments_node_create(parser);
13627 }
13628
13629 pm_arguments_node_arguments_append(parser->arena, call->arguments, value);
13630 PM_NODE_LENGTH_SET_NODE(target, value);
13631
13632 // Replace the name with "[]=".
13633 call->name = pm_parser_constant_id_constant(parser, "[]=", 3);
13634 call->equal_loc = TOK2LOC(parser, operator);
13635
13636 // Ensure that the arguments for []= don't contain keywords
13637 pm_index_arguments_check(parser, call->arguments, call->block);
13638 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13639
13640 return target;
13641 }
13642
13643 // If there are arguments on the call node, then it can't be a
13644 // method call ending with = or a local variable write, so it must
13645 // be a syntax error. In this case we'll fall through to our default
13646 // handling. We need to free the value that we parsed because there
13647 // is no way for us to attach it to the tree at this point.
13648 //
13649 // Since it is possible for the value to contain an implicit
13650 // parameter somewhere in its subtree, we need to walk it and remove
13651 // any implicit parameters from the list of implicit parameters for
13652 // the current scope.
13653 pm_node_unreference(parser, value);
13654 }
13656 default:
13657 // In this case we have a node that we don't know how to convert into a
13658 // target. We need to treat it as an error. For now, we'll mark it as an
13659 // error and just skip right past it.
13660 pm_parser_err_token(parser, operator, PM_ERR_WRITE_TARGET_UNEXPECTED);
13661 return target;
13662 }
13663}
13664
13671static pm_node_t *
13672parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) {
13673 switch (PM_NODE_TYPE(target)) {
13674 case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13675 case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13676 case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13677 case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13678 case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13679 case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13680 case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13681 default: break;
13682 }
13683
13684 pm_constant_id_t name = pm_parser_local_add_location(parser, &target->location, 1);
13685 pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals);
13686
13687 return UP(result);
13688}
13689
13700static pm_node_t *
13701parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13702 bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE);
13703
13704 pm_multi_target_node_t *result = pm_multi_target_node_create(parser);
13705 pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target, true, false));
13706
13707 while (accept1(parser, PM_TOKEN_COMMA)) {
13708 if (accept1(parser, PM_TOKEN_USTAR)) {
13709 // Here we have a splat operator. It can have a name or be
13710 // anonymous. It can be the final target or be in the middle if
13711 // there haven't been any others yet.
13712 if (has_rest) {
13713 pm_parser_err_previous(parser, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
13714 }
13715
13716 pm_token_t star_operator = parser->previous;
13717 pm_node_t *name = NULL;
13718
13719 if (token_begins_expression_p(parser->current.type)) {
13720 name = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
13721 name = parse_target(parser, name, true, true);
13722 }
13723
13724 pm_node_t *splat = UP(pm_splat_node_create(parser, &star_operator, name));
13725 pm_multi_target_node_targets_append(parser, result, splat);
13726 has_rest = true;
13727 } else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
13728 context_push(parser, PM_CONTEXT_MULTI_TARGET);
13729 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13730 target = parse_target(parser, target, true, false);
13731
13732 pm_multi_target_node_targets_append(parser, result, target);
13733 context_pop(parser);
13734 } else if (token_begins_expression_p(parser->current.type)) {
13735 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13736 target = parse_target(parser, target, true, false);
13737
13738 pm_multi_target_node_targets_append(parser, result, target);
13739 } else if (!match1(parser, PM_TOKEN_EOF)) {
13740 // If we get here, then we have a trailing , in a multi target node.
13741 // We'll add an implicit rest node to represent this.
13742 pm_node_t *rest = UP(pm_implicit_rest_node_create(parser, &parser->previous));
13743 pm_multi_target_node_targets_append(parser, result, rest);
13744 break;
13745 }
13746 }
13747
13748 return UP(result);
13749}
13750
13755static pm_node_t *
13756parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13757 pm_node_t *result = parse_targets(parser, first_target, binding_power, depth);
13758
13759 // If we're inside parentheses, then we allow a newline before the
13760 // closing parenthesis or equals sign. Outside of parentheses, a newline
13761 // is not allowed (e.g., `a, b\n= 1, 2` is not valid).
13762 if (context_p(parser, PM_CONTEXT_PARENS) || context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
13763 accept1(parser, PM_TOKEN_NEWLINE);
13764 }
13765
13766 // Ensure that we have either an = or a ) after the targets.
13767 if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
13768 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13769 }
13770
13771 return result;
13772}
13773
13777static pm_statements_node_t *
13778parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
13779 // First, skip past any optional terminators that might be at the beginning
13780 // of the statements.
13781 while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE));
13782
13783 // If we have a terminator, then we can just return NULL.
13784 if (context_terminator(context, &parser->current)) return NULL;
13785
13786 pm_statements_node_t *statements = pm_statements_node_create(parser);
13787
13788 // At this point we know we have at least one statement, and that it
13789 // immediately follows the current token.
13790 context_push(parser, context);
13791
13792 while (true) {
13793 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
13794 pm_statements_node_body_append(parser, statements, node, true);
13795
13796 // If we're recovering from a syntax error, then we need to stop parsing
13797 // the statements now.
13798 if (parser->recovering) {
13799 // If this is the level of context where the recovery has happened,
13800 // then we can mark the parser as done recovering.
13801 if (context_terminator(context, &parser->current)) parser->recovering = false;
13802 break;
13803 }
13804
13805 // If we have a terminator, then we will parse all consecutive
13806 // terminators and then continue parsing the statements list.
13807 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
13808 // If we have a terminator, then we will continue parsing the
13809 // statements list.
13810 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13811 if (context_terminator(context, &parser->current)) break;
13812
13813 // Now we can continue parsing the list of statements.
13814 continue;
13815 }
13816
13817 // At this point we have a list of statements that are not terminated by
13818 // a newline or semicolon. At this point we need to check if we're at
13819 // the end of the statements list. If we are, then we should break out
13820 // of the loop.
13821 if (context_terminator(context, &parser->current)) break;
13822
13823 // At this point, we have a syntax error, because the statement was not
13824 // terminated by a newline or semicolon, and we're not at the end of the
13825 // statements list. Ideally we should scan forward to determine if we
13826 // should insert a missing terminator or break out of parsing the
13827 // statements list at this point.
13828 //
13829 // We don't have that yet, so instead we'll do a more naive approach. If
13830 // we were unable to parse an expression, then we will skip past this
13831 // token and continue parsing the statements list. Otherwise we'll add
13832 // an error and continue parsing the statements list.
13833 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) {
13834 parser_lex(parser);
13835
13836 // If we are at the end of the file, then we need to stop parsing
13837 // the statements entirely at this point. Mark the parser as
13838 // recovering, as we know that EOF closes the top-level context, and
13839 // then break out of the loop.
13840 if (match1(parser, PM_TOKEN_EOF)) {
13841 parser->recovering = true;
13842 break;
13843 }
13844
13845 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13846 if (context_terminator(context, &parser->current)) break;
13847 } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) {
13848 // This is an inlined version of accept1 because the error that we
13849 // want to add has varargs. If this happens again, we should
13850 // probably extract a helper function.
13851 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
13852 parser->previous.start = parser->previous.end;
13853 parser->previous.type = 0;
13854 }
13855 }
13856
13857 context_pop(parser);
13858
13859 bool last_value = true;
13860 switch (context) {
13861 case PM_CONTEXT_BEGIN_ENSURE:
13862 case PM_CONTEXT_DEF_ENSURE:
13863 last_value = false;
13864 break;
13865 default:
13866 break;
13867 }
13868 pm_void_statements_check(parser, statements, last_value);
13869
13870 return statements;
13871}
13872
13876static void
13877pm_hash_key_duplicated_warn(pm_parser_t *parser, const pm_node_t *duplicated, const pm_node_t *node) {
13878 pm_buffer_t buffer = { 0 };
13879 pm_static_literal_inspect(&buffer, parser, duplicated);
13880
13881 pm_diagnostic_list_append_format(
13882 &parser->metadata_arena,
13883 &parser->warning_list,
13884 duplicated->location.start,
13885 duplicated->location.length,
13886 PM_WARN_DUPLICATED_HASH_KEY,
13887 (int) pm_buffer_length(&buffer),
13888 pm_buffer_value(&buffer),
13889 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line
13890 );
13891
13892 pm_buffer_cleanup(&buffer);
13893}
13894
13899static void
13900pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13901 const pm_node_t *duplicated = pm_static_literals_add(parser, literals, node, true);
13902
13903 if (duplicated != NULL) {
13904 pm_hash_key_duplicated_warn(parser, duplicated, node);
13905 }
13906}
13907
13915static void
13916pm_hash_key_static_literals_merge(pm_parser_t *parser, pm_static_literals_t *literals, const pm_hash_node_t *hash, uint32_t boundary) {
13917 const pm_node_list_t *elements = &hash->elements;
13918
13919 for (size_t index = 0; index < elements->size; index++) {
13920 pm_node_t *element = elements->nodes[index];
13921
13922 switch (PM_NODE_TYPE(element)) {
13923 case PM_ASSOC_NODE: {
13924 pm_node_t *key = ((pm_assoc_node_t *) element)->key;
13925 const pm_node_t *duplicated = pm_static_literals_add(parser, literals, key, true);
13926
13927 if (duplicated != NULL && PM_NODE_START(duplicated) < boundary) {
13928 pm_hash_key_duplicated_warn(parser, duplicated, key);
13929 }
13930
13931 break;
13932 }
13933 case PM_ASSOC_SPLAT_NODE: {
13934 const pm_node_t *value = ((pm_assoc_splat_node_t *) element)->value;
13935
13936 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
13937 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, boundary);
13938 }
13939
13940 break;
13941 }
13942 default:
13943 break;
13944 }
13945 }
13946}
13947
13952static void
13953pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13954 pm_node_t *previous;
13955
13956 if ((previous = pm_static_literals_add(parser, literals, node, false)) != NULL) {
13957 pm_diagnostic_list_append_format(
13958 &parser->metadata_arena,
13959 &parser->warning_list,
13960 PM_NODE_START(node),
13961 PM_NODE_LENGTH(node),
13962 PM_WARN_DUPLICATED_WHEN_CLAUSE,
13963 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line,
13964 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(previous), parser->start_line).line
13965 );
13966 }
13967}
13968
13972static bool
13973parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node, uint16_t depth) {
13974 assert(PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE));
13975 bool contains_keyword_splat = false;
13976
13977 while (true) {
13978 pm_node_t *element;
13979
13980 switch (parser->current.type) {
13981 case PM_TOKEN_USTAR_STAR: {
13982 parser_lex(parser);
13983 pm_token_t operator = parser->previous;
13984 pm_node_t *value = NULL;
13985
13986 if (token_begins_expression_p(parser->current.type)) {
13987 value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH, (uint16_t) (depth + 1));
13988
13989 /* If the splatted value is itself a hash literal, its keys
13990 * become part of this hash for the duplicate key warning. */
13991 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
13992 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, PM_NODE_START(value));
13993 }
13994 } else {
13995 pm_parser_scope_forwarding_keywords_check(parser, &operator);
13996 }
13997
13998 element = UP(pm_assoc_splat_node_create(parser, value, &operator));
13999 contains_keyword_splat = true;
14000 break;
14001 }
14002 case PM_TOKEN_LABEL: {
14003 pm_token_t label = parser->current;
14004 parser_lex(parser);
14005
14006 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &label));
14007 pm_hash_key_static_literals_add(parser, literals, key);
14008
14009 pm_node_t *value = NULL;
14010
14011 if (token_begins_expression_p(parser->current.type)) {
14012 value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_EXPRESSION_AFTER_LABEL, (uint16_t) (depth + 1));
14013 } else {
14014 if (parser->encoding->isupper_char(label.start, (label.end - 1) - label.start)) {
14015 pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 };
14016 value = UP(pm_constant_read_node_create(parser, &constant));
14017 } else {
14018 int depth = -1;
14019 pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 };
14020
14021 if (identifier.end[-1] == '!' || identifier.end[-1] == '?') {
14022 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ);
14023 } else {
14024 depth = pm_parser_local_depth(parser, &identifier);
14025 }
14026
14027 if (depth == -1) {
14028 value = UP(pm_call_node_variable_call_create(parser, &identifier));
14029 } else {
14030 value = UP(pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth));
14031 }
14032 }
14033
14034 value->location.length++;
14035 value = UP(pm_implicit_node_create(parser, value));
14036 }
14037
14038 element = UP(pm_assoc_node_create(parser, key, NULL, value));
14039 break;
14040 }
14041 default: {
14042 pm_node_t *key = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK | PM_PARSE_ACCEPTS_LABEL, PM_ERR_HASH_KEY, (uint16_t) (depth + 1));
14043
14044 // Hash keys that are strings are automatically frozen. We will
14045 // mark that here.
14046 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
14047 pm_node_flag_set(key, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
14048 }
14049
14050 pm_hash_key_static_literals_add(parser, literals, key);
14051
14052 pm_token_t operator = { 0 };
14053 if (!pm_symbol_node_label_p(parser, key)) {
14054 expect1(parser, PM_TOKEN_EQUAL_GREATER, PM_ERR_HASH_ROCKET);
14055 operator = parser->previous;
14056 }
14057
14058 pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1));
14059 element = UP(pm_assoc_node_create(parser, key, NTOK2PTR(operator), value));
14060 break;
14061 }
14062 }
14063
14064 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
14065 pm_hash_node_elements_append(parser->arena, (pm_hash_node_t *) node, element);
14066 } else {
14067 pm_keyword_hash_node_elements_append(parser->arena, (pm_keyword_hash_node_t *) node, element);
14068 }
14069
14070 // If there's no comma after the element, then we're done.
14071 if (!accept1(parser, PM_TOKEN_COMMA)) break;
14072
14073 // If the next element starts with a label or a **, then we know we have
14074 // another element in the hash, so we'll continue parsing.
14075 if (match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)) continue;
14076
14077 // Otherwise we need to check if the subsequent token begins an expression.
14078 // If it does, then we'll continue parsing.
14079 if (token_begins_expression_p(parser->current.type)) continue;
14080
14081 // Otherwise by default we will exit out of this loop.
14082 break;
14083 }
14084
14085 return contains_keyword_splat;
14086}
14087
14088static PRISM_INLINE bool
14089argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) {
14090 if (pm_symbol_node_label_p(parser, argument)) {
14091 return true;
14092 }
14093
14094 switch (PM_NODE_TYPE(argument)) {
14095 case PM_CALL_NODE: {
14096 pm_call_node_t *cast = (pm_call_node_t *) argument;
14097 if (cast->opening_loc.length == 0 && cast->arguments != NULL) {
14098 if (PM_NODE_FLAG_P(cast->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS | PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
14099 return false;
14100 }
14101 if (cast->block != NULL) {
14102 return false;
14103 }
14104 }
14105 break;
14106 }
14107 default: break;
14108 }
14109 return accept1(parser, PM_TOKEN_EQUAL_GREATER);
14110}
14111
14115static PRISM_INLINE void
14116parse_arguments_append(pm_parser_t *parser, pm_arguments_t *arguments, pm_node_t *argument) {
14117 if (arguments->arguments == NULL) {
14118 arguments->arguments = pm_arguments_node_create(parser);
14119 }
14120
14121 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, argument);
14122}
14123
14128static PRISM_INLINE bool
14129pm_call_node_command_p(const pm_call_node_t *node) {
14130 return (
14131 (node->opening_loc.length == 0) &&
14132 (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) &&
14133 (node->arguments != NULL || node->block != NULL)
14134 );
14135}
14136
14146static bool
14147pm_constant_path_command_call_p(const pm_parser_t *parser, const pm_call_node_t *call) {
14148 return (
14149 call->receiver != NULL &&
14150 call->opening_loc.length == 0 &&
14151 call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE) &&
14152 call->call_operator_loc.length > 0 &&
14153 parser->start[call->call_operator_loc.start] == ':' &&
14154 call->message_loc.length > 0 &&
14155 parser->encoding->isupper_char(parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)
14156 );
14157}
14158
14164static bool
14165pm_command_call_value_p(const pm_parser_t *parser, const pm_node_t *node) {
14166 switch (PM_NODE_TYPE(node)) {
14167 case PM_CALL_NODE: {
14168 const pm_call_node_t *call = (const pm_call_node_t *) node;
14169
14170 /* Command-style calls (e.g., foo bar, obj.foo bar). Attribute
14171 * writes (e.g., a.b = 1) are not commands. */
14172 if (pm_call_node_command_p(call) && !PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE) && (call->receiver == NULL || call->call_operator_loc.length > 0)) {
14173 return true;
14174 }
14175
14176 /* A constant-path command with a brace block, e.g. `Foo::Bar { }`. */
14177 if (pm_constant_path_command_call_p(parser, call)) {
14178 return true;
14179 }
14180
14181 /* A `!` or `not` prefix wrapping a command call (e.g., `!foo bar`,
14182 * `not foo bar`) is also a command-call value. */
14183 if (call->receiver != NULL && call->arguments == NULL && call->opening_loc.length == 0 && call->call_operator_loc.length == 0) {
14184 return pm_command_call_value_p(parser, call->receiver);
14185 }
14186
14187 return false;
14188 }
14189 case PM_SUPER_NODE: {
14190 /* A command-style super (no parens). A super carrying a do-block is
14191 * a block call (it permits chaining), so it is excluded here and
14192 * handled by pm_block_call_p instead. */
14193 const pm_super_node_t *cast = (const pm_super_node_t *) node;
14194 return cast->lparen_loc.length == 0 &&
14195 (cast->arguments != NULL || cast->block != NULL) &&
14196 !(cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE));
14197 }
14198 case PM_YIELD_NODE: {
14199 const pm_yield_node_t *cast = (const pm_yield_node_t *) node;
14200 return cast->lparen_loc.length == 0 && cast->arguments != NULL;
14201 }
14202 case PM_RESCUE_MODIFIER_NODE:
14203 return pm_command_call_value_p(parser, ((const pm_rescue_modifier_node_t *) node)->expression);
14204 case PM_DEF_NODE: {
14205 const pm_def_node_t *cast = (const pm_def_node_t *) node;
14206 if (cast->equal_loc.length > 0 && cast->body != NULL) {
14207 const pm_node_t *body = cast->body;
14208 if (PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE)) {
14209 body = ((const pm_statements_node_t *) body)->body.nodes[((const pm_statements_node_t *) body)->body.size - 1];
14210 }
14211 return pm_command_call_value_p(parser, body);
14212 }
14213 return false;
14214 }
14215 default:
14216 return false;
14217 }
14218}
14219
14229static bool
14230pm_command_before_comma_p(const pm_parser_t *parser, const pm_node_t *node) {
14231 switch (PM_NODE_TYPE(node)) {
14232 case PM_CALL_NODE:
14233 return pm_constant_path_command_call_p(parser, (const pm_call_node_t *) node);
14234 case PM_DEF_NODE:
14235 return pm_command_call_value_p(parser, node);
14236 default:
14237 return false;
14238 }
14239}
14240
14247static bool
14248pm_block_call_p(const pm_node_t *node) {
14249 while (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
14250 const pm_call_node_t *call = (const pm_call_node_t *) node;
14251
14252 /* Root: a command (no parentheses) carrying command arguments and a
14253 * block (brace or do), e.g. `foo bar do end`, `foo bar { }`. The
14254 * no-parentheses requirement is what distinguishes a command root from
14255 * a method call root like `foo.bar(1) { }`, which is a primary value
14256 * and may be used as an argument.
14257 */
14258 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE)) {
14259 return true;
14260 }
14261
14262 /* Walk up the receiver chain of a `.`/`::`/`&.` call (e.g.,
14263 * `foo bar do end.baz(1)`). Parentheses on the chained call are allowed
14264 * here -- in parse.y a `block_call` can be extended by
14265 * `call_op2 operation2 opt_paren_args` and remains a block call.
14266 */
14267 if (call->call_operator_loc.length > 0 && call->receiver != NULL) {
14268 node = call->receiver;
14269 continue;
14270 }
14271
14272 return false;
14273 }
14274
14275 /* A `super` with command arguments and a do-block is also a block-call root
14276 * (parse.y: `command do_block`, where the command is `keyword_super
14277 * command_args`). `super do end` with no arguments is a forwarding super
14278 * (a primary value) and is handled elsewhere.
14279 */
14280 if (PM_NODE_TYPE_P(node, PM_SUPER_NODE)) {
14281 const pm_super_node_t *super = (const pm_super_node_t *) node;
14282 return super->lparen_loc.length == 0 && super->block != NULL && PM_NODE_TYPE_P(super->block, PM_BLOCK_NODE);
14283 }
14284
14285 return false;
14286}
14287
14291static void
14292parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator, uint8_t flags, uint16_t depth) {
14293 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
14294
14295 // First we need to check if the next token is one that could be the start
14296 // of an argument. If it's not, then we can just return.
14297 if (
14298 match2(parser, terminator, PM_TOKEN_EOF) ||
14299 (binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) ||
14300 context_terminator(parser->current_context->context, &parser->current)
14301 ) {
14302 return;
14303 }
14304
14305 bool parsed_first_argument = false;
14306 bool parsed_bare_hash = false;
14307 bool parsed_block_argument = false;
14308 bool parsed_forwarding_arguments = false;
14309
14310 while (!match1(parser, PM_TOKEN_EOF)) {
14311 if (parsed_forwarding_arguments) {
14312 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES);
14313 }
14314
14315 pm_node_t *argument = NULL;
14316
14317 switch (parser->current.type) {
14318 case PM_TOKEN_USTAR_STAR:
14319 case PM_TOKEN_LABEL: {
14320 if (parsed_bare_hash) {
14321 pm_parser_err_current(parser, PM_ERR_ARGUMENT_BARE_HASH);
14322 }
14323
14324 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
14325 argument = UP(hash);
14326
14327 pm_static_literals_t hash_keys = { 0 };
14328 bool contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1));
14329
14330 parse_arguments_append(parser, arguments, argument);
14331
14332 pm_node_flags_t node_flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14333 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14334 pm_node_flag_set(UP(arguments->arguments), node_flags);
14335
14336 pm_static_literals_free(&hash_keys);
14337 parsed_bare_hash = true;
14338
14339 break;
14340 }
14341 case PM_TOKEN_UAMPERSAND: {
14342 parser_lex(parser);
14343 pm_token_t operator = parser->previous;
14344 pm_node_t *expression = NULL;
14345
14346 if (token_begins_expression_p(parser->current.type)) {
14347 expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14348 } else {
14349 pm_parser_scope_forwarding_block_check(parser, &operator);
14350 }
14351
14352 argument = UP(pm_block_argument_node_create(parser, &operator, expression));
14353 if (parsed_block_argument) {
14354 parse_arguments_append(parser, arguments, argument);
14355 } else {
14356 arguments->block = argument;
14357 }
14358
14359 if (match1(parser, PM_TOKEN_COMMA)) {
14360 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK);
14361 }
14362
14363 parsed_block_argument = true;
14364 break;
14365 }
14366 case PM_TOKEN_USTAR: {
14367 parser_lex(parser);
14368 pm_token_t operator = parser->previous;
14369
14370 if (match4(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_SEMICOLON, PM_TOKEN_BRACKET_RIGHT)) {
14371 pm_parser_scope_forwarding_positionals_check(parser, &operator);
14372 argument = UP(pm_splat_node_create(parser, &operator, NULL));
14373 if (parsed_bare_hash) {
14374 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT);
14375 }
14376 } else {
14377 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_SPLAT, (uint16_t) (depth + 1));
14378
14379 if (parsed_bare_hash) {
14380 pm_parser_err(parser, PM_TOKEN_START(parser, &operator), PM_NODE_END(expression) - PM_TOKEN_START(parser, &operator), PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT);
14381 }
14382
14383 argument = UP(pm_splat_node_create(parser, &operator, expression));
14384 }
14385
14386 parse_arguments_append(parser, arguments, argument);
14387 break;
14388 }
14389 case PM_TOKEN_UDOT_DOT_DOT: {
14390 if (accepts_forwarding) {
14391 parser_lex(parser);
14392
14393 if (token_begins_expression_p(parser->current.type)) {
14394 // If the token begins an expression then this ... was
14395 // not actually argument forwarding but was instead a
14396 // range.
14397 pm_token_t operator = parser->previous;
14398 pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_RANGE, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
14399
14400 // If we parse a range, we need to validate that we
14401 // didn't accidentally violate the nonassoc rules of the
14402 // ... operator.
14403 if (PM_NODE_TYPE_P(right, PM_RANGE_NODE)) {
14404 pm_range_node_t *range = (pm_range_node_t *) right;
14405 pm_parser_err(parser, range->operator_loc.start, range->operator_loc.length, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
14406 }
14407
14408 argument = UP(pm_range_node_create(parser, NULL, &operator, right));
14409 } else {
14410 pm_parser_scope_forwarding_all_check(parser, &parser->previous);
14411 if (parsed_first_argument && terminator == PM_TOKEN_EOF) {
14412 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND);
14413 }
14414
14415 argument = UP(pm_forwarding_arguments_node_create(parser, &parser->previous));
14416 parse_arguments_append(parser, arguments, argument);
14417 pm_node_flag_set(UP(arguments->arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
14418 arguments->has_forwarding = true;
14419 parsed_forwarding_arguments = true;
14420 break;
14421 }
14422 }
14423 }
14425 default: {
14426 if (argument == NULL) {
14427 /* The first argument may be a command call, and since Ruby
14428 * 4.0 also an endless method with a command body
14429 * (`private def f = foo 1`), either of which must then be
14430 * the sole argument. */
14431 uint8_t command_flags = 0;
14432 if (!parsed_first_argument) {
14433 command_flags = PM_PARSE_ACCEPTS_COMMAND_CALL;
14434 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
14435 }
14436
14437 argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | command_flags | PM_PARSE_ACCEPTS_LABEL), PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14438 }
14439
14440 bool contains_keywords = false;
14441 bool contains_keyword_splat = false;
14442
14443 if (argument_allowed_for_bare_hash(parser, argument)) {
14444 if (parsed_bare_hash) {
14445 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH);
14446 }
14447
14448 /* A hash key must be an argument (`arg`). A command call or
14449 * block call (e.g. `Foo::Bar { } => v`, `foo bar do end =>
14450 * v`) is not an argument, so reject it as a key. Plain
14451 * command calls never reach here as a key because they
14452 * absorb the `=>` into their own arguments first.
14453 */
14454 if (pm_command_call_value_p(parser, argument) || pm_block_call_p(argument)) {
14455 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->previous.type));
14456 }
14457
14458 pm_token_t operator = { 0 };
14459 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
14460 operator = parser->previous;
14461 }
14462
14463 pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser);
14464 contains_keywords = true;
14465
14466 // Create the set of static literals for this hash.
14467 pm_static_literals_t hash_keys = { 0 };
14468 pm_hash_key_static_literals_add(parser, &hash_keys, argument);
14469
14470 // Finish parsing the one we are part way through.
14471 pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1));
14472 argument = UP(pm_assoc_node_create(parser, argument, NTOK2PTR(operator), value));
14473
14474 pm_keyword_hash_node_elements_append(parser->arena, bare_hash, argument);
14475 argument = UP(bare_hash);
14476
14477 // Then parse more if we have a comma
14478 if (accept1(parser, PM_TOKEN_COMMA) && (
14479 token_begins_expression_p(parser->current.type) ||
14480 match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)
14481 )) {
14482 contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(bare_hash), (uint16_t) (depth + 1));
14483 }
14484
14485 pm_static_literals_free(&hash_keys);
14486 parsed_bare_hash = true;
14487 }
14488
14489 parse_arguments_append(parser, arguments, argument);
14490
14491 pm_node_flags_t node_flags = 0;
14492 if (contains_keywords) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14493 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14494 pm_node_flag_set(UP(arguments->arguments), node_flags);
14495
14496 break;
14497 }
14498 }
14499
14500 parsed_first_argument = true;
14501
14502 // If parsing the argument failed, we need to stop parsing arguments.
14503 if (PM_NODE_TYPE_P(argument, PM_ERROR_RECOVERY_NODE) || parser->recovering) break;
14504
14505 // If the terminator of these arguments is not EOF, then we have a
14506 // specific token we're looking for. In that case we can accept a
14507 // newline here because it is not functioning as a statement terminator.
14508 bool accepted_newline = false;
14509 if (terminator != PM_TOKEN_EOF) {
14510 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14511 }
14512
14513 if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) {
14514 // If we previously were on a comma and we just parsed a bare hash,
14515 // then we want to continue parsing arguments. This is because the
14516 // comma was grabbed up by the hash parser.
14517 } else if (accept1(parser, PM_TOKEN_COMMA)) {
14518 // If there was a comma, then we need to check if we also accepted a
14519 // newline. If we did, then this is a syntax error.
14520 if (accepted_newline) {
14521 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14522 }
14523
14524 // If this is a command call and an argument takes a block,
14525 // there can be no further arguments. For example,
14526 // `foo(bar 1 do end, 2)` should be rejected.
14527 if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) {
14528 pm_call_node_t *call = (pm_call_node_t *) argument;
14529 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL) {
14530 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14531 break;
14532 }
14533 }
14534
14535 /* A command can only be the sole argument, as in
14536 * `foo Foo::Bar { }, y` or `foo def f = bar 1 rescue z, y`. */
14537 if (pm_command_before_comma_p(parser, argument)) {
14538 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14539 }
14540 } else {
14541 // If there is no comma at the end of the argument list then we're
14542 // done parsing arguments and can break out of this loop.
14543 break;
14544 }
14545
14546 // If we hit the terminator, then that means we have a trailing comma so
14547 // we can accept that output as well.
14548 if (match1(parser, terminator)) {
14549 // A forwarding `...` argument must be the last argument and cannot
14550 // be followed by a trailing comma, e.g. `foo(...,)`. A comma
14551 // followed by another argument is already rejected at the top of
14552 // this loop, so the only case left to reject here is the trailing
14553 // one.
14554 if (parsed_forwarding_arguments) {
14555 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14556 }
14557
14558 break;
14559 }
14560 }
14561}
14562
14574parse_required_destructured_parameter(pm_parser_t *parser) {
14575 expect1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER);
14576
14577 pm_multi_target_node_t *node = pm_multi_target_node_create(parser);
14578 pm_multi_target_node_opening_set(parser, node, &parser->previous);
14579
14580 do {
14581 pm_node_t *param;
14582
14583 // If we get here then we have a trailing comma, which isn't allowed in
14584 // the grammar. In other places, multi targets _do_ allow trailing
14585 // commas, so here we'll assume this is a mistake of the user not
14586 // knowing it's not allowed here.
14587 if (node->lefts.size > 0 && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
14588 param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14589 pm_multi_target_node_targets_append(parser, node, param);
14590 pm_parser_err_current(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14591 break;
14592 }
14593
14594 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
14595 param = UP(parse_required_destructured_parameter(parser));
14596 } else if (accept1(parser, PM_TOKEN_USTAR)) {
14597 pm_token_t star = parser->previous;
14598 pm_node_t *value = NULL;
14599
14600 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14601 pm_token_t name = parser->previous;
14602 value = UP(pm_required_parameter_node_create(parser, &name));
14603 if (pm_parser_parameter_name_check(parser, &name)) {
14604 pm_node_flag_set_repeated_parameter(value);
14605 }
14606 pm_parser_local_add_token(parser, &name, 1);
14607 }
14608
14609 param = UP(pm_splat_node_create(parser, &star, value));
14610 } else {
14611 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER);
14612 pm_token_t name = parser->previous;
14613
14614 param = UP(pm_required_parameter_node_create(parser, &name));
14615 if (pm_parser_parameter_name_check(parser, &name)) {
14616 pm_node_flag_set_repeated_parameter(param);
14617 }
14618 pm_parser_local_add_token(parser, &name, 1);
14619 }
14620
14621 pm_multi_target_node_targets_append(parser, node, param);
14622 } while (accept1(parser, PM_TOKEN_COMMA));
14623
14624 accept1(parser, PM_TOKEN_NEWLINE);
14625 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN_REQ_PARAMETER);
14626 pm_multi_target_node_closing_set(parser, node, &parser->previous);
14627
14628 return node;
14629}
14630
14635typedef enum {
14636 PM_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state
14637 PM_PARAMETERS_ORDER_NOTHING_AFTER = 1,
14638 PM_PARAMETERS_ORDER_KEYWORDS_REST,
14639 PM_PARAMETERS_ORDER_KEYWORDS,
14640 PM_PARAMETERS_ORDER_REST,
14641 PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14642 PM_PARAMETERS_ORDER_OPTIONAL,
14643 PM_PARAMETERS_ORDER_NAMED,
14644 PM_PARAMETERS_ORDER_NONE,
14645} pm_parameters_order_t;
14646
14650static pm_parameters_order_t parameters_ordering[PM_TOKEN_MAXIMUM] = {
14651 [0] = PM_PARAMETERS_NO_CHANGE,
14652 [PM_TOKEN_UAMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14653 [PM_TOKEN_AMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14654 [PM_TOKEN_UDOT_DOT_DOT] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14655 [PM_TOKEN_IDENTIFIER] = PM_PARAMETERS_ORDER_NAMED,
14656 [PM_TOKEN_PARENTHESIS_LEFT_GROUPING] = PM_PARAMETERS_ORDER_NAMED,
14657 [PM_TOKEN_EQUAL] = PM_PARAMETERS_ORDER_OPTIONAL,
14658 [PM_TOKEN_LABEL] = PM_PARAMETERS_ORDER_KEYWORDS,
14659 [PM_TOKEN_USTAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14660 [PM_TOKEN_STAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14661 [PM_TOKEN_USTAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST,
14662 [PM_TOKEN_STAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST
14663};
14664
14672static bool
14673update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_order_t *current) {
14674 pm_parameters_order_t state = parameters_ordering[token->type];
14675 if (state == PM_PARAMETERS_NO_CHANGE) return true;
14676
14677 // If we see another ordered argument after a optional argument
14678 // we only continue parsing ordered arguments until we stop seeing ordered arguments.
14679 if (*current == PM_PARAMETERS_ORDER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14680 *current = PM_PARAMETERS_ORDER_AFTER_OPTIONAL;
14681 return true;
14682 } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14683 return true;
14684 }
14685
14686 if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14687 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR);
14688 return false;
14689 } else if (token->type == PM_TOKEN_UDOT_DOT_DOT && (*current >= PM_PARAMETERS_ORDER_KEYWORDS_REST && *current <= PM_PARAMETERS_ORDER_AFTER_OPTIONAL)) {
14690 pm_parser_err_token(parser, token, *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL ? PM_ERR_PARAMETER_FORWARDING_AFTER_REST : PM_ERR_PARAMETER_ORDER);
14691 return false;
14692 } else if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) {
14693 // We know what transition we failed on, so we can provide a better error here.
14694 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_ORDER);
14695 return false;
14696 }
14697
14698 if (state < *current) *current = state;
14699 return true;
14700}
14701
14702static PRISM_INLINE void
14703parse_parameters_handle_trailing_comma(
14704 pm_parser_t *parser,
14705 pm_parameters_node_t *params,
14706 pm_parameters_order_t order,
14707 bool in_block,
14708 bool allows_trailing_comma
14709) {
14710 if (!allows_trailing_comma) {
14711 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14712 return;
14713 }
14714
14715 if (in_block) {
14716 if (order >= PM_PARAMETERS_ORDER_NAMED) {
14717 // foo do |bar,|; end
14718 pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14719
14720 if (params->rest == NULL) {
14721 pm_parameters_node_rest_set(params, param);
14722 } else {
14723 pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI);
14724 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14725 }
14726 } else {
14727 // foo do |*bar,|; end
14728 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14729 }
14730 } else {
14731 // https://bugs.ruby-lang.org/issues/19107
14732 // Allow `def foo(bar,); end`, `def foo(*bar,); end`, etc. but not `def foo(...,); end`
14733 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1 || order == PM_PARAMETERS_ORDER_NOTHING_AFTER) {
14734 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14735 }
14736 }
14737}
14738
14742static pm_parameters_node_t *
14743parse_parameters(
14744 pm_parser_t *parser,
14745 pm_binding_power_t binding_power,
14746 bool uses_parentheses,
14747 bool allows_trailing_comma,
14748 bool allows_forwarding_parameters,
14749 bool accepts_blocks_in_defaults,
14750 bool in_block,
14751 pm_diagnostic_id_t diag_id_forwarding,
14752 uint16_t depth
14753) {
14754 pm_do_loop_stack_push(parser, false);
14755
14756 pm_parameters_node_t *params = pm_parameters_node_create(parser);
14757 pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE;
14758
14759 while (true) {
14760 bool parsing = true;
14761
14762 switch (parser->current.type) {
14763 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
14764 update_parameter_state(parser, &parser->current, &order);
14765 pm_node_t *param = UP(parse_required_destructured_parameter(parser));
14766
14767 if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14768 pm_parameters_node_requireds_append(parser->arena, params, param);
14769 } else {
14770 pm_parameters_node_posts_append(parser->arena, params, param);
14771 }
14772 break;
14773 }
14774 case PM_TOKEN_UAMPERSAND:
14775 case PM_TOKEN_AMPERSAND: {
14776 update_parameter_state(parser, &parser->current, &order);
14777 parser_lex(parser);
14778
14779 pm_token_t operator = parser->previous;
14780 pm_node_t *param;
14781
14782 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1 && accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
14783 param = (pm_node_t *) pm_no_block_parameter_node_create(parser, &operator, &parser->previous);
14784 } else {
14785 pm_token_t name = {0};
14786
14787 bool repeated = false;
14788 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14789 name = parser->previous;
14790 repeated = pm_parser_parameter_name_check(parser, &name);
14791 pm_parser_local_add_token(parser, &name, 1);
14792 } else {
14793 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
14794 }
14795
14796 param = (pm_node_t *) pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator);
14797 if (repeated) {
14798 pm_node_flag_set_repeated_parameter(param);
14799 }
14800 }
14801
14802 if (params->block == NULL) {
14803 pm_parameters_node_block_set(params, param);
14804 } else {
14805 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_BLOCK_MULTI);
14806 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
14807 }
14808
14809 break;
14810 }
14811 case PM_TOKEN_UDOT_DOT_DOT: {
14812 if (!allows_forwarding_parameters) {
14813 pm_parser_err_current(parser, diag_id_forwarding);
14814 }
14815
14816 bool succeeded = update_parameter_state(parser, &parser->current, &order);
14817 parser_lex(parser);
14818
14819 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
14820 pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
14821
14822 if (params->keyword_rest != NULL) {
14823 // If we already have a keyword rest parameter, then we replace it with the
14824 // forwarding parameter and move the keyword rest parameter to the posts list.
14825 pm_node_t *keyword_rest = params->keyword_rest;
14826 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, keyword_rest)));
14827 if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD);
14828 params->keyword_rest = NULL;
14829 }
14830
14831 pm_parameters_node_keyword_rest_set(params, UP(param));
14832 break;
14833 }
14834 case PM_TOKEN_CLASS_VARIABLE:
14835 case PM_TOKEN_IDENTIFIER:
14836 case PM_TOKEN_CONSTANT:
14837 case PM_TOKEN_INSTANCE_VARIABLE:
14838 case PM_TOKEN_GLOBAL_VARIABLE:
14839 case PM_TOKEN_METHOD_NAME: {
14840 parser_lex(parser);
14841 switch (parser->previous.type) {
14842 case PM_TOKEN_CONSTANT:
14843 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14844 break;
14845 case PM_TOKEN_INSTANCE_VARIABLE:
14846 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
14847 break;
14848 case PM_TOKEN_GLOBAL_VARIABLE:
14849 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
14850 break;
14851 case PM_TOKEN_CLASS_VARIABLE:
14852 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
14853 break;
14854 case PM_TOKEN_METHOD_NAME:
14855 pm_parser_err_previous(parser, PM_ERR_PARAMETER_METHOD_NAME);
14856 break;
14857 default: break;
14858 }
14859
14860 if (parser->current.type == PM_TOKEN_EQUAL) {
14861 update_parameter_state(parser, &parser->current, &order);
14862 } else {
14863 update_parameter_state(parser, &parser->previous, &order);
14864 }
14865
14866 pm_token_t name = parser->previous;
14867 bool repeated = pm_parser_parameter_name_check(parser, &name);
14868 pm_parser_local_add_token(parser, &name, 1);
14869
14870 if (match1(parser, PM_TOKEN_EQUAL)) {
14871 pm_token_t operator = parser->current;
14872 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14873 parser_lex(parser);
14874
14875 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
14876 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14877
14878 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14879 pm_node_t *value = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1));
14880 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14881
14882 pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
14883
14884 if (repeated) {
14885 pm_node_flag_set_repeated_parameter(UP(param));
14886 }
14887 pm_parameters_node_optionals_append(parser->arena, params, param);
14888
14889 // If the value of the parameter increased the number of
14890 // reads of that parameter, then we need to warn that we
14891 // have a circular definition.
14892 if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14893 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &name, PM_ERR_PARAMETER_CIRCULAR);
14894 }
14895
14896 context_pop(parser);
14897
14898 // If parsing the value of the parameter resulted in error recovery,
14899 // then we can put a missing node in its place and stop parsing the
14900 // parameters entirely now.
14901 if (parser->recovering) {
14902 parsing = false;
14903 break;
14904 }
14905 } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14906 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14907 if (repeated) {
14908 pm_node_flag_set_repeated_parameter(UP(param));
14909 }
14910 pm_parameters_node_requireds_append(parser->arena, params, UP(param));
14911 } else {
14912 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14913 if (repeated) {
14914 pm_node_flag_set_repeated_parameter(UP(param));
14915 }
14916 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14917 }
14918
14919 break;
14920 }
14921 case PM_TOKEN_LABEL: {
14922 if (!uses_parentheses && !in_block) parser->in_keyword_arg = true;
14923 update_parameter_state(parser, &parser->current, &order);
14924
14925 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14926 parser_lex(parser);
14927
14928 pm_token_t name = parser->previous;
14929 pm_token_t local = name;
14930 local.end -= 1;
14931
14932 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)) {
14933 pm_parser_err(parser, PM_TOKEN_START(parser, &local), PM_TOKEN_LENGTH(&local), PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14934 } else if (local.end[-1] == '!' || local.end[-1] == '?') {
14935 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE);
14936 }
14937
14938 bool repeated = pm_parser_parameter_name_check(parser, &local);
14939 pm_parser_local_add_token(parser, &local, 1);
14940
14941 switch (parser->current.type) {
14942 case PM_TOKEN_COMMA:
14943 case PM_TOKEN_PARENTHESIS_RIGHT:
14944 case PM_TOKEN_PIPE: {
14945 context_pop(parser);
14946
14947 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14948 if (repeated) {
14949 pm_node_flag_set_repeated_parameter(param);
14950 }
14951
14952 pm_parameters_node_keywords_append(parser->arena, params, param);
14953 break;
14954 }
14955 case PM_TOKEN_SEMICOLON:
14956 case PM_TOKEN_NEWLINE: {
14957 context_pop(parser);
14958
14959 if (uses_parentheses) {
14960 parsing = false;
14961 break;
14962 }
14963
14964 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14965 if (repeated) {
14966 pm_node_flag_set_repeated_parameter(param);
14967 }
14968
14969 pm_parameters_node_keywords_append(parser->arena, params, param);
14970 break;
14971 }
14972 default: {
14973 pm_node_t *param;
14974
14975 if (token_begins_expression_p(parser->current.type)) {
14976 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
14977 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14978
14979 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14980 pm_node_t *value = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PARAMETER_NO_DEFAULT_KW, (uint16_t) (depth + 1));
14981 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14982
14983 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14984 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_PARAMETER_CIRCULAR);
14985 }
14986
14987 param = UP(pm_optional_keyword_parameter_node_create(parser, &name, value));
14988 }
14989 else {
14990 param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14991 }
14992
14993 if (repeated) {
14994 pm_node_flag_set_repeated_parameter(param);
14995 }
14996
14997 context_pop(parser);
14998 pm_parameters_node_keywords_append(parser->arena, params, param);
14999
15000 // If parsing the value of the parameter resulted in error recovery,
15001 // then we can put a missing node in its place and stop parsing the
15002 // parameters entirely now.
15003 if (parser->recovering) {
15004 parsing = false;
15005 break;
15006 }
15007 }
15008 }
15009
15010 parser->in_keyword_arg = false;
15011 break;
15012 }
15013 case PM_TOKEN_USTAR:
15014 case PM_TOKEN_STAR: {
15015 update_parameter_state(parser, &parser->current, &order);
15016 parser_lex(parser);
15017
15018 pm_token_t operator = parser->previous;
15019 pm_token_t name = { 0 };
15020 bool repeated = false;
15021
15022 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
15023 name = parser->previous;
15024 repeated = pm_parser_parameter_name_check(parser, &name);
15025 pm_parser_local_add_token(parser, &name, 1);
15026 } else {
15027 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
15028 }
15029
15030 pm_node_t *param = UP(pm_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
15031 if (repeated) {
15032 pm_node_flag_set_repeated_parameter(param);
15033 }
15034
15035 if (params->rest == NULL) {
15036 pm_parameters_node_rest_set(params, param);
15037 } else {
15038 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_SPLAT_MULTI);
15039 pm_parameters_node_posts_append(parser->arena, params, param);
15040 }
15041
15042 break;
15043 }
15044 case PM_TOKEN_STAR_STAR:
15045 case PM_TOKEN_USTAR_STAR: {
15046 pm_parameters_order_t previous_order = order;
15047 update_parameter_state(parser, &parser->current, &order);
15048 parser_lex(parser);
15049
15050 pm_token_t operator = parser->previous;
15051 pm_node_t *param;
15052
15053 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
15054 if (previous_order <= PM_PARAMETERS_ORDER_KEYWORDS) {
15055 pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW);
15056 }
15057
15058 param = UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
15059 } else {
15060 pm_token_t name = { 0 };
15061
15062 bool repeated = false;
15063 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
15064 name = parser->previous;
15065 repeated = pm_parser_parameter_name_check(parser, &name);
15066 pm_parser_local_add_token(parser, &name, 1);
15067 } else {
15068 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS;
15069 }
15070
15071 param = UP(pm_keyword_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
15072 if (repeated) {
15073 pm_node_flag_set_repeated_parameter(param);
15074 }
15075 }
15076
15077 if (params->keyword_rest == NULL) {
15078 pm_parameters_node_keyword_rest_set(params, param);
15079 } else {
15080 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI);
15081 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
15082 }
15083
15084 break;
15085 }
15086 default:
15087 if (parser->previous.type == PM_TOKEN_COMMA) {
15088 parse_parameters_handle_trailing_comma(parser, params, order, in_block, allows_trailing_comma);
15089 }
15090
15091 parsing = false;
15092 break;
15093 }
15094
15095 // If we hit some kind of issue while parsing the parameter, this would
15096 // have been set to false. In that case, we need to break out of the
15097 // loop.
15098 if (!parsing) break;
15099
15100 bool accepted_newline = false;
15101 if (uses_parentheses) {
15102 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
15103 }
15104
15105 if (accept1(parser, PM_TOKEN_COMMA)) {
15106 // If there was a comma, but we also accepted a newline, then this
15107 // is a syntax error.
15108 if (accepted_newline) {
15109 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
15110 }
15111 } else {
15112 // If there was no comma, then we're done parsing parameters.
15113 break;
15114 }
15115 }
15116
15117 pm_do_loop_stack_pop(parser);
15118
15119 // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`.
15120 if (PM_NODE_START(params) == PM_NODE_END(params)) {
15121 return NULL;
15122 }
15123
15124 return params;
15125}
15126
15131static size_t
15132token_newline_index(const pm_parser_t *parser) {
15133 if (parser->heredoc_end == NULL) {
15134 // This is the common case. In this case we can look at the previously
15135 // recorded newline in the newline list and subtract from the current
15136 // offset.
15137 return parser->line_offsets.size - 1;
15138 } else {
15139 // This is unlikely. This is the case that we have already parsed the
15140 // start of a heredoc, so we cannot rely on looking at the previous
15141 // offset of the newline list, and instead must go through the whole
15142 // process of a binary search for the line number.
15143 return (size_t) pm_line_offset_list_line(&parser->line_offsets, PM_TOKEN_START(parser, &parser->current), 0);
15144 }
15145}
15146
15151static int64_t
15152token_column(const pm_parser_t *parser, size_t newline_index, const pm_token_t *token, bool break_on_non_space) {
15153 const uint8_t *cursor = parser->start + parser->line_offsets.offsets[newline_index];
15154 const uint8_t *end = token->start;
15155
15156 // Skip over the BOM if it is present.
15157 if (
15158 newline_index == 0 &&
15159 parser->start[0] == 0xef &&
15160 parser->start[1] == 0xbb &&
15161 parser->start[2] == 0xbf
15162 ) cursor += 3;
15163
15164 int64_t column = 0;
15165 for (; cursor < end; cursor++) {
15166 switch (*cursor) {
15167 case '\t':
15168 column = ((column / PM_TAB_WHITESPACE_SIZE) + 1) * PM_TAB_WHITESPACE_SIZE;
15169 break;
15170 case ' ':
15171 column++;
15172 break;
15173 default:
15174 column++;
15175 if (break_on_non_space) return -1;
15176 break;
15177 }
15178 }
15179
15180 return column;
15181}
15182
15187static void
15188parser_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) {
15189 // If these warnings are disabled (unlikely), then we can just return.
15190 if (!parser->warn_mismatched_indentation) return;
15191
15192 // If the tokens are on the same line, we do not warn.
15193 size_t closing_newline_index = token_newline_index(parser);
15194 if (opening_newline_index == closing_newline_index) return;
15195
15196 // If the opening token has anything other than spaces or tabs before it,
15197 // then we do not warn. This is unless we are matching up an `if`/`end` pair
15198 // and the `if` immediately follows an `else` keyword.
15199 int64_t opening_column = token_column(parser, opening_newline_index, opening_token, !if_after_else);
15200 if (!if_after_else && (opening_column == -1)) return;
15201
15202 // Get a reference to the closing token off the current parser. This assumes
15203 // that the caller has placed this in the correct position.
15204 pm_token_t *closing_token = &parser->current;
15205
15206 // If the tokens are at the same indentation, we do not warn.
15207 int64_t closing_column = token_column(parser, closing_newline_index, closing_token, true);
15208 if ((closing_column == -1) || (opening_column == closing_column)) return;
15209
15210 // If the closing column is greater than the opening column and we are
15211 // allowing indentation, then we do not warn.
15212 if (allow_indent && (closing_column > opening_column)) return;
15213
15214 // Otherwise, add a warning.
15215 PM_PARSER_WARN_FORMAT(
15216 parser,
15217 PM_TOKEN_START(parser, closing_token),
15218 PM_TOKEN_LENGTH(closing_token),
15219 PM_WARN_INDENTATION_MISMATCH,
15220 (int) (closing_token->end - closing_token->start),
15221 (const char *) closing_token->start,
15222 (int) (opening_token->end - opening_token->start),
15223 (const char *) opening_token->start,
15224 ((int32_t) opening_newline_index) + parser->start_line
15225 );
15226}
15227
15228typedef enum {
15229 PM_RESCUES_BEGIN = 1,
15230 PM_RESCUES_BLOCK,
15231 PM_RESCUES_CLASS,
15232 PM_RESCUES_DEF,
15233 PM_RESCUES_LAMBDA,
15234 PM_RESCUES_MODULE,
15235 PM_RESCUES_SCLASS
15236} pm_rescues_type_t;
15237
15242static PRISM_INLINE void
15243parse_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) {
15244 pm_rescue_node_t *current = NULL;
15245
15246 while (match1(parser, PM_TOKEN_KEYWORD_RESCUE)) {
15247 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15248 parser_lex(parser);
15249
15250 pm_rescue_node_t *rescue = pm_rescue_node_create(parser, &parser->previous);
15251
15252 switch (parser->current.type) {
15253 case PM_TOKEN_EQUAL_GREATER: {
15254 // Here we have an immediate => after the rescue keyword, in which case
15255 // we're going to have an empty list of exceptions to rescue (which
15256 // implies StandardError).
15257 parser_lex(parser);
15258 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15259
15260 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15261 reference = parse_target(parser, reference, false, false);
15262
15263 pm_rescue_node_reference_set(rescue, reference);
15264 break;
15265 }
15266 case PM_TOKEN_NEWLINE:
15267 case PM_TOKEN_SEMICOLON:
15268 case PM_TOKEN_KEYWORD_THEN:
15269 // Here we have a terminator for the rescue keyword, in which
15270 // case we're going to just continue on.
15271 break;
15272 default: {
15273 if (token_begins_expression_p(parser->current.type) || match1(parser, PM_TOKEN_USTAR)) {
15274 // Here we have something that could be an exception expression, so
15275 // we'll attempt to parse it here and any others delimited by commas.
15276
15277 do {
15278 pm_node_t *expression = parse_starred_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_RESCUE_EXPRESSION, (uint16_t) (depth + 1));
15279 pm_rescue_node_exceptions_append(parser->arena, rescue, expression);
15280
15281 // If we hit a newline, then this is the end of the rescue expression. We
15282 // can continue on to parse the statements.
15283 if (match3(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_THEN)) break;
15284
15285 // If we hit a `=>` then we're going to parse the exception variable. Once
15286 // we've done that, we'll break out of the loop and parse the statements.
15287 if (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
15288 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15289
15290 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15291 reference = parse_target(parser, reference, false, false);
15292
15293 pm_rescue_node_reference_set(rescue, reference);
15294 break;
15295 }
15296 } while (accept1(parser, PM_TOKEN_COMMA));
15297 }
15298 }
15299 }
15300
15301 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
15302 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
15303 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15304 }
15305 } else {
15306 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM);
15307 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15308 }
15309
15310 if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
15311 pm_accepts_block_stack_push(parser, true);
15312 pm_context_t context;
15313
15314 switch (type) {
15315 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break;
15316 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break;
15317 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break;
15318 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break;
15319 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break;
15320 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break;
15321 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break;
15322 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15323 }
15324
15325 pm_statements_node_t *statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15326 if (statements != NULL) pm_rescue_node_statements_set(rescue, statements);
15327
15328 pm_accepts_block_stack_pop(parser);
15329 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15330 }
15331
15332 if (current == NULL) {
15333 pm_begin_node_rescue_clause_set(parent_node, rescue);
15334 } else {
15335 pm_rescue_node_subsequent_set(current, rescue);
15336 }
15337
15338 current = rescue;
15339 }
15340
15341 // The end node locations on rescue nodes will not be set correctly
15342 // since we won't know the end until we've found all subsequent
15343 // clauses. This sets the end location on all rescues once we know it.
15344 if (current != NULL) {
15345 pm_rescue_node_t *clause = parent_node->rescue_clause;
15346
15347 while (clause != NULL) {
15348 PM_NODE_LENGTH_SET_NODE(clause, current);
15349 clause = clause->subsequent;
15350 }
15351 }
15352
15353 pm_token_t else_keyword;
15354 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
15355 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15356 opening_newline_index = token_newline_index(parser);
15357
15358 else_keyword = parser->current;
15359 opening = &else_keyword;
15360
15361 parser_lex(parser);
15362 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15363
15364 pm_statements_node_t *else_statements = NULL;
15365 if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) {
15366 pm_accepts_block_stack_push(parser, true);
15367 pm_context_t context;
15368
15369 switch (type) {
15370 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break;
15371 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break;
15372 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break;
15373 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break;
15374 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break;
15375 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break;
15376 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break;
15377 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_ELSE; break;
15378 }
15379
15380 else_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15381 pm_accepts_block_stack_pop(parser);
15382
15383 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15384 }
15385
15386 pm_else_node_t *else_clause = pm_else_node_create(parser, &else_keyword, else_statements, &parser->current);
15387 pm_begin_node_else_clause_set(parent_node, else_clause);
15388
15389 // If we don't have a `current` rescue node, then this is a dangling
15390 // else, and it's an error.
15391 if (current == NULL) pm_parser_err_node(parser, UP(else_clause), PM_ERR_BEGIN_LONELY_ELSE);
15392 }
15393
15394 if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) {
15395 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15396 pm_token_t ensure_keyword = parser->current;
15397
15398 parser_lex(parser);
15399 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15400
15401 pm_statements_node_t *ensure_statements = NULL;
15402 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15403 pm_accepts_block_stack_push(parser, true);
15404 pm_context_t context;
15405
15406 switch (type) {
15407 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break;
15408 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break;
15409 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break;
15410 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break;
15411 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break;
15412 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break;
15413 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break;
15414 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15415 }
15416
15417 ensure_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15418 pm_accepts_block_stack_pop(parser);
15419
15420 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15421 }
15422
15423 pm_ensure_node_t *ensure_clause = pm_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current);
15424 pm_begin_node_ensure_clause_set(parent_node, ensure_clause);
15425 }
15426
15427 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
15428 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15429 pm_begin_node_end_keyword_set(parser, parent_node, &parser->current);
15430 } else {
15431 pm_token_t end_keyword = (pm_token_t) { .type = PM_TOKEN_KEYWORD_END, .start = parser->previous.end, .end = parser->previous.end };
15432 pm_begin_node_end_keyword_set(parser, parent_node, &end_keyword);
15433 }
15434}
15435
15440static pm_begin_node_t *
15441parse_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) {
15442 pm_begin_node_t *node = pm_begin_node_create(parser, NULL, statements);
15443 parse_rescues(parser, opening_newline_index, opening, node, type, (uint16_t) (depth + 1));
15444
15445 node->base.location.start = U32(start - parser->start);
15446 PM_NODE_LENGTH_SET_TOKEN(parser, node, &parser->current);
15447
15448 return node;
15449}
15450
15455parse_block_parameters(
15456 pm_parser_t *parser,
15457 bool allows_trailing_comma,
15458 const pm_token_t *opening,
15459 bool is_lambda_literal,
15460 bool accepts_blocks_in_defaults,
15461 uint16_t depth
15462) {
15463 pm_parameters_node_t *parameters = NULL;
15464 if (!match1(parser, PM_TOKEN_SEMICOLON)) {
15465 if (!is_lambda_literal) {
15466 context_push(parser, PM_CONTEXT_BLOCK_PARAMETERS);
15467 }
15468 parameters = parse_parameters(
15469 parser,
15470 is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX,
15471 false,
15472 allows_trailing_comma,
15473 false,
15474 accepts_blocks_in_defaults,
15475 true,
15476 is_lambda_literal ? PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA : PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK,
15477 (uint16_t) (depth + 1)
15478 );
15479 if (!is_lambda_literal) {
15480 context_pop(parser);
15481 }
15482 }
15483
15484 pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening);
15485 if (opening != NULL) {
15486 accept1(parser, PM_TOKEN_NEWLINE);
15487
15488 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
15489 do {
15490 switch (parser->current.type) {
15491 case PM_TOKEN_CONSTANT:
15492 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
15493 parser_lex(parser);
15494 break;
15495 case PM_TOKEN_INSTANCE_VARIABLE:
15496 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
15497 parser_lex(parser);
15498 break;
15499 case PM_TOKEN_GLOBAL_VARIABLE:
15500 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
15501 parser_lex(parser);
15502 break;
15503 case PM_TOKEN_CLASS_VARIABLE:
15504 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
15505 parser_lex(parser);
15506 break;
15507 default:
15508 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
15509 break;
15510 }
15511
15512 bool repeated = pm_parser_parameter_name_check(parser, &parser->previous);
15513 pm_parser_local_add_token(parser, &parser->previous, 1);
15514
15515 pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous);
15516 if (repeated) pm_node_flag_set_repeated_parameter(UP(local));
15517
15518 pm_block_parameters_node_append_local(parser->arena, block_parameters, local);
15519 } while (accept1(parser, PM_TOKEN_COMMA));
15520 }
15521 }
15522
15523 return block_parameters;
15524}
15525
15530static bool
15531outer_scope_using_numbered_parameters_p(pm_parser_t *parser) {
15532 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15533 if (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) return true;
15534 }
15535
15536 return false;
15537}
15538
15544static const char * const pm_numbered_parameter_names[] = {
15545 "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9"
15546};
15547
15553static pm_node_t *
15554parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_token_t *opening, const pm_token_t *closing) {
15555 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
15556
15557 // If we have ordinary parameters, then we will return them as the set of
15558 // parameters.
15559 if (parameters != NULL) {
15560 // If we also have implicit parameters, then this is an error.
15561 if (implicit_parameters->size > 0) {
15562 pm_node_t *node = implicit_parameters->nodes[0];
15563
15564 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15565 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_ORDINARY);
15566 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15567 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_ORDINARY);
15568 } else {
15569 assert(false && "unreachable");
15570 }
15571 }
15572
15573 return parameters;
15574 }
15575
15576 // If we don't have any implicit parameters, then the set of parameters is
15577 // NULL.
15578 if (implicit_parameters->size == 0) {
15579 return NULL;
15580 }
15581
15582 // If we don't have ordinary parameters, then we now must validate our set
15583 // of implicit parameters. We can only have numbered parameters or it, but
15584 // they cannot be mixed.
15585 uint8_t numbered_parameter = 0;
15586 bool it_parameter = false;
15587
15588 for (size_t index = 0; index < implicit_parameters->size; index++) {
15589 pm_node_t *node = implicit_parameters->nodes[index];
15590
15591 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15592 if (it_parameter) {
15593 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_IT);
15594 } else if (outer_scope_using_numbered_parameters_p(parser)) {
15595 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK);
15596 } else if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_INNER) {
15597 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK);
15598 } else if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
15599 numbered_parameter = MAX(numbered_parameter, (uint8_t) (parser->start[node->location.start + 1] - '0'));
15600 } else {
15601 assert(false && "unreachable");
15602 }
15603 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15604 if (numbered_parameter > 0) {
15605 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_NUMBERED);
15606 } else {
15607 it_parameter = true;
15608 }
15609 }
15610 }
15611
15612 if (numbered_parameter > 0) {
15613 // Go through the parent scopes and mark them as being disallowed from
15614 // using numbered parameters because this inner scope is using them.
15615 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15616 scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_INNER;
15617 }
15618 return UP(pm_numbered_parameters_node_create(parser, opening, closing, numbered_parameter));
15619 }
15620
15621 if (it_parameter) {
15622 return UP(pm_it_parameters_node_create(parser, opening, closing));
15623 }
15624
15625 return NULL;
15626}
15627
15631static pm_block_node_t *
15632parse_block(pm_parser_t *parser, uint16_t depth) {
15633 pm_token_t opening = parser->previous;
15634 accept1(parser, PM_TOKEN_NEWLINE);
15635
15636 /* A brace block is delimited by `{`/`}`, whose block-accepting frame is
15637 * managed by the lexer. A `do`/`end` block is delimited by keywords, so we
15638 * push the frame here (covering the block parameters and body) and pop it
15639 * before consuming `end`, mirroring parse.y's `do_body` rule. */
15640 bool do_block = opening.type != PM_TOKEN_BRACE_LEFT && opening.type != PM_TOKEN_BRACE_LEFT_ARGUMENT;
15641 if (do_block) pm_accepts_block_stack_push(parser, true);
15642 pm_parser_scope_push(parser, false);
15643
15644 pm_block_parameters_node_t *block_parameters = NULL;
15645
15646 if (accept1(parser, PM_TOKEN_PIPE)) {
15647 pm_token_t block_parameters_opening = parser->previous;
15648 if (match1(parser, PM_TOKEN_PIPE)) {
15649 block_parameters = pm_block_parameters_node_create(parser, NULL, &block_parameters_opening);
15650 parser->command_start = true;
15651 parser_lex(parser);
15652 } else {
15653 block_parameters = parse_block_parameters(parser, true, &block_parameters_opening, false, true, (uint16_t) (depth + 1));
15654 accept1(parser, PM_TOKEN_NEWLINE);
15655 parser->command_start = true;
15656 expect1(parser, PM_TOKEN_PIPE, PM_ERR_BLOCK_PARAM_PIPE_TERM);
15657 }
15658
15659 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
15660 }
15661
15662 accept1(parser, PM_TOKEN_NEWLINE);
15663 pm_node_t *statements = NULL;
15664
15665 if (!do_block) {
15666 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
15667 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1)));
15668 }
15669
15670 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening);
15671 } else {
15672 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15673 if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) {
15674 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS, (uint16_t) (depth + 1)));
15675 }
15676
15677 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
15678 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
15679 statements = UP(parse_rescues_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1)));
15680 }
15681 }
15682
15683 /* Pop the `do`/`end` frame before consuming `end` so the token
15684 * following the block is lexed in the enclosing context. */
15685 pm_accepts_block_stack_pop(parser);
15686 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening);
15687 }
15688
15689 pm_constant_id_list_t locals;
15690 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
15691 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &opening, &parser->previous);
15692
15693 pm_parser_scope_pop(parser);
15694 return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous);
15695}
15696
15708static bool
15709parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_arguments, uint8_t flags, uint16_t depth) {
15710 /* Fast path: if the current token can't begin an expression and isn't
15711 * a parenthesis, block opener, or splat/block-pass operator, there are
15712 * no arguments to parse. */
15713 if (
15714 !token_begins_expression_p(parser->current.type) &&
15715 !match6(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_KEYWORD_DO_BLOCK, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)
15716 ) {
15717 return false;
15718 }
15719
15720 bool found = false;
15721 bool parsed_command_args = false;
15722
15723 if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
15724 found |= true;
15725 arguments->opening_loc = TOK2LOC(parser, &parser->previous);
15726
15727 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15728 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15729 } else {
15730 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_PARENTHESIS_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
15731
15732 // `yield` parses its arguments through the restricted `call_args`
15733 // grammar, which (unlike the `opt_call_args` that method calls and
15734 // `super` use) permits neither a block argument nor a trailing
15735 // comma. `full_arguments` is false only for `yield`, so we use it
15736 // to reject the trailing comma in `yield(a,)` that the arguments
15737 // parser otherwise accepts before the closing parenthesis.
15738 if (!full_arguments && parser->previous.type == PM_TOKEN_COMMA) {
15739 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15740 }
15741
15742 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15743 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_str(parser->current.type));
15744 parser->previous.start = parser->previous.end;
15745 parser->previous.type = 0;
15746 }
15747
15748 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15749 }
15750 } else if ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
15751 found |= true;
15752 parsed_command_args = true;
15753
15754 /* The command-args frame does not accept blocks, so that a trailing
15755 * `do` binds to this command rather than to an argument. Mirroring
15756 * parse.y's `command_args` rule: when the first argument begins with an
15757 * opening delimiter, the lexer has already pushed that delimiter's
15758 * (block-accepting) frame. We must push the command-args frame beneath
15759 * it, so pop the delimiter frame, push the command-args frame, and then
15760 * restore the delimiter frame on top (the delimiter's closing token
15761 * will pop it back off during argument parsing). */
15762 bool lookahead_delimiter = match5(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES, PM_TOKEN_BRACKET_LEFT, PM_TOKEN_BRACKET_LEFT_ARRAY);
15763 if (lookahead_delimiter) pm_accepts_block_stack_pop(parser);
15764 pm_accepts_block_stack_push(parser, false);
15765 if (lookahead_delimiter) pm_accepts_block_stack_push(parser, true);
15766
15767 // If we get here, then the subsequent token cannot be used as an infix
15768 // operator. In this case we assume the subsequent token is part of an
15769 // argument to this method call.
15770 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
15771
15772 // If we have done with the arguments and still not consumed the comma,
15773 // then we have a trailing comma where we need to check whether it is
15774 // allowed or not.
15775 if (parser->previous.type == PM_TOKEN_COMMA && !match1(parser, PM_TOKEN_SEMICOLON)) {
15776 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15777 }
15778
15779 /* Symmetrically, if the command arguments are followed by a brace block
15780 * (`m args { }`), the lexer has already pushed that block's frame. Pop
15781 * it, pop the command-args frame beneath it, and restore the block
15782 * frame so the block's `}` still pops it. This mirrors the `tLBRACE_ARG`
15783 * lookahead handling in parse.y's `command_args` rule. */
15784 bool lookahead_brace = match2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT);
15785 if (lookahead_brace) pm_accepts_block_stack_pop(parser);
15786 pm_accepts_block_stack_pop(parser);
15787 if (lookahead_brace) pm_accepts_block_stack_push(parser, true);
15788 }
15789
15790 // If we're at the end of the arguments, we can now check if there is a block
15791 // node that starts with a {. If there is, then we can parse it and add it to
15792 // the arguments.
15793 if (full_arguments) {
15794 pm_block_node_t *block = NULL;
15795
15796 if (accept2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT)) {
15797 found |= true;
15798 block = parse_block(parser, (uint16_t) (depth + 1));
15799 pm_arguments_validate_block(parser, arguments, block);
15800 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
15801 found |= true;
15802 block = parse_block(parser, (uint16_t) (depth + 1));
15803 } else if (parsed_command_args && pm_accepts_block_stack_p(parser) && (flags & PM_PARSE_ACCEPTS_DO_BLOCK) && accept1(parser, PM_TOKEN_KEYWORD_DO_BLOCK)) {
15804 found |= true;
15805 block = parse_block(parser, (uint16_t) (depth + 1));
15806 }
15807
15808 if (block != NULL) {
15809 if (arguments->block == NULL && !arguments->has_forwarding) {
15810 arguments->block = UP(block);
15811 } else {
15812 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_BLOCK_MULTI);
15813
15814 if (arguments->block != NULL) {
15815 if (arguments->arguments == NULL) {
15816 arguments->arguments = pm_arguments_node_create(parser);
15817 }
15818 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, arguments->block);
15819 }
15820 arguments->block = UP(block);
15821 }
15822 }
15823 }
15824
15825 return found;
15826}
15827
15832static void
15833ensure_valid_return(pm_parser_t *parser, pm_node_t *node) {
15834 bool in_sclass = false;
15835 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15836 switch (context_node->context) {
15837 case PM_CONTEXT_BEGIN_ELSE:
15838 case PM_CONTEXT_BEGIN_ENSURE:
15839 case PM_CONTEXT_BEGIN_RESCUE:
15840 case PM_CONTEXT_BEGIN:
15841 case PM_CONTEXT_CASE_IN:
15842 case PM_CONTEXT_CASE_WHEN:
15843 case PM_CONTEXT_DEFAULT_PARAMS:
15844 case PM_CONTEXT_DEFINED:
15845 case PM_CONTEXT_ELSE:
15846 case PM_CONTEXT_ELSIF:
15847 case PM_CONTEXT_EMBEXPR:
15848 case PM_CONTEXT_FOR_INDEX:
15849 case PM_CONTEXT_FOR:
15850 case PM_CONTEXT_IF:
15851 case PM_CONTEXT_LOOP_PREDICATE:
15852 case PM_CONTEXT_MAIN:
15853 case PM_CONTEXT_MULTI_TARGET:
15854 case PM_CONTEXT_PARENS:
15855 case PM_CONTEXT_POSTEXE:
15856 case PM_CONTEXT_PREDICATE:
15857 case PM_CONTEXT_PREEXE:
15858 case PM_CONTEXT_RESCUE_MODIFIER:
15859 case PM_CONTEXT_TERNARY:
15860 case PM_CONTEXT_UNLESS:
15861 case PM_CONTEXT_UNTIL:
15862 case PM_CONTEXT_WHILE:
15863 // Keep iterating up the lists of contexts, because returns can
15864 // see through these.
15865 continue;
15866 case PM_CONTEXT_SCLASS_ELSE:
15867 case PM_CONTEXT_SCLASS_ENSURE:
15868 case PM_CONTEXT_SCLASS_RESCUE:
15869 case PM_CONTEXT_SCLASS:
15870 in_sclass = true;
15871 continue;
15872 case PM_CONTEXT_CLASS_ELSE:
15873 case PM_CONTEXT_CLASS_ENSURE:
15874 case PM_CONTEXT_CLASS_RESCUE:
15875 case PM_CONTEXT_CLASS:
15876 case PM_CONTEXT_MODULE_ELSE:
15877 case PM_CONTEXT_MODULE_ENSURE:
15878 case PM_CONTEXT_MODULE_RESCUE:
15879 case PM_CONTEXT_MODULE:
15880 // These contexts are invalid for a return.
15881 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15882 return;
15883 case PM_CONTEXT_BLOCK_BRACES:
15884 case PM_CONTEXT_BLOCK_ELSE:
15885 case PM_CONTEXT_BLOCK_ENSURE:
15886 case PM_CONTEXT_BLOCK_KEYWORDS:
15887 case PM_CONTEXT_BLOCK_RESCUE:
15888 case PM_CONTEXT_BLOCK_PARAMETERS:
15889 case PM_CONTEXT_DEF_ELSE:
15890 case PM_CONTEXT_DEF_ENSURE:
15891 case PM_CONTEXT_DEF_PARAMS:
15892 case PM_CONTEXT_DEF_RESCUE:
15893 case PM_CONTEXT_DEF:
15894 case PM_CONTEXT_LAMBDA_BRACES:
15895 case PM_CONTEXT_LAMBDA_DO_END:
15896 case PM_CONTEXT_LAMBDA_ELSE:
15897 case PM_CONTEXT_LAMBDA_ENSURE:
15898 case PM_CONTEXT_LAMBDA_RESCUE:
15899 // These contexts are valid for a return, and we should not
15900 // continue to loop.
15901 return;
15902 case PM_CONTEXT_NONE:
15903 case PM_CONTEXT_MAXIMUM:
15904 // This case should never happen.
15905 assert(false && "unreachable");
15906 break;
15907 }
15908 }
15909 if (in_sclass && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
15910 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15911 }
15912}
15913
15918static void
15919ensure_valid_jump(pm_parser_t *parser, pm_node_t *node) {
15920 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15921 switch (context_node->context) {
15922 case PM_CONTEXT_BLOCK_BRACES:
15923 case PM_CONTEXT_BLOCK_KEYWORDS:
15924 case PM_CONTEXT_BLOCK_ELSE:
15925 case PM_CONTEXT_BLOCK_ENSURE:
15926 case PM_CONTEXT_BLOCK_PARAMETERS:
15927 case PM_CONTEXT_BLOCK_RESCUE:
15928 case PM_CONTEXT_DEFINED:
15929 case PM_CONTEXT_FOR:
15930 case PM_CONTEXT_LAMBDA_BRACES:
15931 case PM_CONTEXT_LAMBDA_DO_END:
15932 case PM_CONTEXT_LAMBDA_ELSE:
15933 case PM_CONTEXT_LAMBDA_ENSURE:
15934 case PM_CONTEXT_LAMBDA_RESCUE:
15935 case PM_CONTEXT_LOOP_PREDICATE:
15936 case PM_CONTEXT_UNTIL:
15937 case PM_CONTEXT_WHILE:
15938 // These are the good cases. We're allowed to have a block exit
15939 // in these contexts.
15940 return;
15941 case PM_CONTEXT_POSTEXE:
15942 // https://bugs.ruby-lang.org/issues/20409
15943 if (context_node->context == PM_CONTEXT_POSTEXE) {
15944 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
15945 return;
15946 }
15947 }
15949 case PM_CONTEXT_DEF:
15950 case PM_CONTEXT_DEF_PARAMS:
15951 case PM_CONTEXT_DEF_ELSE:
15952 case PM_CONTEXT_DEF_ENSURE:
15953 case PM_CONTEXT_DEF_RESCUE:
15954 case PM_CONTEXT_MAIN:
15955 case PM_CONTEXT_PREEXE:
15956 case PM_CONTEXT_SCLASS:
15957 case PM_CONTEXT_SCLASS_ELSE:
15958 case PM_CONTEXT_SCLASS_ENSURE:
15959 case PM_CONTEXT_SCLASS_RESCUE:
15960 // These are the bad cases. We're not allowed to have a block
15961 // exit in these contexts.
15962 //
15963 // If we get here, then we're about to mark this block exit
15964 // as invalid. However, it could later _become_ valid if we
15965 // find a trailing while/until on the expression. In this
15966 // case instead of adding the error here, we'll add the
15967 // block exit to the list of exits for the expression, and
15968 // the node parsing will handle validating it instead.
15969 assert(parser->current_block_exits != NULL);
15970 pm_node_list_append_inline(&parser->scratch_arena, parser->current_block_exits, node);
15971 return;
15972 case PM_CONTEXT_BEGIN_ELSE:
15973 case PM_CONTEXT_BEGIN_ENSURE:
15974 case PM_CONTEXT_BEGIN_RESCUE:
15975 case PM_CONTEXT_BEGIN:
15976 case PM_CONTEXT_CASE_IN:
15977 case PM_CONTEXT_CASE_WHEN:
15978 case PM_CONTEXT_CLASS_ELSE:
15979 case PM_CONTEXT_CLASS_ENSURE:
15980 case PM_CONTEXT_CLASS_RESCUE:
15981 case PM_CONTEXT_CLASS:
15982 case PM_CONTEXT_DEFAULT_PARAMS:
15983 case PM_CONTEXT_ELSE:
15984 case PM_CONTEXT_ELSIF:
15985 case PM_CONTEXT_EMBEXPR:
15986 case PM_CONTEXT_FOR_INDEX:
15987 case PM_CONTEXT_IF:
15988 case PM_CONTEXT_MODULE_ELSE:
15989 case PM_CONTEXT_MODULE_ENSURE:
15990 case PM_CONTEXT_MODULE_RESCUE:
15991 case PM_CONTEXT_MODULE:
15992 case PM_CONTEXT_MULTI_TARGET:
15993 case PM_CONTEXT_PARENS:
15994 case PM_CONTEXT_PREDICATE:
15995 case PM_CONTEXT_RESCUE_MODIFIER:
15996 case PM_CONTEXT_TERNARY:
15997 case PM_CONTEXT_UNLESS:
15998 // In these contexts we should continue walking up the list of
15999 // contexts.
16000 break;
16001 case PM_CONTEXT_NONE:
16002 case PM_CONTEXT_MAXIMUM:
16003 // This case should never happen.
16004 assert(false && "unreachable");
16005 break;
16006 }
16007 }
16008}
16009
16014static pm_node_list_t *
16015push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
16016 pm_node_list_t *previous_block_exits = parser->current_block_exits;
16017 parser->current_block_exits = current_block_exits;
16018 return previous_block_exits;
16019}
16020
16026static void
16027flush_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
16028 pm_node_t *block_exit;
16029 PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
16030 const char *type;
16031
16032 switch (PM_NODE_TYPE(block_exit)) {
16033 case PM_BREAK_NODE: type = "break"; break;
16034 case PM_NEXT_NODE: type = "next"; break;
16035 case PM_REDO_NODE: type = "redo"; break;
16036 default: assert(false && "unreachable"); type = ""; break;
16037 }
16038
16039 PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
16040 }
16041
16042 parser->current_block_exits = previous_block_exits;
16043}
16044
16049static void
16050pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
16051 if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) {
16052 // If we matched a trailing while/until, then all of the block exits in
16053 // the contained list are valid. In this case we do not need to do
16054 // anything.
16055 parser->current_block_exits = previous_block_exits;
16056 } else if (previous_block_exits != NULL) {
16057 // If we did not matching a trailing while/until, then all of the block
16058 // exits contained in the list are invalid for this specific context.
16059 // However, they could still become valid in a higher level context if
16060 // there is another list above this one. In this case we'll push all of
16061 // the block exits up to the previous list.
16062 pm_node_list_concat(&parser->scratch_arena, previous_block_exits, parser->current_block_exits);
16063 parser->current_block_exits = previous_block_exits;
16064 } else {
16065 // If we did not match a trailing while/until and this was the last
16066 // chance to do so, then all of the block exits in the list are invalid
16067 // and we need to add an error for each of them.
16068 flush_block_exits(parser, previous_block_exits);
16069 }
16070}
16071
16072static PRISM_INLINE pm_node_t *
16073parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword, uint16_t depth) {
16074 context_push(parser, PM_CONTEXT_PREDICATE);
16075 pm_diagnostic_id_t error_id = context == PM_CONTEXT_IF ? PM_ERR_CONDITIONAL_IF_PREDICATE : PM_ERR_CONDITIONAL_UNLESS_PREDICATE;
16076 pm_node_t *predicate = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, error_id, (uint16_t) (depth + 1));
16077
16078 // Predicates are closed by a term, a "then", or a term and then a "then".
16079 bool predicate_closed = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16080
16081 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
16082 predicate_closed = true;
16083 *then_keyword = parser->previous;
16084 }
16085
16086 if (!predicate_closed) {
16087 pm_parser_err_current(parser, PM_ERR_CONDITIONAL_PREDICATE_TERM);
16088 }
16089
16090 context_pop(parser);
16091 return predicate;
16092}
16093
16094static PRISM_INLINE pm_node_t *
16095parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newline_index, bool if_after_else, uint16_t depth) {
16096 pm_node_list_t current_block_exits = { 0 };
16097 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
16098
16099 pm_token_t keyword = parser->previous;
16100 pm_token_t then_keyword = { 0 };
16101
16102 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, context, &then_keyword, (uint16_t) (depth + 1));
16103 pm_statements_node_t *statements = NULL;
16104
16105 if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
16106 pm_accepts_block_stack_push(parser, true);
16107 statements = parse_statements(parser, context, (uint16_t) (depth + 1));
16108 pm_accepts_block_stack_pop(parser);
16109 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16110 }
16111
16112 pm_node_t *parent = NULL;
16113
16114 switch (context) {
16115 case PM_CONTEXT_IF:
16116 parent = UP(pm_if_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
16117 break;
16118 case PM_CONTEXT_UNLESS:
16119 parent = UP(pm_unless_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements));
16120 break;
16121 default:
16122 assert(false && "unreachable");
16123 break;
16124 }
16125
16126 pm_node_t *current = parent;
16127
16128 // Parse any number of elsif clauses. This will form a linked list of if
16129 // nodes pointing to each other from the top.
16130 if (context == PM_CONTEXT_IF) {
16131 while (match1(parser, PM_TOKEN_KEYWORD_ELSIF)) {
16132 if (parser_end_of_line_p(parser)) {
16133 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
16134 }
16135
16136 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
16137 pm_token_t elsif_keyword = parser->current;
16138 parser_lex(parser);
16139
16140 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1));
16141 pm_accepts_block_stack_push(parser, true);
16142
16143 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1));
16144 pm_accepts_block_stack_pop(parser);
16145 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16146
16147 pm_node_t *elsif = UP(pm_if_node_create(parser, &elsif_keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
16148 ((pm_if_node_t *) current)->subsequent = elsif;
16149 current = elsif;
16150 }
16151 }
16152
16153 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
16154 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
16155 opening_newline_index = token_newline_index(parser);
16156
16157 parser_lex(parser);
16158 pm_token_t else_keyword = parser->previous;
16159
16160 pm_accepts_block_stack_push(parser, true);
16161 pm_statements_node_t *else_statements = parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1));
16162 pm_accepts_block_stack_pop(parser);
16163
16164 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16165 parser_warn_indentation_mismatch(parser, opening_newline_index, &else_keyword, false, false);
16166 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE, &keyword);
16167
16168 pm_else_node_t *else_node = pm_else_node_create(parser, &else_keyword, else_statements, &parser->previous);
16169
16170 switch (context) {
16171 case PM_CONTEXT_IF:
16172 ((pm_if_node_t *) current)->subsequent = UP(else_node);
16173 break;
16174 case PM_CONTEXT_UNLESS:
16175 ((pm_unless_node_t *) parent)->else_clause = else_node;
16176 break;
16177 default:
16178 assert(false && "unreachable");
16179 break;
16180 }
16181 } else {
16182 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false);
16183 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM, &keyword);
16184 }
16185
16186 // Set the appropriate end location for all of the nodes in the subtree.
16187 switch (context) {
16188 case PM_CONTEXT_IF: {
16189 pm_node_t *current = parent;
16190 bool recursing = true;
16191
16192 while (recursing) {
16193 switch (PM_NODE_TYPE(current)) {
16194 case PM_IF_NODE:
16195 pm_if_node_end_keyword_loc_set(parser, (pm_if_node_t *) current, &parser->previous);
16196 current = ((pm_if_node_t *) current)->subsequent;
16197 recursing = current != NULL;
16198 break;
16199 case PM_ELSE_NODE:
16200 pm_else_node_end_keyword_loc_set(parser, (pm_else_node_t *) current, &parser->previous);
16201 recursing = false;
16202 break;
16203 default: {
16204 recursing = false;
16205 break;
16206 }
16207 }
16208 }
16209 break;
16210 }
16211 case PM_CONTEXT_UNLESS:
16212 pm_unless_node_end_keyword_loc_set(parser, (pm_unless_node_t *) parent, &parser->previous);
16213 break;
16214 default:
16215 assert(false && "unreachable");
16216 break;
16217 }
16218
16219 pop_block_exits(parser, previous_block_exits);
16220 return parent;
16221}
16222
16227#define PM_CASE_KEYWORD PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16228 case PM_TOKEN_KEYWORD_ALIAS: case PM_TOKEN_KEYWORD_AND: case PM_TOKEN_KEYWORD_BEGIN: case PM_TOKEN_KEYWORD_BEGIN_UPCASE: \
16229 case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_CASE: case PM_TOKEN_KEYWORD_CLASS: case PM_TOKEN_KEYWORD_DEF: \
16230 case PM_TOKEN_KEYWORD_DEFINED: case PM_TOKEN_KEYWORD_DO: case PM_TOKEN_KEYWORD_DO_BLOCK: case PM_TOKEN_KEYWORD_DO_LAMBDA: case PM_TOKEN_KEYWORD_DO_LOOP: case PM_TOKEN_KEYWORD_ELSE: \
16231 case PM_TOKEN_KEYWORD_ELSIF: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_END_UPCASE: case PM_TOKEN_KEYWORD_ENSURE: \
16232 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD_FOR: case PM_TOKEN_KEYWORD_IF: case PM_TOKEN_KEYWORD_IN: \
16233 case PM_TOKEN_KEYWORD_MODULE: case PM_TOKEN_KEYWORD_NEXT: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_NOT: \
16234 case PM_TOKEN_KEYWORD_OR: case PM_TOKEN_KEYWORD_REDO: case PM_TOKEN_KEYWORD_RESCUE: case PM_TOKEN_KEYWORD_RETRY: \
16235 case PM_TOKEN_KEYWORD_RETURN: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_SUPER: case PM_TOKEN_KEYWORD_THEN: \
16236 case PM_TOKEN_KEYWORD_TRUE: case PM_TOKEN_KEYWORD_UNDEF: case PM_TOKEN_KEYWORD_UNLESS: case PM_TOKEN_KEYWORD_UNTIL: \
16237 case PM_TOKEN_KEYWORD_WHEN: case PM_TOKEN_KEYWORD_WHILE: case PM_TOKEN_KEYWORD_YIELD
16238
16243#define PM_CASE_OPERATOR PM_TOKEN_AMPERSAND: case PM_TOKEN_BACKTICK: case PM_TOKEN_BANG_EQUAL: \
16244 case PM_TOKEN_BANG_TILDE: case PM_TOKEN_BANG: case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \
16245 case PM_TOKEN_BRACKET_LEFT_RIGHT: case PM_TOKEN_CARET: case PM_TOKEN_EQUAL_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL: \
16246 case PM_TOKEN_EQUAL_TILDE: case PM_TOKEN_GREATER_EQUAL: case PM_TOKEN_GREATER_GREATER: case PM_TOKEN_GREATER: \
16247 case PM_TOKEN_LESS_EQUAL_GREATER: case PM_TOKEN_LESS_EQUAL: case PM_TOKEN_LESS_LESS: case PM_TOKEN_LESS: \
16248 case PM_TOKEN_MINUS: case PM_TOKEN_PERCENT: case PM_TOKEN_PIPE: case PM_TOKEN_PLUS: case PM_TOKEN_SLASH: \
16249 case PM_TOKEN_STAR_STAR: case PM_TOKEN_STAR: case PM_TOKEN_TILDE: case PM_TOKEN_UAMPERSAND: case PM_TOKEN_UMINUS: \
16250 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_UPLUS: case PM_TOKEN_USTAR: case PM_TOKEN_USTAR_STAR
16251
16257#define PM_CASE_PRIMITIVE PM_TOKEN_INTEGER: case PM_TOKEN_INTEGER_IMAGINARY: case PM_TOKEN_INTEGER_RATIONAL: \
16258 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: case PM_TOKEN_FLOAT: case PM_TOKEN_FLOAT_IMAGINARY: \
16259 case PM_TOKEN_FLOAT_RATIONAL: case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: case PM_TOKEN_SYMBOL_BEGIN: \
16260 case PM_TOKEN_REGEXP_BEGIN: case PM_TOKEN_XSTRING_BEGIN: case PM_TOKEN_PERCENT_LOWER_X: case PM_TOKEN_PERCENT_LOWER_I: \
16261 case PM_TOKEN_PERCENT_LOWER_W: case PM_TOKEN_PERCENT_UPPER_I: case PM_TOKEN_PERCENT_UPPER_W: \
16262 case PM_TOKEN_STRING_BEGIN: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_TRUE: \
16263 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16264 case PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_MINUS_GREATER: case PM_TOKEN_HEREDOC_START: \
16265 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_CHARACTER_LITERAL
16266
16271#define PM_CASE_PARAMETER PM_TOKEN_UAMPERSAND: case PM_TOKEN_AMPERSAND: case PM_TOKEN_UDOT_DOT_DOT: \
16272 case PM_TOKEN_IDENTIFIER: case PM_TOKEN_LABEL: case PM_TOKEN_USTAR: case PM_TOKEN_STAR: case PM_TOKEN_STAR_STAR: \
16273 case PM_TOKEN_USTAR_STAR: case PM_TOKEN_CONSTANT: case PM_TOKEN_INSTANCE_VARIABLE: case PM_TOKEN_GLOBAL_VARIABLE: \
16274 case PM_TOKEN_CLASS_VARIABLE
16275
16280#define PM_CASE_WRITABLE PM_CLASS_VARIABLE_READ_NODE: case PM_CONSTANT_PATH_NODE: \
16281 case PM_CONSTANT_READ_NODE: case PM_GLOBAL_VARIABLE_READ_NODE: case PM_LOCAL_VARIABLE_READ_NODE: \
16282 case PM_INSTANCE_VARIABLE_READ_NODE: case PM_MULTI_TARGET_NODE: case PM_BACK_REFERENCE_READ_NODE: \
16283 case PM_NUMBERED_REFERENCE_READ_NODE: case PM_IT_LOCAL_VARIABLE_READ_NODE
16284
16285// Assert here that the flags are the same so that we can safely switch the type
16286// of the node without having to move the flags.
16287PM_STATIC_ASSERT(__LINE__, ((int) PM_STRING_FLAGS_FORCED_UTF8_ENCODING) == ((int) PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING), "Expected the flags to match.");
16288
16293static PRISM_INLINE pm_node_flags_t
16294parse_unescaped_encoding(const pm_parser_t *parser, const pm_encoding_t *explicit_encoding) {
16295 if (explicit_encoding != NULL) {
16296 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
16297 // If the there's an explicit encoding and it's using a UTF-8 escape
16298 // sequence, then mark the string as UTF-8.
16299 return PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
16300 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
16301 // If there's a non-UTF-8 escape sequence being used, then the
16302 // string uses the source encoding, unless the source is marked as
16303 // US-ASCII. In that case the string is forced as ASCII-8BIT in
16304 // order to keep the string valid.
16305 return PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
16306 }
16307 }
16308 return 0;
16309}
16310
16315static pm_node_t *
16316parse_string_part(pm_parser_t *parser, uint16_t depth) {
16317 switch (parser->current.type) {
16318 // Here the lexer has returned to us plain string content. In this case
16319 // we'll create a string node that has no opening or closing and return that
16320 // as the part. These kinds of parts look like:
16321 //
16322 // "aaa #{bbb} #@ccc ddd"
16323 // ^^^^ ^ ^^^^
16324 case PM_TOKEN_STRING_CONTENT: {
16325 pm_node_t *node = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16326 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
16327
16328 parser_lex(parser);
16329 return node;
16330 }
16331 // Here the lexer has returned the beginning of an embedded expression. In
16332 // that case we'll parse the inner statements and return that as the part.
16333 // These kinds of parts look like:
16334 //
16335 // "aaa #{bbb} #@ccc ddd"
16336 // ^^^^^^
16337 case PM_TOKEN_EMBEXPR_BEGIN: {
16338 // Ruby disallows seeing encoding around interpolation in strings,
16339 // even though it is known at parse time.
16340 parser->explicit_encoding = NULL;
16341
16342 pm_lex_state_t state = parser->lex_state;
16343 int brace_nesting = parser->brace_nesting;
16344
16345 parser->brace_nesting = 0;
16346 lex_state_set(parser, PM_LEX_STATE_BEG);
16347 parser_lex(parser);
16348
16349 pm_token_t opening = parser->previous;
16350 pm_statements_node_t *statements = NULL;
16351
16352 if (!match3(parser, PM_TOKEN_EMBEXPR_END, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
16353 statements = parse_statements(parser, PM_CONTEXT_EMBEXPR, (uint16_t) (depth + 1));
16354 }
16355
16356 parser->brace_nesting = brace_nesting;
16357 lex_state_set(parser, state);
16358 expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END);
16359
16360 // If this set of embedded statements only contains a single
16361 // statement, then Ruby does not consider it as a possible statement
16362 // that could emit a line event.
16363 if (statements != NULL && statements->body.size == 1) {
16364 pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE);
16365 }
16366
16367 return UP(pm_embedded_statements_node_create(parser, &opening, statements, &parser->previous));
16368 }
16369
16370 // Here the lexer has returned the beginning of an embedded variable.
16371 // In that case we'll parse the variable and create an appropriate node
16372 // for it and then return that node. These kinds of parts look like:
16373 //
16374 // "aaa #{bbb} #@ccc ddd"
16375 // ^^^^^
16376 case PM_TOKEN_EMBVAR: {
16377 // Ruby disallows seeing encoding around interpolation in strings,
16378 // even though it is known at parse time.
16379 parser->explicit_encoding = NULL;
16380
16381 lex_state_set(parser, PM_LEX_STATE_BEG);
16382 parser_lex(parser);
16383
16384 pm_token_t operator = parser->previous;
16385 pm_node_t *variable;
16386
16387 switch (parser->current.type) {
16388 // In this case a back reference is being interpolated. We'll
16389 // create a global variable read node.
16390 case PM_TOKEN_BACK_REFERENCE:
16391 parser_lex(parser);
16392 variable = UP(pm_back_reference_read_node_create(parser, &parser->previous));
16393 break;
16394 // In this case an nth reference is being interpolated. We'll
16395 // create a global variable read node.
16396 case PM_TOKEN_NUMBERED_REFERENCE:
16397 parser_lex(parser);
16398 variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16399 break;
16400 // In this case a global variable is being interpolated. We'll
16401 // create a global variable read node.
16402 case PM_TOKEN_GLOBAL_VARIABLE:
16403 parser_lex(parser);
16404 variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
16405 break;
16406 // In this case an instance variable is being interpolated.
16407 // We'll create an instance variable read node.
16408 case PM_TOKEN_INSTANCE_VARIABLE:
16409 parser_lex(parser);
16410 variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
16411 break;
16412 // In this case a class variable is being interpolated. We'll
16413 // create a class variable read node.
16414 case PM_TOKEN_CLASS_VARIABLE:
16415 parser_lex(parser);
16416 variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
16417 break;
16418 // We can hit here if we got an invalid token. In that case
16419 // we'll not attempt to lex this token and instead just return a
16420 // missing node.
16421 default:
16422 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EMBVAR_INVALID);
16423 variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16424 break;
16425 }
16426
16427 return UP(pm_embedded_variable_node_create(parser, &operator, variable));
16428 }
16429 default:
16430 parser_lex(parser);
16431 pm_parser_err_previous(parser, PM_ERR_CANNOT_PARSE_STRING_PART);
16432 return NULL;
16433 }
16434}
16435
16441static const uint8_t *
16442parse_operator_symbol_name(const pm_token_t *name) {
16443 switch (name->type) {
16444 case PM_TOKEN_TILDE:
16445 case PM_TOKEN_BANG:
16446 if (name->end[-1] == '@') return name->end - 1;
16448 default:
16449 return name->end;
16450 }
16451}
16452
16453static pm_node_t *
16454parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_state_t next_state) {
16455 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, opening, &parser->current, NULL);
16456 const uint8_t *end = parse_operator_symbol_name(&parser->current);
16457
16458 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16459 parser_lex(parser);
16460
16461 pm_string_shared_init(&symbol->unescaped, parser->previous.start, end);
16462 pm_node_flag_set(UP(symbol), PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING);
16463
16464 return UP(symbol);
16465}
16466
16472static pm_node_t *
16473parse_symbol(pm_parser_t *parser, pm_lex_state_t next_state, uint16_t depth) {
16474 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
16475 parser_lex(parser);
16476
16477 const pm_token_t opening = parser->previous;
16478
16479 if (lex_mode.mode != PM_LEX_STRING) {
16480 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16481
16482 switch (parser->current.type) {
16483 case PM_CASE_OPERATOR:
16484 return parse_operator_symbol(parser, &opening, next_state == PM_LEX_STATE_NONE ? PM_LEX_STATE_ENDFN : next_state);
16485 case PM_TOKEN_IDENTIFIER:
16486 case PM_TOKEN_CONSTANT:
16487 case PM_TOKEN_INSTANCE_VARIABLE:
16488 case PM_TOKEN_METHOD_NAME:
16489 case PM_TOKEN_CLASS_VARIABLE:
16490 case PM_TOKEN_GLOBAL_VARIABLE:
16491 case PM_TOKEN_NUMBERED_REFERENCE:
16492 case PM_TOKEN_BACK_REFERENCE:
16493 case PM_CASE_KEYWORD:
16494 parser_lex(parser);
16495 break;
16496 default:
16497 expect2(parser, PM_TOKEN_IDENTIFIER, PM_TOKEN_METHOD_NAME, PM_ERR_SYMBOL_INVALID);
16498 break;
16499 }
16500
16501 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, NULL);
16502 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16503 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16504
16505 return UP(symbol);
16506 }
16507
16508 if (lex_mode.as.string.interpolation) {
16509 // If we have the end of the symbol, then we can return an empty symbol.
16510 if (match1(parser, PM_TOKEN_STRING_END)) {
16511 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16512 parser_lex(parser);
16513 pm_token_t content = {
16514 .type = PM_TOKEN_STRING_CONTENT,
16515 .start = parser->previous.start,
16516 .end = parser->previous.start
16517 };
16518
16519 return UP(pm_symbol_node_create(parser, &opening, &content, &parser->previous));
16520 }
16521
16522 // Now we can parse the first part of the symbol.
16523 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
16524
16525 // If we got a string part, then it's possible that we could transform
16526 // what looks like an interpolated symbol into a regular symbol.
16527 if (part && PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16528 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16529 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16530
16531 return UP(pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous));
16532 }
16533
16534 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16535 if (part) pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16536
16537 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16538 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16539 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16540 }
16541 }
16542
16543 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16544 if (match1(parser, PM_TOKEN_EOF)) {
16545 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16546 } else {
16547 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16548 }
16549
16550 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16551 return UP(symbol);
16552 }
16553
16554 pm_token_t content;
16555 pm_string_t unescaped;
16556
16557 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16558 content = parser->current;
16559 unescaped = parser->current_string;
16560 parser_lex(parser);
16561
16562 // If we have two string contents in a row, then the content of this
16563 // symbol is split because of heredoc contents. This looks like:
16564 //
16565 // <<A; :'a
16566 // A
16567 // b'
16568 //
16569 // In this case, the best way we have to represent this is as an
16570 // interpolated string node, so that's what we'll do here.
16571 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16572 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16573 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16574 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16575
16576 part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->current, NULL, &parser->current_string));
16577 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16578
16579 if (next_state != PM_LEX_STATE_NONE) {
16580 lex_state_set(parser, next_state);
16581 }
16582
16583 parser_lex(parser);
16584 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16585
16586 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16587 return UP(symbol);
16588 }
16589 } else {
16590 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end };
16591 pm_string_shared_init(&unescaped, content.start, content.end);
16592 }
16593
16594 if (next_state != PM_LEX_STATE_NONE) {
16595 lex_state_set(parser, next_state);
16596 }
16597
16598 if (match1(parser, PM_TOKEN_EOF)) {
16599 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_DYNAMIC);
16600 } else {
16601 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16602 }
16603
16604 return UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, false)));
16605}
16606
16611static PRISM_INLINE pm_node_t *
16612parse_undef_argument(pm_parser_t *parser, uint16_t depth) {
16613 switch (parser->current.type) {
16614 case PM_CASE_OPERATOR:
16615 return parse_operator_symbol(parser, NULL, PM_LEX_STATE_NONE);
16616 case PM_CASE_KEYWORD:
16617 case PM_TOKEN_CONSTANT:
16618 case PM_TOKEN_IDENTIFIER:
16619 case PM_TOKEN_METHOD_NAME: {
16620 parser_lex(parser);
16621
16622 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16623 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16624 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16625
16626 return UP(symbol);
16627 }
16628 case PM_TOKEN_SYMBOL_BEGIN:
16629 return parse_symbol(parser, PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16630 default:
16631 pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT);
16632 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16633 }
16634}
16635
16642static PRISM_INLINE pm_node_t *
16643parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) {
16644 switch (parser->current.type) {
16645 case PM_CASE_OPERATOR:
16646 return parse_operator_symbol(parser, NULL, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE);
16647 case PM_CASE_KEYWORD:
16648 case PM_TOKEN_CONSTANT:
16649 case PM_TOKEN_IDENTIFIER:
16650 case PM_TOKEN_METHOD_NAME: {
16651 if (first) lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
16652 parser_lex(parser);
16653
16654 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16655 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16656 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16657
16658 return UP(symbol);
16659 }
16660 case PM_TOKEN_SYMBOL_BEGIN:
16661 return parse_symbol(parser, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16662 case PM_TOKEN_BACK_REFERENCE:
16663 parser_lex(parser);
16664 return UP(pm_back_reference_read_node_create(parser, &parser->previous));
16665 case PM_TOKEN_NUMBERED_REFERENCE:
16666 parser_lex(parser);
16667 return UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16668 case PM_TOKEN_GLOBAL_VARIABLE:
16669 parser_lex(parser);
16670 return UP(pm_global_variable_read_node_create(parser, &parser->previous));
16671 default:
16672 pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT);
16673 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16674 }
16675}
16676
16681static pm_node_t *
16682parse_variable(pm_parser_t *parser) {
16683 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &parser->previous);
16684 int depth;
16685 bool is_numbered_param = pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
16686
16687 if (!is_numbered_param && ((depth = pm_parser_local_depth_constant_id(parser, name_id)) != -1)) {
16688 return UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false));
16689 }
16690
16691 pm_scope_t *current_scope = parser->current_scope;
16692 if (!current_scope->closed && !(current_scope->parameters & PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED)) {
16693 if (is_numbered_param) {
16694 // When you use a numbered parameter, it implies the existence of
16695 // all of the locals that exist before it. For example, referencing
16696 // _2 means that _1 must exist. Therefore here we loop through all
16697 // of the possibilities and add them into the constant pool.
16698 uint8_t maximum = (uint8_t) (parser->previous.start[1] - '0');
16699 for (uint8_t number = 1; number <= maximum; number++) {
16700 pm_parser_local_add_constant(parser, pm_numbered_parameter_names[number - 1], 2);
16701 }
16702
16703 if (!match1(parser, PM_TOKEN_EQUAL)) {
16704 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_FOUND;
16705 }
16706
16707 pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false));
16708 pm_node_list_append_inline(&parser->scratch_arena, &current_scope->implicit_parameters, node);
16709
16710 return node;
16711 } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16712 pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous));
16713 pm_node_list_append_inline(&parser->scratch_arena, &current_scope->implicit_parameters, node);
16714
16715 return node;
16716 }
16717 }
16718
16719 return NULL;
16720}
16721
16725static pm_node_t *
16726parse_variable_call(pm_parser_t *parser) {
16727 pm_node_flags_t flags = 0;
16728
16729 if (!match1(parser, PM_TOKEN_PARENTHESIS_LEFT) && (parser->previous.end[-1] != '!') && (parser->previous.end[-1] != '?')) {
16730 pm_node_t *node = parse_variable(parser);
16731 if (node != NULL) return node;
16732 flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL;
16733 }
16734
16735 pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous);
16736 pm_node_flag_set(UP(node), flags);
16737
16738 return UP(node);
16739}
16740
16747parse_method_definition_name(pm_parser_t *parser) {
16748 switch (parser->current.type) {
16749 case PM_CASE_KEYWORD:
16750 case PM_TOKEN_CONSTANT:
16751 case PM_TOKEN_METHOD_NAME:
16752 parser_lex(parser);
16753 return parser->previous;
16754 case PM_TOKEN_IDENTIFIER:
16755 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current));
16756 parser_lex(parser);
16757 return parser->previous;
16758 case PM_CASE_OPERATOR:
16759 lex_state_set(parser, PM_LEX_STATE_ENDFN);
16760 parser_lex(parser);
16761 return parser->previous;
16762 default:
16763 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_NAME, pm_token_str(parser->current.type));
16764 return (pm_token_t) { .type = 0, .start = parser->current.start, .end = parser->current.end };
16765 }
16766}
16767
16768static void
16769parse_heredoc_dedent_string(pm_arena_t *arena, pm_string_t *string, size_t common_whitespace) {
16770 // Make a writable copy of the string in the arena, since strings only view
16771 // their bytes. We keep a mutable pointer to the arena memory so we can
16772 // memmove into it below without casting away const from the string's
16773 // source field.
16774 size_t length = pm_string_length(string);
16775 uint8_t *writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
16776 pm_string_init(string, writable, length);
16777
16778 // Now get the bounds of the existing string. We'll use this as a
16779 // destination to move bytes into. We'll also use it for bounds checking
16780 // since we don't require that these strings be null terminated.
16781 size_t dest_length = pm_string_length(string);
16782 const uint8_t *source_cursor = writable;
16783 const uint8_t *source_end = source_cursor + dest_length;
16784
16785 // We're going to move bytes backward in the string when we get leading
16786 // whitespace, so we'll maintain a pointer to the current position in the
16787 // string that we're writing to.
16788 size_t trimmed_whitespace = 0;
16789
16790 // While we haven't reached the amount of common whitespace that we need to
16791 // trim and we haven't reached the end of the string, we'll keep trimming
16792 // whitespace. Trimming in this context means skipping over these bytes such
16793 // that they aren't copied into the new string.
16794 while ((source_cursor < source_end) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
16795 if (*source_cursor == '\t') {
16796 trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
16797 if (trimmed_whitespace > common_whitespace) break;
16798 } else {
16799 trimmed_whitespace++;
16800 }
16801
16802 source_cursor++;
16803 dest_length--;
16804 }
16805
16806 memmove(writable, source_cursor, (size_t) (source_end - source_cursor));
16807 string->length = dest_length;
16808}
16809
16814static PRISM_INLINE bool
16815heredoc_dedent_discard_string_node(pm_parser_t *parser, pm_string_node_t *string_node) {
16816 if (string_node->unescaped.length == 0) {
16817 const uint8_t *cursor = parser->start + PM_LOCATION_START(&string_node->content_loc);
16818 return pm_memchr(cursor, '\\', string_node->content_loc.length, parser->encoding_changed, parser->encoding) == NULL;
16819 }
16820 return false;
16821}
16822
16826static void
16827parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_whitespace) {
16828 // The next node should be dedented if it's the first node in the list or if
16829 // it follows a string node.
16830 bool dedent_next = true;
16831
16832 // Iterate over all nodes, and trim whitespace accordingly. We're going to
16833 // keep around two indices: a read and a write.
16834 size_t write_index = 0;
16835
16836 pm_node_t *node;
16837 PM_NODE_LIST_FOREACH(nodes, read_index, node) {
16838 // We're not manipulating child nodes that aren't strings. In this case
16839 // we'll skip past it and indicate that the subsequent node should not
16840 // be dedented.
16841 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE)) {
16842 nodes->nodes[write_index++] = node;
16843 dedent_next = false;
16844 continue;
16845 }
16846
16847 pm_string_node_t *string_node = ((pm_string_node_t *) node);
16848 if (dedent_next) {
16849 parse_heredoc_dedent_string(parser->arena, &string_node->unescaped, common_whitespace);
16850 }
16851
16852 if (heredoc_dedent_discard_string_node(parser, string_node)) {
16853 } else {
16854 nodes->nodes[write_index++] = node;
16855 }
16856
16857 // We always dedent the next node if it follows a string node.
16858 dedent_next = true;
16859 }
16860
16861 nodes->size = write_index;
16862}
16863
16867static pm_token_t
16868parse_strings_empty_content(const uint8_t *location) {
16869 return (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = location, .end = location };
16870}
16871
16875static PRISM_INLINE pm_node_t *
16876parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint16_t depth) {
16877 assert(parser->current.type == PM_TOKEN_STRING_BEGIN);
16878 bool concating = false;
16879
16880 while (match1(parser, PM_TOKEN_STRING_BEGIN)) {
16881 pm_node_t *node = NULL;
16882
16883 // Here we have found a string literal. We'll parse it and add it to
16884 // the list of strings.
16885 const pm_lex_mode_t *lex_mode = parser->lex_modes.current;
16886 assert(lex_mode->mode == PM_LEX_STRING);
16887 bool lex_interpolation = lex_mode->as.string.interpolation;
16888 bool label_allowed = lex_mode->as.string.label_allowed && accepts_label;
16889
16890 pm_token_t opening = parser->current;
16891 parser_lex(parser);
16892
16893 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16894 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16895 // If we get here, then we have an end immediately after a
16896 // start. In that case we'll create an empty content token and
16897 // return an uninterpolated string.
16898 pm_token_t content = parse_strings_empty_content(parser->previous.start);
16899 pm_string_node_t *string = pm_string_node_create(parser, &opening, &content, &parser->previous);
16900
16901 pm_string_shared_init(&string->unescaped, content.start, content.end);
16902 node = UP(string);
16903 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16904 // If we get here, then we have an end of a label immediately
16905 // after a start. In that case we'll create an empty symbol
16906 // node.
16907 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, NULL, &parser->previous);
16908 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.start);
16909 node = UP(symbol);
16910
16911 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16912 } else if (!lex_interpolation) {
16913 // If we don't accept interpolation then we expect the string to
16914 // start with a single string content node.
16915 pm_string_t unescaped;
16916 pm_token_t content;
16917
16918 if (match1(parser, PM_TOKEN_EOF)) {
16919 unescaped = PM_STRING_EMPTY;
16920 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start, .end = parser->start };
16921 } else {
16922 unescaped = parser->current_string;
16923 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_EXPECT_STRING_CONTENT);
16924 content = parser->previous;
16925 }
16926
16927 // It is unfortunately possible to have multiple string content
16928 // nodes in a row in the case that there's heredoc content in
16929 // the middle of the string, like this cursed example:
16930 //
16931 // <<-END+'b
16932 // a
16933 // END
16934 // c'+'d'
16935 //
16936 // In that case we need to switch to an interpolated string to
16937 // be able to contain all of the parts.
16938 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16939 pm_node_list_t parts = { 0 };
16940 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16941 pm_node_list_append_inline(parser->arena, &parts, part);
16942
16943 do {
16944 part = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16945 pm_node_list_append_inline(parser->arena, &parts, part);
16946 parser_lex(parser);
16947 } while (match1(parser, PM_TOKEN_STRING_CONTENT));
16948
16949 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16950 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16951 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16952 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, true)));
16953 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16954 } else if (match1(parser, PM_TOKEN_EOF)) {
16955 pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF);
16956 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16957 } else if (accept1(parser, PM_TOKEN_STRING_END)) {
16958 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16959 } else {
16960 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_str(parser->previous.type));
16961 parser->previous.start = parser->previous.end;
16962 parser->previous.type = 0;
16963 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16964 }
16965 } else if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16966 // In this case we've hit string content so we know the string
16967 // at least has something in it. We'll need to check if the
16968 // following token is the end (in which case we can return a
16969 // plain string) or if it's not then it has interpolation.
16970 pm_token_t content = parser->current;
16971 pm_string_t unescaped = parser->current_string;
16972 const pm_encoding_t *explicit_encoding = parser->explicit_encoding;
16973 parser_lex(parser);
16974
16975 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16976 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16977 pm_node_flag_set(node, parse_unescaped_encoding(parser, explicit_encoding));
16978
16979 // Kind of odd behavior, but basically if we have an
16980 // unterminated string and it ends in a newline, we back up one
16981 // character so that the error message is on the last line of
16982 // content in the string.
16983 if (!accept1(parser, PM_TOKEN_STRING_END)) {
16984 const uint8_t *location = parser->previous.end;
16985 if (location > parser->start && location[-1] == '\n') location--;
16986 pm_parser_err(parser, U32(location - parser->start), 0, PM_ERR_STRING_LITERAL_EOF);
16987
16988 parser->previous.start = parser->previous.end;
16989 parser->previous.type = 0;
16990 }
16991 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16992 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, explicit_encoding, &content, &unescaped, true)));
16993 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16994 } else {
16995 // If we get here, then we have interpolation so we'll need
16996 // to create a string or symbol node with interpolation.
16997 pm_node_list_t parts = { 0 };
16998 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
16999 pm_node_flag_set(part, parse_unescaped_encoding(parser, explicit_encoding));
17000 pm_node_list_append_inline(parser->arena, &parts, part);
17001
17002 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
17003 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
17004 pm_node_list_append_inline(parser->arena, &parts, part);
17005 }
17006 }
17007
17008 if (accept1(parser, PM_TOKEN_LABEL_END)) {
17009 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
17010 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
17011 } else if (match1(parser, PM_TOKEN_EOF)) {
17012 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
17013 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
17014 } else {
17015 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
17016 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
17017 }
17018 }
17019 } else {
17020 // If we get here, then the first part of the string is not plain
17021 // string content, in which case we need to parse the string as an
17022 // interpolated string.
17023 pm_node_list_t parts = { 0 };
17024 pm_node_t *part;
17025
17026 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
17027 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
17028 pm_node_list_append_inline(parser->arena, &parts, part);
17029 }
17030 }
17031
17032 if (accept1(parser, PM_TOKEN_LABEL_END)) {
17033 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
17034 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
17035 } else if (match1(parser, PM_TOKEN_EOF)) {
17036 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
17037 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
17038 } else {
17039 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
17040 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
17041 }
17042 }
17043
17044 if (current == NULL) {
17045 // If the node we just parsed is a symbol node, then we can't
17046 // concatenate it with anything else, so we can now return that
17047 // node.
17048 if (PM_NODE_TYPE_P(node, PM_SYMBOL_NODE) || PM_NODE_TYPE_P(node, PM_INTERPOLATED_SYMBOL_NODE)) {
17049 return node;
17050 }
17051
17052 // If we don't already have a node, then it's fine and we can just
17053 // set the result to be the node we just parsed.
17054 current = node;
17055 } else {
17056 // Otherwise we need to check the type of the node we just parsed.
17057 // If it cannot be concatenated with the previous node, then we'll
17058 // need to add a syntax error.
17059 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE) && !PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
17060 pm_parser_err_node(parser, node, PM_ERR_STRING_CONCATENATION);
17061 }
17062
17063 // If we haven't already created our container for concatenation,
17064 // we'll do that now.
17065 if (!concating) {
17066 if (!PM_NODE_TYPE_P(current, PM_STRING_NODE) && !PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
17067 pm_parser_err_node(parser, current, PM_ERR_STRING_CONCATENATION);
17068 }
17069
17070 concating = true;
17071 pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
17072 pm_interpolated_string_node_append(parser, container, current);
17073 current = UP(container);
17074 }
17075
17076 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, node);
17077 }
17078 }
17079
17080 return current;
17081}
17082
17083#define PM_PARSE_PATTERN_SINGLE 0
17084#define PM_PARSE_PATTERN_TOP 1
17085#define PM_PARSE_PATTERN_MULTI 2
17086
17087static pm_node_t *
17088parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
17089
17095static void
17096parse_pattern_capture(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_constant_id_t capture, const pm_location_t *location) {
17097 // Skip this capture if it starts with an underscore.
17098 if (peek_at(parser, parser->start + location->start) == '_') return;
17099
17100 if (!pm_constant_id_set_insert(&parser->scratch_arena, captures, capture)) {
17101 pm_parser_err(parser, location->start, location->length, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
17102 }
17103}
17104
17108static pm_node_t *
17109parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_t *node, uint16_t depth) {
17110 // Now, if there are any :: operators that follow, parse them as constant
17111 // path nodes.
17112 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
17113 pm_token_t delimiter = parser->previous;
17114 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
17115 node = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
17116 }
17117
17118 // If there is a [ or ( that follows, then this is part of a larger pattern
17119 // expression. We'll parse the inner pattern here, then modify the returned
17120 // inner pattern with our constant path attached.
17121 if (!match2(parser, PM_TOKEN_BRACKET_LEFT, PM_TOKEN_PARENTHESIS_LEFT)) {
17122 return node;
17123 }
17124
17125 pm_token_t opening;
17126 pm_token_t closing;
17127 pm_node_t *inner = NULL;
17128
17129 if (accept1(parser, PM_TOKEN_BRACKET_LEFT)) {
17130 opening = parser->previous;
17131 accept1(parser, PM_TOKEN_NEWLINE);
17132
17133 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17134 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
17135 accept1(parser, PM_TOKEN_NEWLINE);
17136 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
17137 }
17138
17139 closing = parser->previous;
17140 } else {
17141 parser_lex(parser);
17142 opening = parser->previous;
17143 accept1(parser, PM_TOKEN_NEWLINE);
17144
17145 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
17146 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17147 accept1(parser, PM_TOKEN_NEWLINE);
17148 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17149 }
17150
17151 closing = parser->previous;
17152 }
17153
17154 if (!inner) {
17155 // If there was no inner pattern, then we have something like Foo() or
17156 // Foo[]. In that case we'll create an array pattern with no requireds.
17157 return UP(pm_array_pattern_node_constant_create(parser, node, &opening, &closing));
17158 }
17159
17160 // Now that we have the inner pattern, check to see if it's an array, find,
17161 // or hash pattern. If it is, then we'll attach our constant path to it if
17162 // it doesn't already have a constant. If it's not one of those node types
17163 // or it does have a constant, then we'll create an array pattern.
17164 switch (PM_NODE_TYPE(inner)) {
17165 case PM_ARRAY_PATTERN_NODE: {
17166 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17167
17168 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17169 PM_NODE_START_SET_NODE(pattern_node, node);
17170 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17171
17172 pattern_node->constant = node;
17173 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17174 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17175
17176 return UP(pattern_node);
17177 }
17178
17179 break;
17180 }
17181 case PM_FIND_PATTERN_NODE: {
17182 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17183
17184 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17185 PM_NODE_START_SET_NODE(pattern_node, node);
17186 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17187
17188 pattern_node->constant = node;
17189 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17190 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17191
17192 return UP(pattern_node);
17193 }
17194
17195 break;
17196 }
17197 case PM_HASH_PATTERN_NODE: {
17198 pm_hash_pattern_node_t *pattern_node = (pm_hash_pattern_node_t *) inner;
17199
17200 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17201 PM_NODE_START_SET_NODE(pattern_node, node);
17202 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17203
17204 pattern_node->constant = node;
17205 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17206 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17207
17208 return UP(pattern_node);
17209 }
17210
17211 break;
17212 }
17213 default:
17214 break;
17215 }
17216
17217 // If we got here, then we didn't return one of the inner patterns by
17218 // attaching its constant. In this case we'll create an array pattern and
17219 // attach our constant to it.
17220 pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing);
17221 pm_array_pattern_node_requireds_append(parser->arena, pattern_node, inner);
17222 return UP(pattern_node);
17223}
17224
17228static pm_splat_node_t *
17229parse_pattern_rest(pm_parser_t *parser, pm_constant_id_set_t *captures) {
17230 assert(parser->previous.type == PM_TOKEN_USTAR);
17231 pm_token_t operator = parser->previous;
17232 pm_node_t *name = NULL;
17233
17234 // Rest patterns don't necessarily have a name associated with them. So we
17235 // will check for that here. If they do, then we'll add it to the local
17236 // table since this pattern will cause it to become a local variable.
17237 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17238 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17239
17240 int depth;
17241 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17242 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17243 }
17244
17245 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17246 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17247 name = UP(pm_local_variable_target_node_create(
17248 parser,
17249 &previous_loc,
17250 constant_id,
17251 (uint32_t) (depth == -1 ? 0 : depth)
17252 ));
17253 }
17254
17255 // Finally we can return the created node.
17256 return pm_splat_node_create(parser, &operator, name);
17257}
17258
17262static pm_node_t *
17263parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_set_t *captures) {
17264 assert(parser->current.type == PM_TOKEN_USTAR_STAR);
17265 parser_lex(parser);
17266
17267 pm_token_t operator = parser->previous;
17268 pm_node_t *value = NULL;
17269
17270 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
17271 return UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
17272 }
17273
17274 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17275 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17276
17277 int depth;
17278 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17279 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17280 }
17281
17282 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17283 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17284 value = UP(pm_local_variable_target_node_create(
17285 parser,
17286 &previous_loc,
17287 constant_id,
17288 (uint32_t) (depth == -1 ? 0 : depth)
17289 ));
17290 }
17291
17292 return UP(pm_assoc_splat_node_create(parser, value, &operator));
17293}
17294
17299static bool
17300pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
17301 ptrdiff_t length = end - start;
17302 if (length == 0) return false;
17303
17304 // First ensure that it starts with a valid identifier starting character.
17305 size_t width = char_is_identifier_start(parser, start, end - start);
17306 if (width == 0) return false;
17307
17308 // Next, ensure that it's not an uppercase character.
17309 if (parser->encoding_changed) {
17310 if (parser->encoding->isupper_char(start, length)) return false;
17311 } else {
17312 if (pm_encoding_utf_8_isupper_char(start, length)) return false;
17313 }
17314
17315 // Next, iterate through all of the bytes of the string to ensure that they
17316 // are all valid identifier characters.
17317 const uint8_t *cursor = start + width;
17318 while ((width = char_is_identifier(parser, cursor, end - cursor))) cursor += width;
17319 return cursor == end;
17320}
17321
17326static pm_node_t *
17327parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_symbol_node_t *key) {
17328 const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->content_loc;
17329 const uint8_t *start = parser->start + PM_LOCATION_START(value_loc);
17330 const uint8_t *end = parser->start + PM_LOCATION_END(value_loc);
17331
17332 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
17333 int depth = -1;
17334
17335 if (pm_slice_is_valid_local(parser, start, end)) {
17336 depth = pm_parser_local_depth_constant_id(parser, constant_id);
17337 } else {
17338 pm_parser_err(parser, PM_NODE_START(key), PM_NODE_LENGTH(key), PM_ERR_PATTERN_HASH_KEY_LOCALS);
17339
17340 if ((end > start) && ((end[-1] == '!') || (end[-1] == '?'))) {
17341 PM_PARSER_ERR_FORMAT(parser, value_loc->start, value_loc->length, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (end - start), (const char *) start);
17342 }
17343 }
17344
17345 if (depth == -1) {
17346 pm_parser_local_add(parser, constant_id, start, end, 0);
17347 }
17348
17349 parse_pattern_capture(parser, captures, constant_id, value_loc);
17350 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17351 parser,
17352 value_loc,
17353 constant_id,
17354 (uint32_t) (depth == -1 ? 0 : depth)
17355 );
17356
17357 return UP(pm_implicit_node_create(parser, UP(target)));
17358}
17359
17364static void
17365parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) {
17366 if (pm_static_literals_add(parser, keys, node, true) != NULL) {
17367 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE);
17368 }
17369}
17370
17375parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_t *first_node, uint16_t depth) {
17376 pm_node_list_t assocs = { 0 };
17377 pm_static_literals_t keys = { 0 };
17378 pm_node_t *rest = NULL;
17379
17380 switch (PM_NODE_TYPE(first_node)) {
17381 case PM_ASSOC_SPLAT_NODE:
17382 case PM_NO_KEYWORDS_PARAMETER_NODE:
17383 rest = first_node;
17384 break;
17385 case PM_INTERPOLATED_SYMBOL_NODE:
17386 case PM_SYMBOL_NODE: {
17387 if (pm_symbol_node_label_p(parser, first_node)) {
17388 if (PM_NODE_TYPE_P(first_node, PM_INTERPOLATED_SYMBOL_NODE)) {
17389 pm_parser_err_node(parser, first_node, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17390 } else {
17391 parse_pattern_hash_key(parser, &keys, first_node);
17392 }
17393
17394 pm_node_t *value;
17395
17396 /*
17397 * The label has an implicit value when the next token cannot
17398 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17399 * reduction.
17400 */
17401 if (!token_begins_pattern_p(parser->current.type)) {
17402 if (PM_NODE_TYPE_P(first_node, PM_SYMBOL_NODE)) {
17403 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
17404 } else {
17405 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(first_node), 0));
17406 }
17407 } else {
17408 // Here we have a value for the first assoc in the list, so
17409 // we will parse it now.
17410 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17411 }
17412
17413 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17414 pm_node_list_append_inline(parser->arena, &assocs, assoc);
17415 break;
17416 }
17417 }
17419 default: {
17420 // If we get anything else, then this is an error. For this we'll
17421 // create a missing node for the value and create an assoc node for
17422 // the first node in the list.
17423 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;
17424 pm_parser_err_node(parser, first_node, diag_id);
17425
17426 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_START(first_node), PM_NODE_LENGTH(first_node)));
17427 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17428
17429 pm_node_list_append_inline(parser->arena, &assocs, assoc);
17430 break;
17431 }
17432 }
17433
17434 // If there are any other assocs, then we'll parse them now.
17435 while (accept1(parser, PM_TOKEN_COMMA)) {
17436 /*
17437 * A trailing comma ends the pattern when the next token cannot begin
17438 * another element, mirroring the grammar's `p_kwargs: p_kwarg ','`
17439 * reduction.
17440 */
17441 if (!token_begins_pattern_p(parser->current.type)) {
17442 // Trailing commas are not allowed to follow a rest pattern.
17443 if (rest != NULL) {
17444 pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17445 }
17446
17447 break;
17448 }
17449
17450 if (match1(parser, PM_TOKEN_USTAR_STAR)) {
17451 pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures);
17452
17453 if (rest == NULL) {
17454 rest = assoc;
17455 } else {
17456 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17457 pm_node_list_append_inline(parser->arena, &assocs, assoc);
17458 }
17459 } else {
17460 pm_node_t *key;
17461
17462 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
17463 key = parse_strings(parser, NULL, true, (uint16_t) (depth + 1));
17464
17465 if (PM_NODE_TYPE_P(key, PM_INTERPOLATED_SYMBOL_NODE)) {
17466 pm_parser_err_node(parser, key, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17467 } else if (!pm_symbol_node_label_p(parser, key)) {
17468 pm_parser_err_node(parser, key, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17469 }
17470 } else if (accept1(parser, PM_TOKEN_LABEL)) {
17471 key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17472 } else {
17473 expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17474
17475 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = parser->previous.end, .end = parser->previous.end };
17476 key = UP(pm_symbol_node_create(parser, NULL, &label, NULL));
17477 }
17478
17479 parse_pattern_hash_key(parser, &keys, key);
17480 pm_node_t *value = NULL;
17481
17482 /*
17483 * The label has an implicit value when the next token cannot
17484 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17485 * reduction.
17486 */
17487 if (!token_begins_pattern_p(parser->current.type)) {
17488 if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) {
17489 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key);
17490 } else {
17491 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(key), 0));
17492 }
17493 } else {
17494 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17495 }
17496
17497 pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, NULL, value));
17498
17499 if (rest != NULL) {
17500 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17501 }
17502
17503 pm_node_list_append_inline(parser->arena, &assocs, assoc);
17504 }
17505 }
17506
17507 pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest);
17508 // assocs.nodes is arena-allocated; no explicit free needed.
17509
17510 pm_static_literals_free(&keys);
17511 return node;
17512}
17513
17517static pm_node_t *
17518parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_diagnostic_id_t diag_id, uint16_t depth) {
17519 switch (parser->current.type) {
17520 case PM_TOKEN_IDENTIFIER:
17521 case PM_TOKEN_METHOD_NAME: {
17522 parser_lex(parser);
17523 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17524
17525 int depth;
17526 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17527 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17528 }
17529
17530 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17531 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17532 return UP(pm_local_variable_target_node_create(
17533 parser,
17534 &previous_loc,
17535 constant_id,
17536 (uint32_t) (depth == -1 ? 0 : depth)
17537 ));
17538 }
17539 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
17540 pm_token_t opening = parser->current;
17541 parser_lex(parser);
17542
17543 if (accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17544 // If we have an empty array pattern, then we'll just return a new
17545 // array pattern node.
17546 return UP(pm_array_pattern_node_empty_create(parser, &opening, &parser->previous));
17547 }
17548
17549 // Otherwise, we'll parse the inner pattern, then deal with it depending
17550 // on the type it returns.
17551 pm_node_t *inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
17552
17553 accept1(parser, PM_TOKEN_NEWLINE);
17554 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
17555 pm_token_t closing = parser->previous;
17556
17557 switch (PM_NODE_TYPE(inner)) {
17558 case PM_ARRAY_PATTERN_NODE: {
17559 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17560 if (pattern_node->opening_loc.length == 0) {
17561 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17562 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17563
17564 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17565 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17566
17567 return UP(pattern_node);
17568 }
17569
17570 break;
17571 }
17572 case PM_FIND_PATTERN_NODE: {
17573 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17574 if (pattern_node->opening_loc.length == 0) {
17575 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17576 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17577
17578 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17579 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17580
17581 return UP(pattern_node);
17582 }
17583
17584 break;
17585 }
17586 default:
17587 break;
17588 }
17589
17590 pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing);
17591 pm_array_pattern_node_requireds_append(parser->arena, node, inner);
17592 return UP(node);
17593 }
17594 case PM_TOKEN_BRACE_LEFT_HASH: {
17595 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17596 parser->pattern_matching_newlines = false;
17597
17599 pm_token_t opening = parser->current;
17600 parser_lex(parser);
17601
17602 if (accept1(parser, PM_TOKEN_BRACE_RIGHT)) {
17603 // If we have an empty hash pattern, then we'll just return a new hash
17604 // pattern node.
17605 node = pm_hash_pattern_node_empty_create(parser, &opening, &parser->previous);
17606 } else {
17607 pm_node_t *first_node;
17608
17609 switch (parser->current.type) {
17610 case PM_TOKEN_LABEL:
17611 parser_lex(parser);
17612 first_node = UP(pm_symbol_node_label_create(parser, &parser->previous));
17613 break;
17614 case PM_TOKEN_USTAR_STAR:
17615 first_node = parse_pattern_keyword_rest(parser, captures);
17616 break;
17617 case PM_TOKEN_STRING_BEGIN:
17618 first_node = parse_expression(parser, PM_BINDING_POWER_MAX, PM_PARSE_ACCEPTS_DO_BLOCK | PM_PARSE_ACCEPTS_LABEL, PM_ERR_PATTERN_HASH_KEY_LABEL, (uint16_t) (depth + 1));
17619 break;
17620 default: {
17621 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_str(parser->current.type));
17622 parser_lex(parser);
17623
17624 first_node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
17625 break;
17626 }
17627 }
17628
17629 node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1));
17630
17631 accept1(parser, PM_TOKEN_NEWLINE);
17632 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE, &opening);
17633 pm_token_t closing = parser->previous;
17634
17635 PM_NODE_START_SET_TOKEN(parser, node, &opening);
17636 PM_NODE_LENGTH_SET_TOKEN(parser, node, &closing);
17637
17638 node->opening_loc = TOK2LOC(parser, &opening);
17639 node->closing_loc = TOK2LOC(parser, &closing);
17640 }
17641
17642 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17643 return UP(node);
17644 }
17645 case PM_TOKEN_UDOT_DOT:
17646 case PM_TOKEN_UDOT_DOT_DOT: {
17647 pm_token_t operator = parser->current;
17648 parser_lex(parser);
17649
17650 // Since we have a unary range operator, we need to parse the subsequent
17651 // expression as the right side of the range.
17652 switch (parser->current.type) {
17653 case PM_CASE_PRIMITIVE: {
17654 pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1));
17655 return UP(pm_range_node_create(parser, NULL, &operator, right));
17656 }
17657 default: {
17658 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE);
17659 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17660 return UP(pm_range_node_create(parser, NULL, &operator, right));
17661 }
17662 }
17663 }
17664 case PM_CASE_PRIMITIVE: {
17665 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_MAX, PM_PARSE_ACCEPTS_LABEL | PM_PARSE_ACCEPTS_DO_BLOCK, diag_id, (uint16_t) (depth + 1));
17666
17667 // If we found a label, we need to immediately return to the caller.
17668 if (pm_symbol_node_label_p(parser, node)) return node;
17669
17670 // Call nodes (arithmetic operations) are not allowed in patterns
17671 if (PM_NODE_TYPE(node) == PM_CALL_NODE) {
17672 pm_parser_err_node(parser, node, diag_id);
17673 return UP(pm_error_recovery_node_create_unexpected(parser, node));
17674 }
17675
17676 // Now that we have a primitive, we need to check if it's part of a range.
17677 if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
17678 pm_token_t operator = parser->previous;
17679
17680 // Now that we have the operator, we need to check if this is followed
17681 // by another expression. If it is, then we will create a full range
17682 // node. Otherwise, we'll create an endless range.
17683 switch (parser->current.type) {
17684 case PM_CASE_PRIMITIVE: {
17685 pm_node_t *right = parse_expression(parser, PM_BINDING_POWER_MAX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE, (uint16_t) (depth + 1));
17686 return UP(pm_range_node_create(parser, node, &operator, right));
17687 }
17688 default:
17689 return UP(pm_range_node_create(parser, node, &operator, NULL));
17690 }
17691 }
17692
17693 return node;
17694 }
17695 case PM_TOKEN_CARET: {
17696 parser_lex(parser);
17697 pm_token_t operator = parser->previous;
17698
17699 // At this point we have a pin operator. We need to check the subsequent
17700 // expression to determine if it's a variable or an expression.
17701 switch (parser->current.type) {
17702 case PM_TOKEN_IDENTIFIER: {
17703 parser_lex(parser);
17704 pm_node_t *variable = UP(parse_variable(parser));
17705
17706 if (variable == NULL) {
17707 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->previous, PM_ERR_NO_LOCAL_VARIABLE);
17708 variable = UP(pm_local_variable_read_node_missing_create(parser, &parser->previous, 0));
17709 }
17710
17711 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17712 }
17713 case PM_TOKEN_INSTANCE_VARIABLE: {
17714 parser_lex(parser);
17715 pm_node_t *variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
17716
17717 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17718 }
17719 case PM_TOKEN_CLASS_VARIABLE: {
17720 parser_lex(parser);
17721 pm_node_t *variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
17722
17723 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17724 }
17725 case PM_TOKEN_GLOBAL_VARIABLE: {
17726 parser_lex(parser);
17727 pm_node_t *variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
17728
17729 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17730 }
17731 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
17732 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17733 parser->pattern_matching_newlines = false;
17734
17735 pm_token_t lparen = parser->current;
17736 parser_lex(parser);
17737
17738 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_PARSE_ACCEPTS_DO_BLOCK | PM_PARSE_ACCEPTS_COMMAND_CALL, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN, (uint16_t) (depth + 1));
17739 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17740
17741 accept1(parser, PM_TOKEN_NEWLINE);
17742 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &lparen);
17743 return UP(pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous));
17744 }
17745 default: {
17746 // If we get here, then we have a pin operator followed by something
17747 // not understood. We'll create a missing node and return that.
17748 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN);
17749 pm_node_t *variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17750 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17751 }
17752 }
17753 }
17754 case PM_TOKEN_UCOLON_COLON: {
17755 pm_token_t delimiter = parser->current;
17756 parser_lex(parser);
17757
17758 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
17759 pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous);
17760
17761 return parse_pattern_constant_path(parser, captures, UP(node), (uint16_t) (depth + 1));
17762 }
17763 case PM_TOKEN_CONSTANT: {
17764 pm_token_t constant = parser->current;
17765 parser_lex(parser);
17766
17767 pm_node_t *node = UP(pm_constant_read_node_create(parser, &constant));
17768 return parse_pattern_constant_path(parser, captures, node, (uint16_t) (depth + 1));
17769 }
17770 default:
17771 pm_parser_err_current(parser, diag_id);
17772 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17773 }
17774}
17775
17783static pm_visit_result_t
17784parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
17785 pm_parser_t *parser = (pm_parser_t *) data;
17786
17787 switch (PM_NODE_TYPE(node)) {
17788 case PM_LOCAL_VARIABLE_TARGET_NODE:
17789 // Underscore-prefixed names are not captures, see
17790 // parse_pattern_capture.
17791 if (peek_at(parser, parser->start + PM_NODE_START(node)) != '_') {
17792 pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
17793 }
17794 return PM_VISIT_SKIP;
17795 case PM_ARRAY_PATTERN_NODE:
17796 case PM_ASSOC_NODE:
17797 case PM_ASSOC_SPLAT_NODE:
17798 case PM_CAPTURE_PATTERN_NODE:
17799 case PM_FIND_PATTERN_NODE:
17800 case PM_HASH_PATTERN_NODE:
17801 case PM_IMPLICIT_NODE:
17802 case PM_PARENTHESES_NODE:
17803 case PM_SPLAT_NODE:
17804 return PM_VISIT_DESCEND;
17805 default:
17806 return PM_VISIT_SKIP;
17807 }
17808}
17809
17815static void
17816parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) {
17817 pm_visit_node(node, parse_pattern_alternation_error_each, parser);
17818}
17819
17824static pm_node_t *
17825parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_t *first_node, pm_diagnostic_id_t diag_id, uint16_t depth) {
17826 pm_node_t *node = first_node;
17827 bool alternation = false;
17828
17829 while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) {
17830 if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) {
17831 parse_pattern_alternation_error(parser, node);
17832 }
17833
17834 switch (parser->current.type) {
17835 case PM_TOKEN_IDENTIFIER:
17836 case PM_TOKEN_BRACKET_LEFT_ARRAY:
17837 case PM_TOKEN_BRACE_LEFT_HASH:
17838 case PM_TOKEN_CARET:
17839 case PM_TOKEN_CONSTANT:
17840 case PM_TOKEN_UCOLON_COLON:
17841 case PM_TOKEN_UDOT_DOT:
17842 case PM_TOKEN_UDOT_DOT_DOT:
17843 case PM_CASE_PRIMITIVE: {
17844 if (!alternation) {
17845 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17846 } else {
17847 pm_token_t operator = parser->previous;
17848 pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1));
17849
17850 if (captures->size) parse_pattern_alternation_error(parser, right);
17851 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17852 }
17853
17854 break;
17855 }
17856 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
17857 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
17858 pm_token_t operator = parser->previous;
17859 pm_token_t opening = parser->current;
17860 parser_lex(parser);
17861
17862 pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17863 accept1(parser, PM_TOKEN_NEWLINE);
17864 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17865 pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0));
17866
17867 if (!alternation) {
17868 node = right;
17869 } else {
17870 if (captures->size) parse_pattern_alternation_error(parser, right);
17871 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17872 }
17873
17874 break;
17875 }
17876 default: {
17877 pm_parser_err_current(parser, diag_id);
17878 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17879
17880 if (!alternation) {
17881 node = right;
17882 } else {
17883 if (captures->size) parse_pattern_alternation_error(parser, right);
17884 node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous));
17885 }
17886
17887 break;
17888 }
17889 }
17890 }
17891
17892 // If we have an =>, then we are assigning this pattern to a variable.
17893 // In this case we should create an assignment node.
17894 while (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
17895 pm_token_t operator = parser->previous;
17896 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET);
17897
17898 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17899 int depth;
17900
17901 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17902 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17903 }
17904
17905 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17906 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17907 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17908 parser,
17909 &previous_loc,
17910 constant_id,
17911 (uint32_t) (depth == -1 ? 0 : depth)
17912 );
17913
17914 node = UP(pm_capture_pattern_node_create(parser, node, target, &operator));
17915 }
17916
17917 return node;
17918}
17919
17923static pm_node_t *
17924parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
17925 /* Nested patterns recurse back into parse_pattern without passing through
17926 * parse_expression, so this cycle needs its own depth check. */
17927 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
17928 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
17929 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17930 }
17931
17932 pm_node_t *node = NULL;
17933
17934 bool leading_rest = false;
17935 bool trailing_rest = false;
17936
17937 switch (parser->current.type) {
17938 case PM_TOKEN_LABEL: {
17939 parser_lex(parser);
17940 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17941 node = UP(parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)));
17942
17943 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17944 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17945 }
17946
17947 return node;
17948 }
17949 case PM_TOKEN_USTAR_STAR: {
17950 node = parse_pattern_keyword_rest(parser, captures);
17951 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17952
17953 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17954 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17955 }
17956
17957 return node;
17958 }
17959 case PM_TOKEN_STRING_BEGIN: {
17960 // We need special handling for string beginnings because they could
17961 // be dynamic symbols leading to hash patterns.
17962 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17963
17964 if (pm_symbol_node_label_p(parser, node)) {
17965 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17966
17967 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17968 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17969 }
17970
17971 return node;
17972 }
17973
17974 node = parse_pattern_primitives(parser, captures, node, diag_id, (uint16_t) (depth + 1));
17975 break;
17976 }
17977 case PM_TOKEN_USTAR: {
17978 if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) {
17979 parser_lex(parser);
17980 node = UP(parse_pattern_rest(parser, captures));
17981 leading_rest = true;
17982 break;
17983 }
17984 }
17986 default:
17987 node = parse_pattern_primitives(parser, captures, NULL, diag_id, (uint16_t) (depth + 1));
17988 break;
17989 }
17990
17991 // If we got a dynamic label symbol, then we need to treat it like the
17992 // beginning of a hash pattern.
17993 if (pm_symbol_node_label_p(parser, node)) {
17994 return UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17995 }
17996
17997 if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) {
17998 // If we have a comma, then we are now parsing either an array pattern
17999 // or a find pattern. We need to parse all of the patterns, put them
18000 // into a big list, and then determine which type of node we have.
18001 pm_node_list_t nodes = { 0 };
18002 pm_node_list_append_inline(parser->arena, &nodes, node);
18003
18004 // Gather up all of the patterns into the list.
18005 while (accept1(parser, PM_TOKEN_COMMA)) {
18006 /*
18007 * A trailing comma ends the pattern when the next token cannot
18008 * begin another pattern element, leaving the token for the
18009 * enclosing context to accept or reject.
18010 */
18011 if (!token_begins_pattern_p(parser->current.type)) {
18012 // A trailing comma forms an implicit rest pattern (`[a,]` is
18013 // `[a, *]`). If a rest pattern has already been parsed, then
18014 // this is a second rest, which is not allowed (e.g. `[a, *b,]`
18015 // or `x => a, *b,`).
18016 if (trailing_rest) {
18017 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
18018 }
18019
18020 node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
18021 pm_node_list_append_inline(parser->arena, &nodes, node);
18022 trailing_rest = true;
18023 break;
18024 }
18025
18026 if (accept1(parser, PM_TOKEN_USTAR)) {
18027 node = UP(parse_pattern_rest(parser, captures));
18028
18029 // If we have already parsed a splat pattern, then this is an
18030 // error. We will continue to parse the rest of the patterns,
18031 // but we will indicate it as an error.
18032 if (trailing_rest) {
18033 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
18034 }
18035
18036 trailing_rest = true;
18037 } else {
18038 node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
18039 }
18040
18041 pm_node_list_append_inline(parser->arena, &nodes, node);
18042 }
18043
18044 // If the first pattern and the last pattern are rest patterns, then we
18045 // will call this a find pattern, regardless of how many rest patterns
18046 // are in between because we know we already added the appropriate
18047 // errors. Otherwise we will create an array pattern.
18048 if (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) {
18049 node = UP(pm_find_pattern_node_create(parser, &nodes));
18050
18051 if (nodes.size == 2) {
18052 pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER);
18053 }
18054 } else {
18055 node = UP(pm_array_pattern_node_node_list_create(parser, &nodes));
18056
18057 if (leading_rest && trailing_rest) {
18058 pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS);
18059 }
18060 }
18061
18062 // nodes.nodes is arena-allocated; no explicit free needed.
18063 } else if (leading_rest) {
18064 // Otherwise, if we parsed a single splat pattern, then we know we have
18065 // an array pattern, so we can go ahead and create that node.
18066 node = UP(pm_array_pattern_node_rest_create(parser, node));
18067 }
18068
18069 return node;
18070}
18071
18077static PRISM_INLINE void
18078parse_negative_numeric(pm_node_t *node) {
18079 switch (PM_NODE_TYPE(node)) {
18080 case PM_INTEGER_NODE: {
18082 cast->base.location.start--;
18083 cast->base.location.length++;
18084 cast->value.negative = true;
18085 break;
18086 }
18087 case PM_FLOAT_NODE: {
18089 cast->base.location.start--;
18090 cast->base.location.length++;
18091 cast->value = -cast->value;
18092 break;
18093 }
18094 case PM_RATIONAL_NODE: {
18096 cast->base.location.start--;
18097 cast->base.location.length++;
18098 cast->numerator.negative = true;
18099 break;
18100 }
18101 case PM_IMAGINARY_NODE:
18102 node->location.start--;
18103 node->location.length++;
18104 parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric);
18105 break;
18106 default:
18107 assert(false && "unreachable");
18108 break;
18109 }
18110}
18111
18117static void
18118pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
18119 switch (diag_id) {
18120 case PM_ERR_HASH_KEY: {
18121 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, pm_token_str(parser->previous.type));
18122 break;
18123 }
18124 case PM_ERR_HASH_VALUE:
18125 case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: {
18126 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
18127 break;
18128 }
18129 case PM_ERR_UNARY_RECEIVER: {
18130 const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_str(parser->current.type));
18131 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, human, parser->previous.start[0]);
18132 break;
18133 }
18134 case PM_ERR_UNARY_DISALLOWED:
18135 case PM_ERR_EXPECT_ARGUMENT: {
18136 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
18137 break;
18138 }
18139 default:
18140 pm_parser_err_previous(parser, diag_id);
18141 break;
18142 }
18143}
18144
18148static void
18149ensure_valid_retry(pm_parser_t *parser, const pm_node_t *node) {
18150#define CONTEXT_NONE 0
18151#define CONTEXT_THROUGH_ENSURE 1
18152#define CONTEXT_THROUGH_ELSE 2
18153
18154 pm_context_node_t *context_node = parser->current_context;
18155 int context = CONTEXT_NONE;
18156
18157 while (context_node != NULL) {
18158 switch (context_node->context) {
18159 case PM_CONTEXT_BEGIN_RESCUE:
18160 case PM_CONTEXT_BLOCK_RESCUE:
18161 case PM_CONTEXT_CLASS_RESCUE:
18162 case PM_CONTEXT_DEF_RESCUE:
18163 case PM_CONTEXT_LAMBDA_RESCUE:
18164 case PM_CONTEXT_MODULE_RESCUE:
18165 case PM_CONTEXT_SCLASS_RESCUE:
18166 case PM_CONTEXT_DEFINED:
18167 case PM_CONTEXT_RESCUE_MODIFIER:
18168 // These are the good cases. We're allowed to have a retry here.
18169 return;
18170 case PM_CONTEXT_CLASS:
18171 case PM_CONTEXT_DEF:
18172 case PM_CONTEXT_DEF_PARAMS:
18173 case PM_CONTEXT_MAIN:
18174 case PM_CONTEXT_MODULE:
18175 case PM_CONTEXT_PREEXE:
18176 case PM_CONTEXT_SCLASS:
18177 // These are the bad cases. We're not allowed to have a retry in
18178 // these contexts.
18179 if (context == CONTEXT_NONE) {
18180 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE);
18181 } else if (context == CONTEXT_THROUGH_ENSURE) {
18182 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE);
18183 } else if (context == CONTEXT_THROUGH_ELSE) {
18184 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE);
18185 }
18186 return;
18187 case PM_CONTEXT_BEGIN_ELSE:
18188 case PM_CONTEXT_BLOCK_ELSE:
18189 case PM_CONTEXT_CLASS_ELSE:
18190 case PM_CONTEXT_DEF_ELSE:
18191 case PM_CONTEXT_LAMBDA_ELSE:
18192 case PM_CONTEXT_MODULE_ELSE:
18193 case PM_CONTEXT_SCLASS_ELSE:
18194 // These are also bad cases, but with a more specific error
18195 // message indicating the else.
18196 context = CONTEXT_THROUGH_ELSE;
18197 break;
18198 case PM_CONTEXT_BEGIN_ENSURE:
18199 case PM_CONTEXT_BLOCK_ENSURE:
18200 case PM_CONTEXT_CLASS_ENSURE:
18201 case PM_CONTEXT_DEF_ENSURE:
18202 case PM_CONTEXT_LAMBDA_ENSURE:
18203 case PM_CONTEXT_MODULE_ENSURE:
18204 case PM_CONTEXT_SCLASS_ENSURE:
18205 // These are also bad cases, but with a more specific error
18206 // message indicating the ensure.
18207 context = CONTEXT_THROUGH_ENSURE;
18208 break;
18209 case PM_CONTEXT_NONE:
18210 case PM_CONTEXT_MAXIMUM:
18211 // This case should never happen.
18212 assert(false && "unreachable");
18213 break;
18214 case PM_CONTEXT_BEGIN:
18215 case PM_CONTEXT_BLOCK_BRACES:
18216 case PM_CONTEXT_BLOCK_KEYWORDS:
18217 case PM_CONTEXT_BLOCK_PARAMETERS:
18218 case PM_CONTEXT_CASE_IN:
18219 case PM_CONTEXT_CASE_WHEN:
18220 case PM_CONTEXT_DEFAULT_PARAMS:
18221 case PM_CONTEXT_ELSE:
18222 case PM_CONTEXT_ELSIF:
18223 case PM_CONTEXT_EMBEXPR:
18224 case PM_CONTEXT_FOR_INDEX:
18225 case PM_CONTEXT_FOR:
18226 case PM_CONTEXT_IF:
18227 case PM_CONTEXT_LAMBDA_BRACES:
18228 case PM_CONTEXT_LAMBDA_DO_END:
18229 case PM_CONTEXT_LOOP_PREDICATE:
18230 case PM_CONTEXT_MULTI_TARGET:
18231 case PM_CONTEXT_PARENS:
18232 case PM_CONTEXT_POSTEXE:
18233 case PM_CONTEXT_PREDICATE:
18234 case PM_CONTEXT_TERNARY:
18235 case PM_CONTEXT_UNLESS:
18236 case PM_CONTEXT_UNTIL:
18237 case PM_CONTEXT_WHILE:
18238 // In these contexts we should continue walking up the list of
18239 // contexts.
18240 break;
18241 }
18242
18243 context_node = context_node->prev;
18244 }
18245
18246#undef CONTEXT_NONE
18247#undef CONTEXT_ENSURE
18248#undef CONTEXT_ELSE
18249}
18250
18254static void
18255ensure_valid_yield(pm_parser_t *parser, const pm_yield_node_t *node) {
18256 pm_context_node_t *context_node = parser->current_context;
18257
18258 while (context_node != NULL) {
18259 switch (context_node->context) {
18260 case PM_CONTEXT_DEF:
18261 case PM_CONTEXT_DEF_PARAMS:
18262 case PM_CONTEXT_DEFINED:
18263 case PM_CONTEXT_DEF_ENSURE:
18264 case PM_CONTEXT_DEF_RESCUE:
18265 case PM_CONTEXT_DEF_ELSE:
18266 // These are the good cases. We're allowed to have a block exit
18267 // in these contexts.
18268 return;
18269 case PM_CONTEXT_CLASS:
18270 case PM_CONTEXT_CLASS_ENSURE:
18271 case PM_CONTEXT_CLASS_RESCUE:
18272 case PM_CONTEXT_CLASS_ELSE:
18273 case PM_CONTEXT_MAIN:
18274 case PM_CONTEXT_MODULE:
18275 case PM_CONTEXT_MODULE_ENSURE:
18276 case PM_CONTEXT_MODULE_RESCUE:
18277 case PM_CONTEXT_MODULE_ELSE:
18278 case PM_CONTEXT_SCLASS:
18279 case PM_CONTEXT_SCLASS_RESCUE:
18280 case PM_CONTEXT_SCLASS_ENSURE:
18281 case PM_CONTEXT_SCLASS_ELSE:
18282 // These are the bad cases. We're not allowed to have a retry in
18283 // these contexts.
18284 pm_parser_err_node(parser, UP(node), PM_ERR_INVALID_YIELD);
18285 return;
18286 case PM_CONTEXT_NONE:
18287 case PM_CONTEXT_MAXIMUM:
18288 // This case should never happen.
18289 assert(false && "unreachable");
18290 break;
18291 case PM_CONTEXT_BEGIN:
18292 case PM_CONTEXT_BEGIN_ELSE:
18293 case PM_CONTEXT_BEGIN_ENSURE:
18294 case PM_CONTEXT_BEGIN_RESCUE:
18295 case PM_CONTEXT_BLOCK_BRACES:
18296 case PM_CONTEXT_BLOCK_KEYWORDS:
18297 case PM_CONTEXT_BLOCK_ELSE:
18298 case PM_CONTEXT_BLOCK_ENSURE:
18299 case PM_CONTEXT_BLOCK_PARAMETERS:
18300 case PM_CONTEXT_BLOCK_RESCUE:
18301 case PM_CONTEXT_CASE_IN:
18302 case PM_CONTEXT_CASE_WHEN:
18303 case PM_CONTEXT_DEFAULT_PARAMS:
18304 case PM_CONTEXT_ELSE:
18305 case PM_CONTEXT_ELSIF:
18306 case PM_CONTEXT_EMBEXPR:
18307 case PM_CONTEXT_FOR_INDEX:
18308 case PM_CONTEXT_FOR:
18309 case PM_CONTEXT_IF:
18310 case PM_CONTEXT_LAMBDA_BRACES:
18311 case PM_CONTEXT_LAMBDA_DO_END:
18312 case PM_CONTEXT_LAMBDA_ELSE:
18313 case PM_CONTEXT_LAMBDA_ENSURE:
18314 case PM_CONTEXT_LAMBDA_RESCUE:
18315 case PM_CONTEXT_LOOP_PREDICATE:
18316 case PM_CONTEXT_MULTI_TARGET:
18317 case PM_CONTEXT_PARENS:
18318 case PM_CONTEXT_POSTEXE:
18319 case PM_CONTEXT_PREDICATE:
18320 case PM_CONTEXT_PREEXE:
18321 case PM_CONTEXT_RESCUE_MODIFIER:
18322 case PM_CONTEXT_TERNARY:
18323 case PM_CONTEXT_UNLESS:
18324 case PM_CONTEXT_UNTIL:
18325 case PM_CONTEXT_WHILE:
18326 // In these contexts we should continue walking up the list of
18327 // contexts.
18328 break;
18329 }
18330
18331 context_node = context_node->prev;
18332 }
18333}
18334
18339static pm_node_t *
18340parse_case(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18341 size_t opening_newline_index = token_newline_index(parser);
18342 parser_lex(parser);
18343
18344 pm_token_t case_keyword = parser->previous;
18345 pm_node_t *predicate = NULL;
18346
18347 pm_node_list_t current_block_exits = { 0 };
18348 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18349
18350 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18351 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18352 predicate = NULL;
18353 } else if (match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_END)) {
18354 predicate = NULL;
18355 } else if (!token_begins_expression_p(parser->current.type)) {
18356 predicate = NULL;
18357 } else {
18358 predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CASE_EXPRESSION_AFTER_CASE, (uint16_t) (depth + 1));
18359 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18360 }
18361
18362 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
18363 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18364 parser_lex(parser);
18365 pop_block_exits(parser, previous_block_exits);
18366 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18367 return UP(pm_case_node_create(parser, &case_keyword, predicate, &parser->previous));
18368 }
18369
18370 /* At this point we can create a case node, though we don't yet know if it
18371 * is a case-in or case-when node. */
18372 pm_node_t *node;
18373
18374 if (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18375 pm_case_node_t *case_node = pm_case_node_create(parser, &case_keyword, predicate, NULL);
18376 pm_static_literals_t literals = { 0 };
18377
18378 /* At this point we've seen a when keyword, so we know this is a
18379 * case-when node. We will continue to parse the when nodes until we hit
18380 * the end of the list. */
18381 while (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18382 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18383 parser_lex(parser);
18384
18385 pm_token_t when_keyword = parser->previous;
18386 pm_when_node_t *when_node = pm_when_node_create(parser, &when_keyword);
18387
18388 do {
18389 if (accept1(parser, PM_TOKEN_USTAR)) {
18390 pm_token_t operator = parser->previous;
18391 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
18392
18393 pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression);
18394 pm_when_node_conditions_append(parser->arena, when_node, UP(splat_node));
18395
18396 if (PM_NODE_TYPE_P(expression, PM_ERROR_RECOVERY_NODE)) break;
18397 } else {
18398 pm_node_t *condition = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CASE_EXPRESSION_AFTER_WHEN, (uint16_t) (depth + 1));
18399 pm_when_node_conditions_append(parser->arena, when_node, condition);
18400
18401 /* If we found a missing node, then this is a syntax error
18402 * and we should stop looping. */
18403 if (PM_NODE_TYPE_P(condition, PM_ERROR_RECOVERY_NODE)) break;
18404
18405 /* If this is a string node, then we need to mark it as
18406 * frozen because when clause strings are frozen. */
18407 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
18408 pm_node_flag_set(condition, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
18409 } else if (PM_NODE_TYPE_P(condition, PM_SOURCE_FILE_NODE) && parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
18410 pm_node_flag_set(condition, PM_NODE_FLAG_STATIC_LITERAL);
18411 }
18412
18413 pm_when_clause_static_literals_add(parser, &literals, condition);
18414 }
18415 } while (accept1(parser, PM_TOKEN_COMMA));
18416
18417 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18418 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18419 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18420 }
18421 } else {
18422 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER);
18423 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18424 }
18425
18426 if (!match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18427 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_CASE_WHEN, (uint16_t) (depth + 1));
18428 if (statements != NULL) {
18429 pm_when_node_statements_set(when_node, statements);
18430 }
18431 }
18432
18433 pm_case_node_condition_append(parser->arena, case_node, UP(when_node));
18434 }
18435
18436 /* If we didn't parse any conditions (in or when) then we need to
18437 * indicate that we have an error. */
18438 if (case_node->conditions.size == 0) {
18439 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18440 }
18441
18442 pm_static_literals_free(&literals);
18443 node = UP(case_node);
18444 } else {
18445 pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate);
18446
18447 /* If this is a case-match node (i.e., it is a pattern matching case
18448 * statement) then we must have a predicate. */
18449 if (predicate == NULL) {
18450 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MATCH_MISSING_PREDICATE);
18451 }
18452
18453 /* At this point we expect that we're parsing a case-in node. We will
18454 * continue to parse the in nodes until we hit the end of the list. */
18455 while (match1(parser, PM_TOKEN_KEYWORD_IN)) {
18456 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18457
18458 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
18459 parser->pattern_matching_newlines = true;
18460
18461 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
18462 parser->command_start = false;
18463 parser_lex(parser);
18464
18465 pm_token_t in_keyword = parser->previous;
18466
18467 pm_constant_id_set_t captures = { 0 };
18468 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));
18469
18470 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
18471
18472 /* Since we're in the top-level of the case-in node we need to
18473 * check for guard clauses in the form of `if` or `unless`
18474 * statements. */
18475 if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) {
18476 pm_token_t keyword = parser->previous;
18477 pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1));
18478 pattern = UP(pm_if_node_modifier_create(parser, pattern, &keyword, predicate));
18479 } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) {
18480 pm_token_t keyword = parser->previous;
18481 pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1));
18482 pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate));
18483 }
18484
18485 /* Now we need to check for the terminator of the in node's pattern.
18486 * It can be a newline or semicolon optionally followed by a `then`
18487 * keyword. */
18488 pm_token_t then_keyword = { 0 };
18489 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18490 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18491 then_keyword = parser->previous;
18492 }
18493 } else {
18494 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_IN_DELIMITER);
18495 then_keyword = parser->previous;
18496 }
18497
18498 /* Now we can actually parse the statements associated with the in
18499 * node. */
18500 pm_statements_node_t *statements;
18501 if (match3(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18502 statements = NULL;
18503 } else {
18504 statements = parse_statements(parser, PM_CONTEXT_CASE_IN, (uint16_t) (depth + 1));
18505 }
18506
18507 /* Now that we have the full pattern and statements, we can create
18508 * the node and attach it to the case node. */
18509 pm_node_t *condition = UP(pm_in_node_create(parser, pattern, statements, &in_keyword, NTOK2PTR(then_keyword)));
18510 pm_case_match_node_condition_append(parser->arena, case_node, condition);
18511 }
18512
18513 /* If we didn't parse any conditions (in or when) then we need to
18514 * indicate that we have an error. */
18515 if (case_node->conditions.size == 0) {
18516 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18517 }
18518
18519 node = UP(case_node);
18520 }
18521
18522 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18523 if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) {
18524 pm_token_t else_keyword = parser->previous;
18525 pm_else_node_t *else_node;
18526
18527 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
18528 else_node = pm_else_node_create(parser, &else_keyword, parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1)), &parser->current);
18529 } else {
18530 else_node = pm_else_node_create(parser, &else_keyword, NULL, &parser->current);
18531 }
18532
18533 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18534 pm_case_node_else_clause_set((pm_case_node_t *) node, else_node);
18535 } else {
18536 pm_case_match_node_else_clause_set((pm_case_match_node_t *) node, else_node);
18537 }
18538 }
18539
18540 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18541 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword);
18542
18543 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18544 pm_case_node_end_keyword_loc_set(parser, (pm_case_node_t *) node, &parser->previous);
18545 } else {
18546 pm_case_match_node_end_keyword_loc_set(parser, (pm_case_match_node_t *) node, &parser->previous);
18547 }
18548
18549 pop_block_exits(parser, previous_block_exits);
18550 return node;
18551}
18552
18557static pm_node_t *
18558parse_class(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18559 size_t opening_newline_index = token_newline_index(parser);
18560 parser_lex(parser);
18561
18562 pm_token_t class_keyword = parser->previous;
18563 pm_do_loop_stack_push(parser, false);
18564
18565 pm_node_list_t current_block_exits = { 0 };
18566 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18567
18568 if (accept1(parser, PM_TOKEN_LESS_LESS)) {
18569 pm_token_t operator = parser->previous;
18570 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS, (uint16_t) (depth + 1));
18571
18572 pm_parser_scope_push(parser, true);
18573 if (!match2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18574 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_str(parser->current.type));
18575 }
18576
18577 pm_node_t *statements = NULL;
18578 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18579 pm_accepts_block_stack_push(parser, true);
18580 statements = UP(parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1)));
18581 pm_accepts_block_stack_pop(parser);
18582 }
18583
18584 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18585 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18586 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)));
18587 } else {
18588 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18589 }
18590
18591 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18592
18593 pm_constant_id_list_t locals;
18594 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18595
18596 pm_parser_scope_pop(parser);
18597 pm_do_loop_stack_pop(parser);
18598
18599 flush_block_exits(parser, previous_block_exits);
18600 return UP(pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous));
18601 }
18602
18603 pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CLASS_NAME, (uint16_t) (depth + 1));
18604 pm_token_t name = parser->previous;
18605 if (name.type != PM_TOKEN_CONSTANT) {
18606 pm_parser_err_token(parser, &name, PM_ERR_CLASS_NAME);
18607 }
18608
18609 pm_token_t inheritance_operator = { 0 };
18610 pm_node_t *superclass;
18611
18612 if (match1(parser, PM_TOKEN_LESS)) {
18613 inheritance_operator = parser->current;
18614 lex_state_set(parser, PM_LEX_STATE_BEG);
18615
18616 parser->command_start = true;
18617 parser_lex(parser);
18618
18619 superclass = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CLASS_SUPERCLASS, (uint16_t) (depth + 1));
18620 } else {
18621 superclass = NULL;
18622 }
18623
18624 pm_parser_scope_push(parser, true);
18625
18626 if (inheritance_operator.start != NULL) {
18627 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CLASS_UNEXPECTED_END);
18628 } else {
18629 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18630 }
18631 pm_node_t *statements = NULL;
18632
18633 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18634 pm_accepts_block_stack_push(parser, true);
18635 statements = UP(parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1)));
18636 pm_accepts_block_stack_pop(parser);
18637 }
18638
18639 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18640 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18641 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)));
18642 } else {
18643 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18644 }
18645
18646 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18647
18648 if (context_def_p(parser)) {
18649 pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD);
18650 }
18651
18652 pm_constant_id_list_t locals;
18653 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18654
18655 pm_parser_scope_pop(parser);
18656 pm_do_loop_stack_pop(parser);
18657
18658 if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) {
18659 pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME);
18660 if (!PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18661 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18662 }
18663 }
18664
18665 pop_block_exits(parser, previous_block_exits);
18666 return UP(pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, NTOK2PTR(inheritance_operator), superclass, statements, &parser->previous));
18667}
18668
18672static pm_node_t *
18673parse_def(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18674 pm_node_list_t current_block_exits = { 0 };
18675 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18676
18677 pm_token_t def_keyword = parser->current;
18678 size_t opening_newline_index = token_newline_index(parser);
18679
18680 pm_node_t *receiver = NULL;
18681 pm_token_t operator = { 0 };
18682 pm_token_t name;
18683
18684 /* This context is necessary for lexing `...` in a bare params correctly. It
18685 * must be pushed before lexing the first param, so it is here. */
18686 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18687 parser_lex(parser);
18688
18689 /* This will be false if the method name is not a valid identifier but could
18690 * be followed by an operator. */
18691 bool valid_name = true;
18692
18693 switch (parser->current.type) {
18694 case PM_CASE_OPERATOR:
18695 pm_parser_scope_push(parser, true);
18696 lex_state_set(parser, PM_LEX_STATE_ENDFN);
18697 parser_lex(parser);
18698
18699 name = parser->previous;
18700 break;
18701 case PM_TOKEN_IDENTIFIER: {
18702 parser_lex(parser);
18703
18704 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18705 receiver = parse_variable_call(parser);
18706
18707 pm_parser_scope_push(parser, true);
18708 lex_state_set(parser, PM_LEX_STATE_FNAME);
18709 parser_lex(parser);
18710
18711 operator = parser->previous;
18712 name = parse_method_definition_name(parser);
18713 } else {
18714 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
18715 pm_parser_scope_push(parser, true);
18716
18717 name = parser->previous;
18718 }
18719
18720 break;
18721 }
18722 case PM_TOKEN_INSTANCE_VARIABLE:
18723 case PM_TOKEN_CLASS_VARIABLE:
18724 case PM_TOKEN_GLOBAL_VARIABLE:
18725 valid_name = false;
18727 case PM_TOKEN_CONSTANT:
18728 case PM_TOKEN_KEYWORD_NIL:
18729 case PM_TOKEN_KEYWORD_SELF:
18730 case PM_TOKEN_KEYWORD_TRUE:
18731 case PM_TOKEN_KEYWORD_FALSE:
18732 case PM_TOKEN_KEYWORD___FILE__:
18733 case PM_TOKEN_KEYWORD___LINE__:
18734 case PM_TOKEN_KEYWORD___ENCODING__: {
18735 pm_parser_scope_push(parser, true);
18736 parser_lex(parser);
18737
18738 pm_token_t identifier = parser->previous;
18739
18740 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18741 lex_state_set(parser, PM_LEX_STATE_FNAME);
18742 parser_lex(parser);
18743 operator = parser->previous;
18744
18745 switch (identifier.type) {
18746 case PM_TOKEN_CONSTANT:
18747 receiver = UP(pm_constant_read_node_create(parser, &identifier));
18748 break;
18749 case PM_TOKEN_INSTANCE_VARIABLE:
18750 receiver = UP(pm_instance_variable_read_node_create(parser, &identifier));
18751 break;
18752 case PM_TOKEN_CLASS_VARIABLE:
18753 receiver = UP(pm_class_variable_read_node_create(parser, &identifier));
18754 break;
18755 case PM_TOKEN_GLOBAL_VARIABLE:
18756 receiver = UP(pm_global_variable_read_node_create(parser, &identifier));
18757 break;
18758 case PM_TOKEN_KEYWORD_NIL:
18759 receiver = UP(pm_nil_node_create(parser, &identifier));
18760 break;
18761 case PM_TOKEN_KEYWORD_SELF:
18762 receiver = UP(pm_self_node_create(parser, &identifier));
18763 break;
18764 case PM_TOKEN_KEYWORD_TRUE:
18765 receiver = UP(pm_true_node_create(parser, &identifier));
18766 break;
18767 case PM_TOKEN_KEYWORD_FALSE:
18768 receiver = UP(pm_false_node_create(parser, &identifier));
18769 break;
18770 case PM_TOKEN_KEYWORD___FILE__:
18771 receiver = UP(pm_source_file_node_create(parser, &identifier));
18772 break;
18773 case PM_TOKEN_KEYWORD___LINE__:
18774 receiver = UP(pm_source_line_node_create(parser, &identifier));
18775 break;
18776 case PM_TOKEN_KEYWORD___ENCODING__:
18777 receiver = UP(pm_source_encoding_node_create(parser, &identifier));
18778 break;
18779 default:
18780 break;
18781 }
18782
18783 name = parse_method_definition_name(parser);
18784 } else {
18785 if (!valid_name) {
18786 PM_PARSER_ERR_TOKEN_FORMAT(parser, &identifier, PM_ERR_DEF_NAME, pm_token_str(identifier.type));
18787 }
18788
18789 name = identifier;
18790 }
18791 break;
18792 }
18793 case PM_TOKEN_PARENTHESIS_LEFT: {
18794 /* The current context is `PM_CONTEXT_DEF_PARAMS`, however the inner
18795 * expression of this parenthesis should not be processed under this
18796 * context. Thus, the context is popped here. */
18797 context_pop(parser);
18798 parser_lex(parser);
18799
18800 pm_token_t lparen = parser->previous;
18801 pm_node_t *expression = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_DEF_RECEIVER, (uint16_t) (depth + 1));
18802
18803 accept1(parser, PM_TOKEN_NEWLINE);
18804 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
18805 pm_token_t rparen = parser->previous;
18806
18807 lex_state_set(parser, PM_LEX_STATE_FNAME);
18808 expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM);
18809
18810 operator = parser->previous;
18811 receiver = UP(pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0));
18812
18813 /* To push `PM_CONTEXT_DEF_PARAMS` again is for the same reason as
18814 * described the above. */
18815 pm_parser_scope_push(parser, true);
18816 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18817 name = parse_method_definition_name(parser);
18818 break;
18819 }
18820 default:
18821 pm_parser_scope_push(parser, true);
18822 name = parse_method_definition_name(parser);
18823 break;
18824 }
18825
18826 pm_token_t lparen = { 0 };
18827 pm_token_t rparen = { 0 };
18828 pm_parameters_node_t *params;
18829
18830 bool accept_endless_def = true;
18831 switch (parser->current.type) {
18832 case PM_TOKEN_PARENTHESIS_LEFT: {
18833 parser_lex(parser);
18834 lparen = parser->previous;
18835
18836 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18837 params = NULL;
18838 } else {
18839 /* https://bugs.ruby-lang.org/issues/19107 */
18840 bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1;
18841 params = parse_parameters(
18842 parser,
18843 PM_BINDING_POWER_DEFINED,
18844 true,
18845 allow_trailing_comma,
18846 true,
18847 true,
18848 false,
18849 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18850 (uint16_t) (depth + 1)
18851 );
18852 }
18853
18854 lex_state_set(parser, PM_LEX_STATE_BEG);
18855 parser->command_start = true;
18856
18857 context_pop(parser);
18858 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18859 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_str(parser->current.type));
18860 parser->previous.start = parser->previous.end;
18861 parser->previous.type = 0;
18862 }
18863
18864 rparen = parser->previous;
18865 break;
18866 }
18867 case PM_CASE_PARAMETER: {
18868 /* If we're about to lex a label, we need to add the label state to
18869 * make sure the next newline is ignored. */
18870 if (parser->current.type == PM_TOKEN_LABEL) {
18871 lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL);
18872 }
18873
18874 params = parse_parameters(
18875 parser,
18876 PM_BINDING_POWER_DEFINED,
18877 false,
18878 false,
18879 true,
18880 true,
18881 false,
18882 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18883 (uint16_t) (depth + 1)
18884 );
18885
18886 /* Reject `def * = 1` and similar. We have to specifically check for
18887 * them because they create ambiguity with optional arguments. */
18888 accept_endless_def = false;
18889
18890 context_pop(parser);
18891 break;
18892 }
18893 default: {
18894 params = NULL;
18895 context_pop(parser);
18896 break;
18897 }
18898 }
18899
18900 pm_node_t *statements = NULL;
18901 pm_token_t equal = { 0 };
18902 pm_token_t end_keyword = { 0 };
18903
18904 if (accept1(parser, PM_TOKEN_EQUAL)) {
18905 if (token_is_setter_name(&name)) {
18906 pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER);
18907 }
18908 if (!accept_endless_def) {
18909 pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
18910 }
18911 if (
18912 parser->current_context->context == PM_CONTEXT_DEFAULT_PARAMS &&
18913 parser->current_context->prev->context == PM_CONTEXT_BLOCK_PARAMETERS
18914 ) {
18915 PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_START(parser, &def_keyword), PM_TOKENS_LENGTH(&def_keyword, &parser->previous), PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE, "endless method definition");
18916 }
18917 equal = parser->previous;
18918
18919 context_push(parser, PM_CONTEXT_DEF);
18920 pm_do_loop_stack_push(parser, false);
18921 statements = UP(pm_statements_node_create(parser));
18922
18923 /* The body accepts a command call (`def f = foo 1`) only where the
18924 * caller accepts an endless method with a command body. */
18925 uint8_t allow_flags = (flags & PM_PARSE_ACCEPTS_COMMAND_DEF) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
18926
18927 /* Inside a def body, we push true onto the accepts_block_stack so that
18928 * `do` is lexed as PM_TOKEN_KEYWORD_DO (which can only start a block
18929 * for primary-level constructs, not commands). During command argument
18930 * parsing, the stack is pushed to false, causing `do` to be lexed as
18931 * PM_TOKEN_KEYWORD_DO_BLOCK, which is not consumed inside the endless
18932 * def body and instead left for the outer context. A method definition
18933 * opens a fresh context all the way through its rescue modifier, so
18934 * this frame spans the rescue modifier value as well: the `do` in
18935 * `baz def f = a rescue z do end` lexes as a plain keyword that
18936 * attaches to `z` rather than to `baz`. */
18937 pm_accepts_block_stack_push(parser, true);
18938 pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_flags | PM_PARSE_IN_ENDLESS_DEF, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1));
18939
18940 /* If an unconsumed PM_TOKEN_KEYWORD_DO follows the body, it is an error
18941 * (e.g., `def f = 1 do end`). PM_TOKEN_KEYWORD_DO_BLOCK is
18942 * intentionally not caught here — it should bubble up to the outer
18943 * context (e.g., `private def f = puts "Hello" do end` where the block
18944 * attaches to `private`). */
18945 if (accept1(parser, PM_TOKEN_KEYWORD_DO)) {
18946 pm_block_node_t *block = parse_block(parser, (uint16_t) (depth + 1));
18947 pm_parser_err_node(parser, UP(block), PM_ERR_DEF_ENDLESS_DO_BLOCK);
18948 }
18949
18950 /* Any number of rescue modifiers chain onto the body within the method
18951 * definition itself, associating to the left: `def f = a rescue b
18952 * rescue c` defines a method whose body is `(a rescue b) rescue c`,
18953 * rather than a rescue modifier guarding the definition. */
18954 while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
18955 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
18956
18957 pm_token_t rescue_keyword = parser->previous;
18958
18959 /* In the Ruby grammar, the rescue value of an endless method
18960 * command excludes and/or and in/=>. */
18961 pm_node_t *value = parse_expression(parser, PM_BINDING_POWER_MATCH + 1, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
18962 context_pop(parser);
18963
18964 statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value));
18965 }
18966
18967 pm_accepts_block_stack_pop(parser);
18968
18969 /* A nested endless def whose body is a command call (e.g.,
18970 * `def f = def g = foo bar`) is a command write and cannot appear
18971 * as a def body. */
18972 if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(parser, statement)) {
18973 PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
18974 }
18975
18976 pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false);
18977 pm_do_loop_stack_pop(parser);
18978 context_pop(parser);
18979 } else {
18980 if (lparen.start == NULL) {
18981 lex_state_set(parser, PM_LEX_STATE_BEG);
18982 parser->command_start = true;
18983 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_DEF_PARAMS_TERM);
18984 } else {
18985 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18986 }
18987
18988 pm_accepts_block_stack_push(parser, true);
18989 pm_do_loop_stack_push(parser, false);
18990
18991 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18992 pm_accepts_block_stack_push(parser, true);
18993 statements = UP(parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1)));
18994 pm_accepts_block_stack_pop(parser);
18995 }
18996
18997 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18998 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18999 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)));
19000 } else {
19001 parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false);
19002 }
19003
19004 pm_accepts_block_stack_pop(parser);
19005 pm_do_loop_stack_pop(parser);
19006
19007 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword);
19008 end_keyword = parser->previous;
19009 }
19010
19011 pm_constant_id_list_t locals;
19012 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
19013 pm_parser_scope_pop(parser);
19014
19015 /* If the final character is `@` as is the case when defining methods to
19016 * override the unary operators, we should ignore the @ in the same way we
19017 * do for symbols. */
19018 pm_constant_id_t name_id = pm_parser_constant_id_raw(parser, name.start, parse_operator_symbol_name(&name));
19019
19020 flush_block_exits(parser, previous_block_exits);
19021
19022 return UP(pm_def_node_create(
19023 parser,
19024 name_id,
19025 &name,
19026 receiver,
19027 params,
19028 statements,
19029 &locals,
19030 &def_keyword,
19031 NTOK2PTR(operator),
19032 NTOK2PTR(lparen),
19033 NTOK2PTR(rparen),
19034 NTOK2PTR(equal),
19035 NTOK2PTR(end_keyword)
19036 ));
19037}
19038
19042static pm_node_t *
19043parse_module(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19044 pm_node_list_t current_block_exits = { 0 };
19045 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19046
19047 size_t opening_newline_index = token_newline_index(parser);
19048 parser_lex(parser);
19049 pm_token_t module_keyword = parser->previous;
19050
19051 pm_node_t *constant_path = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_MODULE_NAME, (uint16_t) (depth + 1));
19052 pm_token_t name;
19053
19054 /* If we can recover from a syntax error that occurred while parsing the
19055 * name of the module, then we'll handle that here. */
19056 if (PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
19057 pop_block_exits(parser, previous_block_exits);
19058
19059 pm_token_t missing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19060 return UP(pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing));
19061 }
19062
19063 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
19064 pm_token_t double_colon = parser->previous;
19065
19066 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
19067 constant_path = UP(pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous));
19068 }
19069
19070 /* Here we retrieve the name of the module. If it wasn't a constant, then
19071 * it's possible that `module foo` was passed, which is a syntax error. We
19072 * handle that here as well. */
19073 name = parser->previous;
19074 if (name.type != PM_TOKEN_CONSTANT) {
19075 pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME);
19076 }
19077
19078 if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE) && !PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
19079 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
19080 }
19081
19082 pm_parser_scope_push(parser, true);
19083 accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE);
19084 pm_node_t *statements = NULL;
19085
19086 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
19087 pm_accepts_block_stack_push(parser, true);
19088 statements = UP(parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1)));
19089 pm_accepts_block_stack_pop(parser);
19090 }
19091
19092 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
19093 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
19094 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)));
19095 } else {
19096 parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false);
19097 }
19098
19099 pm_constant_id_list_t locals;
19100 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
19101
19102 pm_parser_scope_pop(parser);
19103 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword);
19104
19105 if (context_def_p(parser)) {
19106 pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
19107 }
19108
19109 pop_block_exits(parser, previous_block_exits);
19110
19111 return UP(pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous));
19112}
19113
19118static pm_node_t *
19119parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
19120 pm_token_t opening = parser->current;
19121 pm_node_flags_t paren_flags = 0;
19122
19123 pm_node_list_t current_block_exits = { 0 };
19124 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19125
19126 parser_lex(parser);
19127 while (true) {
19128 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19129 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19130 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19131 break;
19132 }
19133 }
19134
19135 /* If this is the end of the file or we match a right parenthesis, then we
19136 * have an empty parentheses node, and we can immediately return. */
19137 if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
19138 /* A command argument group sets EXPR_ENDARG before its ')' is
19139 * consumed, even when the group is empty, so that a following '{' is
19140 * scanned as a block brace. */
19141 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT) && opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19142 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19143 }
19144
19145 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19146 pop_block_exits(parser, previous_block_exits);
19147 return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, paren_flags));
19148 }
19149
19150 /* Otherwise, we're going to parse the first statement in the list of
19151 * statements within the parentheses. */
19152 context_push(parser, PM_CONTEXT_PARENS);
19153 pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19154 context_pop(parser);
19155
19156 /* Determine if this statement is followed by a terminator. In the case of a
19157 * single statement, this is fine. But in the case of multiple statements
19158 * it's required. */
19159 bool terminator_found = false;
19160
19161 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19162 terminator_found = true;
19163 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19164 } else if (accept1(parser, PM_TOKEN_NEWLINE)) {
19165 terminator_found = true;
19166 }
19167
19168 if (terminator_found) {
19169 while (true) {
19170 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19171 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19172 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19173 break;
19174 }
19175 }
19176 }
19177
19178 /* If we hit a right parenthesis, then we're done parsing the parentheses
19179 * node, and we can check which kind of node we should return. */
19180 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19181 if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19182 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19183 }
19184
19185 parser_lex(parser);
19186 pop_block_exits(parser, previous_block_exits);
19187
19188 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19189 /* If we have a single statement and are ending on a right
19190 * parenthesis, then we need to check if this is possibly a multiple
19191 * target node. */
19192 pm_multi_target_node_t *multi_target;
19193
19194 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) && ((pm_multi_target_node_t *) statement)->lparen_loc.length == 0) {
19195 multi_target = (pm_multi_target_node_t *) statement;
19196 } else {
19197 multi_target = pm_multi_target_node_create(parser);
19198 pm_multi_target_node_targets_append(parser, multi_target, statement);
19199 }
19200
19201 multi_target->lparen_loc = TOK2LOC(parser, &opening);
19202 multi_target->rparen_loc = TOK2LOC(parser, &parser->previous);
19203 PM_NODE_START_SET_TOKEN(parser, multi_target, &opening);
19204 PM_NODE_LENGTH_SET_TOKEN(parser, multi_target, &parser->previous);
19205
19206 pm_node_t *result;
19207 if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) {
19208 result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19209 accept1(parser, PM_TOKEN_NEWLINE);
19210 } else {
19211 result = UP(multi_target);
19212 }
19213
19214 if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
19215 /* All set, this is explicitly allowed by the parent context. */
19216 } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match2(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_COMMA)) {
19217 /* All set, we're inside a for loop and we're parsing multiple
19218 * targets. A comma continues the index target list, as in
19219 * `for (a, b), c in ...`. */
19220 } else if (flags & PM_PARSE_ACCEPTS_STATEMENT) {
19221 /* The rescue-modifier value parser promotes this target on a
19222 * following `=` or comma. Reject any other binary operator that
19223 * would otherwise consume the target list (e.g. `(a, b) + c`). */
19224 if (pm_binding_powers[parser->current.type].binary && !match1(parser, PM_TOKEN_EQUAL)) {
19225 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19226 }
19227 } else if (binding_power != PM_BINDING_POWER_STATEMENT) {
19228 /* Multi targets are not allowed when it's not a statement
19229 * level. */
19230 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19231 } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
19232 /* Multi targets must be followed by an equal sign in order to
19233 * be valid (or a right parenthesis if they are nested). */
19234 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19235 }
19236
19237 return result;
19238 }
19239
19240 /* If we have a single statement and are ending on a right parenthesis
19241 * and we didn't return a multiple assignment node, then we can return a
19242 * regular parentheses node now. */
19243 pm_statements_node_t *statements = pm_statements_node_create(parser);
19244 pm_statements_node_body_append(parser, statements, statement, true);
19245
19246 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19247 }
19248
19249 /* If we have more than one statement in the set of parentheses, then we are
19250 * going to parse all of them as a list of statements. We'll do that here.
19251 */
19252 context_push(parser, PM_CONTEXT_PARENS);
19253 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19254
19255 pm_statements_node_t *statements = pm_statements_node_create(parser);
19256 pm_statements_node_body_append(parser, statements, statement, true);
19257
19258 /* If we didn't find a terminator and we didn't find a right parenthesis,
19259 * then this is a syntax error. */
19260 if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) {
19261 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19262 }
19263
19264 /* Parse each statement within the parentheses. */
19265 while (true) {
19266 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19267 pm_statements_node_body_append(parser, statements, node, true);
19268
19269 /* If we're recovering from a syntax error, then we need to stop parsing
19270 * the statements now. */
19271 if (parser->recovering) {
19272 /* If this is the level of context where the recovery has happened,
19273 * then we can mark the parser as done recovering. */
19274 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false;
19275 break;
19276 }
19277
19278 /* If we couldn't parse an expression at all, then we need to bail out
19279 * of the loop. */
19280 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) break;
19281
19282 /* If we successfully parsed a statement, then we are going to need a
19283 * terminator to delimit them. */
19284 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
19285 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
19286 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break;
19287 } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19288 break;
19289 } else if (!match1(parser, PM_TOKEN_EOF)) {
19290 /* If we're at the end of the file, then we're going to add an error
19291 * after this for the ) anyway. */
19292 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19293 }
19294 }
19295
19296 context_pop(parser);
19297 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19298
19299 /* When we're parsing multi targets, we allow them to be followed by a right
19300 * parenthesis if they are at the statement level. This is only possible if
19301 * they are the final statement in a parentheses. We need to explicitly
19302 * reject that here. */
19303 {
19304 pm_node_t *statement = statements->body.nodes[statements->body.size - 1];
19305
19306 if (PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19307 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
19308 pm_multi_target_node_targets_append(parser, multi_target, statement);
19309
19310 statement = UP(multi_target);
19311 statements->body.nodes[statements->body.size - 1] = statement;
19312 }
19313
19314 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) {
19315 const uint8_t *offset = parser->start + PM_NODE_END(statement);
19316 pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset };
19317 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(statement), 0));
19318
19319 statement = UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value));
19320 statements->body.nodes[statements->body.size - 1] = statement;
19321
19322 pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED);
19323 }
19324 }
19325
19326 pop_block_exits(parser, previous_block_exits);
19327 pm_void_statements_check(parser, statements, true);
19328 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19329}
19330
19336static pm_node_t *
19337parse_splat(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19338 pm_token_t operator = parser->previous;
19339 pm_node_t *name = NULL;
19340
19341 if (token_begins_expression_p(parser->current.type)) {
19342 name = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
19343 }
19344
19345 return UP(pm_splat_node_create(parser, &operator, name));
19346}
19347
19349parse_array_keyword_hash(pm_parser_t *parser, pm_node_t *key, uint8_t flags, uint16_t depth) {
19350 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
19351
19352 pm_static_literals_t hash_keys = { 0 };
19353 pm_hash_key_static_literals_add(parser, &hash_keys, key);
19354
19355 pm_token_t operator = { 0 };
19356 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
19357 operator = parser->previous;
19358 }
19359
19360 pm_node_t *value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_VALUE, (uint16_t) (depth + 1));
19361 pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, NTOK2PTR(operator), value));
19362 pm_keyword_hash_node_elements_append(parser->arena, hash, assoc);
19363
19364 if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19365 parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1));
19366 }
19367
19368 pm_static_literals_free(&hash_keys);
19369 return hash;
19370}
19371
19372static pm_array_node_t *
19373parse_array(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19374 pm_array_node_t *array = pm_array_node_create(parser, &parser->current);
19375 parser_lex(parser);
19376
19377 bool parsed_bare_hash = false;
19378 while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
19379 bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
19380
19381 // Handle the case where we don't have a comma and we have a
19382 // newline followed by a right bracket.
19383 if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19384 break;
19385 }
19386
19387 // Ensure that we have a comma between elements in the array.
19388 if (array->elements.size > 0) {
19389 if (accept1(parser, PM_TOKEN_COMMA)) {
19390 // If there was a comma but we also accepts a newline,
19391 // then this is a syntax error.
19392 if (accepted_newline) {
19393 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
19394 }
19395 } else {
19396 // If there was no comma, then we need to add a syntax
19397 // error.
19398 PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_END(parser, &parser->previous), 0, PM_ERR_ARRAY_SEPARATOR, pm_token_str(parser->current.type));
19399 parser->previous.start = parser->previous.end;
19400 parser->previous.type = 0;
19401 }
19402 }
19403
19404 // If we have a right bracket immediately following a comma,
19405 // this is allowed since it's a trailing comma. In this case we
19406 // can break out of the loop.
19407 if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break;
19408
19409 pm_node_t *element;
19410
19411 if (accept1(parser, PM_TOKEN_USTAR)) {
19412 pm_token_t operator = parser->previous;
19413 pm_node_t *expression = NULL;
19414
19415 if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) {
19416 pm_parser_scope_forwarding_positionals_check(parser, &operator);
19417 } else {
19418 expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_ARRAY_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
19419 }
19420
19421 element = UP(pm_splat_node_create(parser, &operator, expression));
19422 } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) {
19423 if (parsed_bare_hash) {
19424 pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH);
19425 }
19426
19427 element = UP(pm_keyword_hash_node_create(parser));
19428 pm_static_literals_t hash_keys = { 0 };
19429
19430 if (!match8(parser, PM_TOKEN_EOF, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_DO_BLOCK, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_KEYWORD_DO, PM_TOKEN_PARENTHESIS_RIGHT)) {
19431 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
19432 }
19433
19434 pm_static_literals_free(&hash_keys);
19435 parsed_bare_hash = true;
19436 } else {
19437 element = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_LABEL), PM_ERR_ARRAY_EXPRESSION, (uint16_t) (depth + 1));
19438
19439 if (pm_symbol_node_label_p(parser, element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
19440 if (parsed_bare_hash) {
19441 pm_parser_err_previous(parser, PM_ERR_EXPRESSION_BARE_HASH);
19442 }
19443
19444 element = UP(parse_array_keyword_hash(parser, element, flags, depth));
19445 parsed_bare_hash = true;
19446 }
19447 }
19448
19449 pm_array_node_elements_append(parser->arena, array, element);
19450 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
19451 }
19452
19453 accept1(parser, PM_TOKEN_NEWLINE);
19454
19455 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19456 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
19457 parser->previous.start = parser->previous.end;
19458 parser->previous.type = 0;
19459 }
19460
19461 pm_array_node_close_set(parser, array, &parser->previous);
19462 return array;
19463}
19464
19465static pm_hash_node_t *
19466parse_hash(pm_parser_t *parser, uint16_t depth) {
19467 pm_token_t opening = parser->current;
19468 pm_hash_node_t *node = pm_hash_node_create(parser, &opening);
19469 parser_lex(parser);
19470
19471 if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
19472 pm_static_literals_t hash_keys = { 0 };
19473 parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1));
19474 pm_static_literals_free(&hash_keys);
19475
19476 accept1(parser, PM_TOKEN_NEWLINE);
19477 }
19478
19479 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening);
19480 pm_hash_node_closing_loc_set(parser, node, &parser->previous);
19481 return node;
19482}
19483
19484static pm_node_t *
19485parse_heredoc(pm_parser_t *parser, uint16_t depth) {
19486 assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC);
19487 pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base;
19488
19489 size_t common_whitespace = (size_t) -1;
19490 parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace;
19491
19492 parser_lex(parser);
19493 pm_token_t opening = parser->previous;
19494
19495 pm_node_t *node;
19496 pm_node_t *part;
19497
19498 if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19499 // If we get here, then we have an empty heredoc. We'll create
19500 // an empty content token and return an empty string node.
19501 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19502 pm_token_t content = parse_strings_empty_content(parser->previous.start);
19503
19504 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19505 node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19506 } else {
19507 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19508 }
19509
19510 PM_NODE_LENGTH_SET_TOKEN(parser, node, &opening);
19511 } else if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) == NULL) {
19512 // If we get here, then we tried to find something in the
19513 // heredoc but couldn't actually parse anything, so we'll just
19514 // return a missing node.
19515 //
19516 // parse_string_part handles its own errors, so there is no need
19517 // for us to add one here.
19518 node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
19519 } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19520 // If we get here, then the part that we parsed was plain string
19521 // content and we're at the end of the heredoc, so we can return
19522 // just a string node with the heredoc opening and closing as
19523 // its opening and closing.
19524 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
19526
19527 cast->opening_loc = TOK2LOC(parser, &opening);
19528 cast->closing_loc = TOK2LOC(parser, &parser->current);
19529 cast->base.location = cast->opening_loc;
19530
19531 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19532 assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
19533 cast->base.type = PM_X_STRING_NODE;
19534 }
19535
19536 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19537 parse_heredoc_dedent_string(parser->arena, &cast->unescaped, common_whitespace);
19538 }
19539
19540 node = UP(cast);
19541 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19542 } else {
19543 // If we get here, then we have multiple parts in the heredoc,
19544 // so we'll need to create an interpolated string node to hold
19545 // them all.
19546 pm_node_list_t parts = { 0 };
19547 pm_node_list_append_inline(parser->arena, &parts, part);
19548
19549 while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19550 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19551 pm_node_list_append_inline(parser->arena, &parts, part);
19552 }
19553 }
19554
19555 // Now that we have all of the parts, create the correct type of
19556 // interpolated node.
19557 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19558 pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19559 cast->parts = parts;
19560
19561 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19562 pm_interpolated_xstring_node_closing_set(parser, cast, &parser->previous);
19563
19564 cast->base.location = cast->opening_loc;
19565 node = UP(cast);
19566 } else {
19567 pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
19568
19569 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19570 pm_interpolated_string_node_closing_set(parser, cast, &parser->previous);
19571
19572 cast->base.location = cast->opening_loc;
19573 node = UP(cast);
19574 }
19575
19576 // If this is a heredoc that is indented with a ~, then we need
19577 // to dedent each line by the common leading whitespace.
19578 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19579 pm_node_list_t *nodes;
19580 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19581 nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
19582 } else {
19583 nodes = &((pm_interpolated_string_node_t *) node)->parts;
19584 }
19585
19586 parse_heredoc_dedent(parser, nodes, common_whitespace);
19587 }
19588 }
19589
19590 /* If a missing terminator left this heredoc's lex mode on the stack, it
19591 * still points at our stack-local common_whitespace. Clear the pointer so
19592 * that subsequent lexing cannot read from this function's dead stack frame.
19593 */
19594 pm_lex_mode_t *whitespace_mode = parser->lex_modes.current;
19595 do {
19596 if (whitespace_mode->mode == PM_LEX_HEREDOC && whitespace_mode->as.heredoc.common_whitespace == &common_whitespace) {
19597 whitespace_mode->as.heredoc.common_whitespace = NULL;
19598 }
19599 whitespace_mode = whitespace_mode->prev;
19600 } while (whitespace_mode != NULL);
19601
19602 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
19603 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
19604 }
19605
19606 return node;
19607}
19608
19609static pm_begin_node_t *
19610parse_begin(pm_parser_t *parser, uint16_t depth) {
19611 pm_token_t begin_keyword = parser->current;
19612 size_t opening_newline_index = token_newline_index(parser);
19613 parser_lex(parser);
19614
19615 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19616
19617 pm_node_list_t current_block_exits = { 0 };
19618 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19619 pm_statements_node_t *begin_statements = NULL;
19620
19621 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
19622 pm_accepts_block_stack_push(parser, true);
19623 begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN, (uint16_t) (depth + 1));
19624 pm_accepts_block_stack_pop(parser);
19625 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19626 }
19627
19628 pm_begin_node_t *node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
19629 parse_rescues(parser, opening_newline_index, &begin_keyword, node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1));
19630 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword);
19631
19632 PM_NODE_LENGTH_SET_TOKEN(parser, node, &parser->previous);
19633 pm_begin_node_end_keyword_set(parser, node, &parser->previous);
19634 pop_block_exits(parser, previous_block_exits);
19635 return node;
19636}
19637
19638static pm_defined_node_t *
19639parse_defined(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19640 pm_token_t keyword = parser->current;
19641 parser_lex(parser);
19642
19643 pm_token_t lparen = { 0 };
19644 pm_token_t rparen = { 0 };
19645 pm_node_t *expression;
19646
19647 context_push(parser, PM_CONTEXT_DEFINED);
19648 bool newline = accept1(parser, PM_TOKEN_NEWLINE);
19649
19650 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
19651 lparen = parser->previous;
19652
19653 if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19654 expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
19655 lparen = (pm_token_t) { 0 };
19656 } else {
19657 expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19658
19659 if (!parser->recovering) {
19660 accept1(parser, PM_TOKEN_NEWLINE);
19661 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19662 rparen = parser->previous;
19663 }
19664 }
19665 } else {
19666 expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19667 }
19668
19669 context_pop(parser);
19670 return pm_defined_node_create(parser, NTOK2PTR(lparen), expression, NTOK2PTR(rparen), &keyword);
19671}
19672
19673static pm_for_node_t *
19674parse_for(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19675 pm_token_t for_keyword = parser->current;
19676 size_t opening_newline_index = token_newline_index(parser);
19677 parser_lex(parser);
19678
19679 pm_node_t *index;
19680 context_push(parser, PM_CONTEXT_FOR_INDEX);
19681
19682 // First, parse out the first index expression.
19683 if (accept1(parser, PM_TOKEN_USTAR)) {
19684 index = parse_splat(parser, flags, depth);
19685 } else if (token_begins_expression_p(parser->current.type)) {
19686 index = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
19687 } else {
19688 pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX);
19689 index = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &for_keyword), PM_TOKEN_LENGTH(&for_keyword)));
19690 }
19691
19692 // Now, if there are multiple index expressions, parse them out.
19693 if (match1(parser, PM_TOKEN_COMMA)) {
19694 index = parse_targets(parser, index, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19695 } else {
19696 index = parse_target(parser, index, false, false);
19697 }
19698
19699 context_pop(parser);
19700 pm_do_loop_stack_push(parser, true);
19701
19702 expect1(parser, PM_TOKEN_KEYWORD_IN, PM_ERR_FOR_IN);
19703 pm_token_t in_keyword = parser->previous;
19704
19705 pm_node_t *collection = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_FOR_COLLECTION, (uint16_t) (depth + 1));
19706 pm_do_loop_stack_pop(parser);
19707
19708 pm_token_t do_keyword = { 0 };
19709 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
19710 do_keyword = parser->previous;
19711 } else {
19712 if (!match2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)) {
19713 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_str(parser->current.type));
19714 }
19715 }
19716
19717 pm_statements_node_t *statements = NULL;
19718 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
19719 statements = parse_statements(parser, PM_CONTEXT_FOR, (uint16_t) (depth + 1));
19720 }
19721
19722 parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false);
19723 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword);
19724
19725 return pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, NTOK2PTR(do_keyword), &parser->previous);
19726}
19727
19728static pm_lambda_node_t *
19729parse_lambda(pm_parser_t *parser, uint16_t depth) {
19730 int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting;
19731 parser->lambda_enclosure_nesting = parser->enclosure_nesting;
19732
19733 pm_token_t operator = parser->current;
19734 size_t opening_newline_index = token_newline_index(parser);
19735 parser_lex(parser);
19736
19737 pm_parser_scope_push(parser, false);
19738 pm_block_parameters_node_t *block_parameters;
19739
19740 switch (parser->current.type) {
19741 case PM_TOKEN_PARENTHESIS_LEFT: {
19742 pm_token_t opening = parser->current;
19743 parser_lex(parser);
19744
19745 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19746 block_parameters = pm_block_parameters_node_create(parser, NULL, &opening);
19747 } else {
19748 block_parameters = parse_block_parameters(parser, false, &opening, true, true, (uint16_t) (depth + 1));
19749 }
19750
19751 accept1(parser, PM_TOKEN_NEWLINE);
19752 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19753
19754 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
19755 break;
19756 }
19757 case PM_CASE_PARAMETER: {
19758 block_parameters = parse_block_parameters(parser, false, NULL, true, false, (uint16_t) (depth + 1));
19759 break;
19760 }
19761 default: {
19762 block_parameters = NULL;
19763 break;
19764 }
19765 }
19766
19767 pm_token_t opening;
19768 pm_node_t *body = NULL;
19769
19770 if (accept1(parser, PM_TOKEN_LAMBDA_BEGIN)) {
19771 opening = parser->previous;
19772
19773 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
19774 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1)));
19775 }
19776
19777 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
19778
19779 /* Restore the enclosing lambda's nesting now that the body has been
19780 * parsed, so that the token following the closing `}` is lexed in the
19781 * enclosing context. During the body the nesting held this lambda's own
19782 * level, which every token inside the braces sits above. */
19783 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
19784 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
19785 } else {
19786 /* A `-> { }` body is delimited by `{`/`}`, whose enclosure frame the
19787 * lexer manages. A `-> do end` body is delimited by keywords, so push
19788 * the frame here and pop it before `end`. The frame isolates the body
19789 * from an enclosing `while`/`until`/`for` condition, so a `do` inside
19790 * it opens a block rather than the loop body. The push must precede
19791 * consuming the `do`, which lexes the first token of the body. */
19792 pm_enclosure_frame_push(parser);
19793 expect1(parser, PM_TOKEN_KEYWORD_DO_LAMBDA, PM_ERR_LAMBDA_OPEN);
19794 opening = parser->previous;
19795
19796 /* The lexer cleared the nesting when it produced the `do`. If it was
19797 * missing entirely, clear it here so that the body is recovered the
19798 * same way it would have been parsed: no token within it sits at the
19799 * beginning of a lambda. */
19800 parser->lambda_enclosure_nesting = -1;
19801
19802 if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
19803 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1)));
19804 }
19805
19806 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
19807 assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE));
19808 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)));
19809 } else {
19810 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
19811 }
19812
19813 pm_enclosure_frame_pop(parser);
19814
19815 /* As with the brace branch above, restore the nesting before consuming
19816 * the closing `end`, which lexes the token that follows it. */
19817 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
19818 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator);
19819 }
19820
19821 pm_constant_id_list_t locals;
19822 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
19823 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &operator, &parser->previous);
19824
19825 pm_parser_scope_pop(parser);
19826 return pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body);
19827}
19828
19829static pm_node_t *
19830parse_regexp(pm_parser_t *parser, uint16_t depth) {
19831 pm_token_t opening = parser->current;
19832 parser_lex(parser);
19833
19834 if (match1(parser, PM_TOKEN_REGEXP_END)) {
19835 // If we get here, then we have an end immediately after a start. In
19836 // that case we'll create an empty content token and return an
19837 // uninterpolated regular expression.
19838 pm_token_t content = (pm_token_t) {
19839 .type = PM_TOKEN_STRING_CONTENT,
19840 .start = parser->previous.end,
19841 .end = parser->previous.end
19842 };
19843
19844 parser_lex(parser);
19845
19846 pm_regular_expression_node_t *node = pm_regular_expression_node_create(parser, &opening, &content, &parser->previous);
19847 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
19848 return UP(node);
19849 }
19850
19852
19853 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
19854 // In this case we've hit string content so we know the regular
19855 // expression at least has something in it. We'll need to check if the
19856 // following token is the end (in which case we can return a plain
19857 // regular expression) or if it's not then it has interpolation.
19858 pm_string_t unescaped = parser->current_string;
19859 pm_token_t content = parser->current;
19860 parser_lex(parser);
19861
19862 // If we hit an end, then we can create a regular expression
19863 // node without interpolation, which can be represented more
19864 // succinctly and more easily compiled.
19865 if (accept1(parser, PM_TOKEN_REGEXP_END)) {
19866 pm_regular_expression_node_t *node = (pm_regular_expression_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
19867
19868 // If we're not immediately followed by a =~, then we
19869 // parse and validate now. If it is followed by a =~,
19870 // then it will get parsed in the =~ handler where
19871 // named captures can also be extracted.
19872 if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) {
19873 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
19874 }
19875
19876 return UP(node);
19877 }
19878
19879 // If we get here, then we have interpolation so we'll need to create
19880 // a regular expression node with interpolation.
19881 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
19882
19883 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
19884 if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
19885 // This is extremely strange, but the first string part of a
19886 // regular expression will always be tagged as binary if we
19887 // are in a US-ASCII file, no matter its contents.
19888 pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING);
19889 }
19890
19891 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
19892 } else {
19893 // If the first part of the body of the regular expression is not a
19894 // string content, then we have interpolation and we need to create an
19895 // interpolated regular expression node.
19896 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
19897 }
19898
19899 // Now that we're here and we have interpolation, we'll parse all of the
19900 // parts into the list.
19901 pm_node_t *part;
19902 while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) {
19903 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19904 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
19905 }
19906 }
19907
19908 pm_token_t closing = parser->current;
19909 if (match1(parser, PM_TOKEN_EOF)) {
19910 pm_parser_err_token(parser, &opening, PM_ERR_REGEXP_TERM);
19911 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19912 } else {
19913 expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM);
19914 }
19915
19916 pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing);
19917 return UP(interpolated);
19918}
19919
19920static pm_node_t *
19921parse_alias(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
19922 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
19923 pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS);
19924 }
19925
19926 pm_token_t keyword = parser->current;
19927 parser_lex(parser);
19928
19929 pm_node_t *new_name = parse_alias_argument(parser, true, (uint16_t) (depth + 1));
19930 pm_node_t *old_name = parse_alias_argument(parser, false, (uint16_t) (depth + 1));
19931
19932 switch (PM_NODE_TYPE(new_name)) {
19933 case PM_BACK_REFERENCE_READ_NODE:
19934 case PM_NUMBERED_REFERENCE_READ_NODE:
19935 case PM_GLOBAL_VARIABLE_READ_NODE: {
19936 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)) {
19937 if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) {
19938 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE);
19939 }
19940 } else if (!PM_NODE_TYPE_P(old_name, PM_ERROR_RECOVERY_NODE)) {
19941 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
19942 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
19943 }
19944
19945 return UP(pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name));
19946 }
19947 case PM_SYMBOL_NODE:
19948 case PM_INTERPOLATED_SYMBOL_NODE: {
19949 if (!PM_NODE_TYPE_P(old_name, PM_SYMBOL_NODE) && !PM_NODE_TYPE_P(old_name, PM_INTERPOLATED_SYMBOL_NODE) && !PM_NODE_TYPE_P(old_name, PM_ERROR_RECOVERY_NODE)) {
19950 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
19951 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
19952 }
19953 }
19955 default:
19956 return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name));
19957 }
19958}
19959
19960static pm_until_node_t *
19961parse_until(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19962 pm_token_t keyword = parser->current;
19963 size_t opening_newline_index = token_newline_index(parser);
19964
19965 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
19966 pm_do_loop_stack_push(parser, true);
19967
19968 parser_lex(parser);
19969 pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1));
19970
19971 pm_do_loop_stack_pop(parser);
19972 context_pop(parser);
19973
19974 pm_token_t do_keyword = { 0 };
19975 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
19976 do_keyword = parser->previous;
19977 } else {
19978 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
19979 }
19980
19981 pm_statements_node_t *statements = NULL;
19982 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
19983 pm_accepts_block_stack_push(parser, true);
19984 statements = parse_statements(parser, PM_CONTEXT_UNTIL, (uint16_t) (depth + 1));
19985 pm_accepts_block_stack_pop(parser);
19986 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19987 }
19988
19989 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
19990 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword);
19991
19992 return pm_until_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0);
19993}
19994
19995static pm_while_node_t *
19996parse_while(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19997 pm_token_t keyword = parser->current;
19998 size_t opening_newline_index = token_newline_index(parser);
19999
20000 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
20001 pm_do_loop_stack_push(parser, true);
20002
20003 parser_lex(parser);
20004 pm_node_t *predicate = parse_value_expression(parser, PM_BINDING_POWER_COMPOSITION, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1));
20005
20006 pm_do_loop_stack_pop(parser);
20007 context_pop(parser);
20008
20009 pm_token_t do_keyword = { 0 };
20010 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20011 do_keyword = parser->previous;
20012 } else {
20013 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
20014 }
20015
20016 pm_statements_node_t *statements = NULL;
20017 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20018 pm_accepts_block_stack_push(parser, true);
20019 statements = parse_statements(parser, PM_CONTEXT_WHILE, (uint16_t) (depth + 1));
20020 pm_accepts_block_stack_pop(parser);
20021 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20022 }
20023
20024 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
20025 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword);
20026
20027 return pm_while_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0);
20028}
20029
20030static pm_node_t *
20031parse_xstring(pm_parser_t *parser, uint16_t depth) {
20032 pm_token_t opening = parser->current;
20033 parser_lex(parser);
20034
20035 // When we get here, we don't know if this string is going to have
20036 // interpolation or not, even though it is allowed. Still, we want to be
20037 // able to return a string node without interpolation if we can since
20038 // it'll be faster.
20039 if (match1(parser, PM_TOKEN_STRING_END)) {
20040 // If we get here, then we have an end immediately after a start. In
20041 // that case we'll create an empty content token and return an
20042 // uninterpolated string.
20043 pm_token_t content = (pm_token_t) {
20044 .type = PM_TOKEN_STRING_CONTENT,
20045 .start = parser->previous.end,
20046 .end = parser->previous.end
20047 };
20048
20049 parser_lex(parser);
20050 return UP(pm_xstring_node_create(parser, &opening, &content, &parser->previous));
20051 }
20052
20054
20055 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20056 // In this case we've hit string content so we know the string
20057 // at least has something in it. We'll need to check if the
20058 // following token is the end (in which case we can return a
20059 // plain string) or if it's not then it has interpolation.
20060 pm_string_t unescaped = parser->current_string;
20061 pm_token_t content = parser->current;
20062 parser_lex(parser);
20063
20064 if (match1(parser, PM_TOKEN_STRING_END)) {
20065 pm_node_t *node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
20066 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
20067 parser_lex(parser);
20068 return node;
20069 }
20070
20071 // If we get here, then we have interpolation so we'll need to
20072 // create a string node with interpolation.
20073 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20074
20075 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
20076 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
20077
20078 pm_interpolated_xstring_node_append(parser->arena, node, part);
20079 } else {
20080 // If the first part of the body of the string is not a string
20081 // content, then we have interpolation and we need to create an
20082 // interpolated string node.
20083 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20084 }
20085
20086 pm_node_t *part;
20087 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20088 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
20089 pm_interpolated_xstring_node_append(parser->arena, node, part);
20090 }
20091 }
20092
20093 pm_token_t closing = parser->current;
20094 if (match1(parser, PM_TOKEN_EOF)) {
20095 pm_parser_err_token(parser, &opening, PM_ERR_XSTRING_TERM);
20096 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20097 } else {
20098 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM);
20099 }
20100 pm_interpolated_xstring_node_closing_set(parser, node, &closing);
20101
20102 return UP(node);
20103}
20104
20105static pm_node_t *
20106parse_multi_target(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_power, uint16_t depth) {
20107 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
20108 /* If we get here, then we have a comma immediately following something
20109 * that could have been a target in a multiple assignment. */
20110 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20111 }
20112 return node;
20113}
20114
20115static pm_node_t *
20116parse_identifier(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20117 pm_token_t identifier = parser->current;
20118 parser_lex(parser);
20119
20120 pm_node_t *node = parse_variable_call(parser);
20121
20122 if (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
20123 // If parse_variable_call returned with a call node, then we
20124 // know the identifier is not in the local table. In that case
20125 // we need to check if there are arguments following the
20126 // identifier.
20127 pm_call_node_t *call = (pm_call_node_t *) node;
20128 pm_arguments_t arguments = { 0 };
20129
20130 if (parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1))) {
20131 // Since we found arguments, we need to turn off the
20132 // variable call bit in the flags.
20133 pm_node_flag_unset(UP(call), PM_CALL_NODE_FLAGS_VARIABLE_CALL);
20134
20135 call->opening_loc = arguments.opening_loc;
20136 call->arguments = arguments.arguments;
20137 call->closing_loc = arguments.closing_loc;
20138 call->block = arguments.block;
20139
20140 const pm_location_t *end = pm_arguments_end(&arguments);
20141 if (end == NULL) {
20142 PM_NODE_LENGTH_SET_LOCATION(call, &call->message_loc);
20143 } else {
20144 PM_NODE_LENGTH_SET_LOCATION(call, end);
20145 }
20146 }
20147 } else {
20148 // Otherwise, we know the identifier is in the local table. This
20149 // can still be a method call if it is followed by arguments or
20150 // a block, so we need to check for that here.
20151 if (
20152 ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
20153 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
20154 match1(parser, PM_TOKEN_BRACE_LEFT)
20155 ) {
20156 pm_arguments_t arguments = { 0 };
20157 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20158 pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments);
20159
20160 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
20161 // If we're about to convert an 'it' implicit local
20162 // variable read into a method call, we need to remove
20163 // it from the list of implicit local variables.
20164 pm_node_unreference(parser, node);
20165 } else {
20166 // Otherwise, we're about to convert a regular local
20167 // variable read into a method call, in which case we
20168 // need to indicate that this was not a read for the
20169 // purposes of warnings.
20170 assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE));
20171
20172 if (pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &identifier), PM_TOKEN_LENGTH(&identifier))) {
20173 pm_node_unreference(parser, node);
20174 } else {
20176 pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
20177 }
20178 }
20179
20180 return UP(fcall);
20181 }
20182 }
20183
20184 return parse_multi_target(parser, node, binding_power, depth);
20185}
20186
20187static pm_array_node_t *
20188parse_uninterpolated_symbol_array(pm_parser_t *parser) {
20189 pm_token_t opening = parser->current;
20190 parser_lex(parser);
20191
20192 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20193 pm_node_t *current = NULL;
20194
20195 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20196 accept1(parser, PM_TOKEN_WORDS_SEP);
20197 if (match1(parser, PM_TOKEN_STRING_END)) break;
20198
20199 // Interpolation is not possible but nested heredocs can still lead to
20200 // consecutive (disjoint) string tokens when the final newline is escaped.
20201 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20202 // Record the string node, moving to interpolation if needed.
20203 if (current == NULL) {
20204 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
20205 parser_lex(parser);
20206 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20207 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20208 parser_lex(parser);
20209 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
20210 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20211 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
20212 pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start + cast->content_loc.start, .end = parser->start + cast->content_loc.start + cast->content_loc.length };
20213 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
20214 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
20215 parser_lex(parser);
20216
20217 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20218 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
20219 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
20220
20221 // current is arena-allocated so no explicit free is needed.
20222 current = UP(interpolated);
20223 } else {
20224 assert(false && "unreachable");
20225 }
20226 }
20227
20228 if (current) {
20229 pm_array_node_elements_append(parser->arena, array, current);
20230 current = NULL;
20231 } else {
20232 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT);
20233 }
20234 }
20235
20236 pm_token_t closing = parser->current;
20237 if (match1(parser, PM_TOKEN_EOF)) {
20238 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_LOWER_TERM);
20239 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20240 } else {
20241 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM);
20242 }
20243 pm_array_node_close_set(parser, array, &closing);
20244
20245 return array;
20246}
20247
20248static pm_array_node_t *
20249parse_interpolated_symbol_array(pm_parser_t *parser, uint16_t depth) {
20250 parser_lex(parser);
20251 pm_token_t opening = parser->previous;
20252 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20253
20254 /* This is the current node that we are parsing that will be added to the
20255 * list of elements. */
20256 pm_node_t *current = NULL;
20257
20258 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20259 switch (parser->current.type) {
20260 case PM_TOKEN_WORDS_SEP: {
20261 /* Reset the explicit encoding if we hit a separator since each
20262 * element can have its own encoding. */
20263 parser->explicit_encoding = NULL;
20264
20265 if (current == NULL) {
20266 /* If we hit a separator before we have any content, then we
20267 * don't need to do anything. */
20268 } else {
20269 /* If we hit a separator after we've hit content, then we
20270 * need to append that content to the list and reset the
20271 * current node. */
20272 pm_array_node_elements_append(parser->arena, array, current);
20273 current = NULL;
20274 }
20275
20276 parser_lex(parser);
20277 break;
20278 }
20279 case PM_TOKEN_STRING_CONTENT: {
20280 if (current == NULL) {
20281 /* If we hit content and the current node is NULL, then this
20282 * is the first string content we've seen. In that case
20283 * we're going to create a new string node and set that to
20284 * the current. */
20285 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
20286 parser_lex(parser);
20287 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20288 /* If we hit string content and the current node is an
20289 * interpolated string, then we need to append the string
20290 * content to the list of child nodes. */
20291 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20292 parser_lex(parser);
20293
20294 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
20295 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20296 /* If we hit string content and the current node is a symbol
20297 * node, then we need to convert the current node into an
20298 * interpolated string and add the string content to the
20299 * list of child nodes. */
20300 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
20301 pm_token_t content = {
20302 .type = PM_TOKEN_STRING_CONTENT,
20303 .start = parser->start + cast->content_loc.start,
20304 .end = parser->start + cast->content_loc.start + cast->content_loc.length
20305 };
20306
20307 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
20308 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
20309 parser_lex(parser);
20310
20311 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20312 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
20313 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
20314
20315 current = UP(interpolated);
20316 } else {
20317 assert(false && "unreachable");
20318 }
20319
20320 break;
20321 }
20322 case PM_TOKEN_EMBVAR: {
20323 bool start_location_set = false;
20324 if (current == NULL) {
20325 /* If we hit an embedded variable and the current node is
20326 * NULL, then this is the start of a new string. We'll set
20327 * the current node to a new interpolated string. */
20328 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
20329 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20330 /* If we hit an embedded variable and the current node is a
20331 * string node, then we'll convert the current into an
20332 * interpolated string and add the string node to the list
20333 * of parts. */
20334 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20335
20336 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
20337 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
20338 PM_NODE_START_SET_NODE(interpolated, current);
20339 start_location_set = true;
20340 current = UP(interpolated);
20341 } else {
20342 /* If we hit an embedded variable and the current node is an
20343 * interpolated string, then we'll just add the embedded
20344 * variable. */
20345 }
20346
20347 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20348 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
20349 if (!start_location_set) {
20350 PM_NODE_START_SET_NODE(current, part);
20351 }
20352 break;
20353 }
20354 case PM_TOKEN_EMBEXPR_BEGIN: {
20355 bool start_location_set = false;
20356 if (current == NULL) {
20357 /* If we hit an embedded expression and the current node is
20358 * NULL, then this is the start of a new string. We'll set
20359 * the current node to a new interpolated string. */
20360 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
20361 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20362 /* If we hit an embedded expression and the current node is
20363 * a string node, then we'll convert the current into an
20364 * interpolated string and add the string node to the list
20365 * of parts. */
20366 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20367
20368 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
20369 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
20370 PM_NODE_START_SET_NODE(interpolated, current);
20371 start_location_set = true;
20372 current = UP(interpolated);
20373 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20374 /* If we hit an embedded expression and the current node is
20375 * an interpolated string, then we'll just continue on. */
20376 } else {
20377 assert(false && "unreachable");
20378 }
20379
20380 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20381 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
20382 if (!start_location_set) {
20383 PM_NODE_START_SET_NODE(current, part);
20384 }
20385 break;
20386 }
20387 default:
20388 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_UPPER_ELEMENT);
20389 parser_lex(parser);
20390 break;
20391 }
20392 }
20393
20394 /* If we have a current node, then we need to append it to the list. */
20395 if (current) {
20396 pm_array_node_elements_append(parser->arena, array, current);
20397 }
20398
20399 pm_token_t closing = parser->current;
20400 if (match1(parser, PM_TOKEN_EOF)) {
20401 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_UPPER_TERM);
20402 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20403 } else {
20404 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM);
20405 }
20406 pm_array_node_close_set(parser, array, &closing);
20407
20408 return array;
20409}
20410
20411static pm_array_node_t *
20412parse_uninterpolated_string_array(pm_parser_t *parser) {
20413 pm_token_t opening = parser->current;
20414 parser_lex(parser);
20415
20416 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20417 pm_node_t *current = NULL;
20418
20419 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20420 accept1(parser, PM_TOKEN_WORDS_SEP);
20421 if (match1(parser, PM_TOKEN_STRING_END)) break;
20422
20423 // Interpolation is not possible but nested heredocs can still lead to
20424 // consecutive (disjoint) string tokens when the final newline is escaped.
20425 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20426 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20427
20428 // Record the string node, moving to interpolation if needed.
20429 if (current == NULL) {
20430 current = string;
20431 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20432 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
20433 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20434 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20435 pm_interpolated_string_node_append(parser, interpolated, current);
20436 pm_interpolated_string_node_append(parser, interpolated, string);
20437 current = UP(interpolated);
20438 } else {
20439 assert(false && "unreachable");
20440 }
20441 parser_lex(parser);
20442 }
20443
20444 if (current) {
20445 pm_array_node_elements_append(parser->arena, array, current);
20446 current = NULL;
20447 } else {
20448 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT);
20449 }
20450 }
20451
20452 pm_token_t closing = parser->current;
20453 if (match1(parser, PM_TOKEN_EOF)) {
20454 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_LOWER_TERM);
20455 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20456 } else {
20457 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM);
20458 }
20459
20460 pm_array_node_close_set(parser, array, &closing);
20461 return array;
20462}
20463
20464static pm_array_node_t *
20465parse_interpolated_string_array(pm_parser_t *parser, uint16_t depth) {
20466 pm_token_t opening = parser->current;
20467 parser_lex(parser);
20468
20469 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20470 pm_node_t *current = NULL;
20471
20472 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20473 switch (parser->current.type) {
20474 case PM_TOKEN_WORDS_SEP: {
20475 /* Reset the explicit encoding if we hit a separator since each
20476 * element can have its own encoding. */
20477 parser->explicit_encoding = NULL;
20478
20479 if (current == NULL) {
20480 /* If we hit a separator before we have any content, then we
20481 * don't need to do anything. */
20482 } else {
20483 /* If we hit a separator after we've hit content, then we
20484 * need to append that content to the list and reset the
20485 * current node. */
20486 pm_array_node_elements_append(parser->arena, array, current);
20487 current = NULL;
20488 }
20489
20490 parser_lex(parser);
20491 break;
20492 }
20493 case PM_TOKEN_STRING_CONTENT: {
20494 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20495 pm_node_flag_set(string, parse_unescaped_encoding(parser, parser->explicit_encoding));
20496 parser_lex(parser);
20497
20498 if (current == NULL) {
20499 /* If we hit content and the current node is NULL, then this
20500 * is the first string content we've seen. In that case
20501 * we're going to create a new string node and set that to
20502 * the current. */
20503 current = string;
20504 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20505 /* If we hit string content and the current node is an
20506 * interpolated string, then we need to append the string
20507 * content to the list of child nodes. */
20508 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
20509 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20510 /* If we hit string content and the current node is a string
20511 * node, then we need to convert the current node into an
20512 * interpolated string and add the string content to the
20513 * list of child nodes. */
20514 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20515 pm_interpolated_string_node_append(parser, interpolated, current);
20516 pm_interpolated_string_node_append(parser, interpolated, string);
20517 current = UP(interpolated);
20518 } else {
20519 assert(false && "unreachable");
20520 }
20521
20522 break;
20523 }
20524 case PM_TOKEN_EMBVAR: {
20525 if (current == NULL) {
20526 /* If we hit an embedded variable and the current node is
20527 * NULL, then this is the start of a new string. We'll set
20528 * the current node to a new interpolated string. */
20529 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
20530 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20531 /* If we hit an embedded variable and the current node is a
20532 * string node, then we'll convert the current into an
20533 * interpolated string and add the string node to the list
20534 * of parts. */
20535 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20536 pm_interpolated_string_node_append(parser, interpolated, current);
20537 current = UP(interpolated);
20538 } else {
20539 /* If we hit an embedded variable and the current node is an
20540 * interpolated string, then we'll just add the embedded
20541 * variable. */
20542 }
20543
20544 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20545 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
20546 break;
20547 }
20548 case PM_TOKEN_EMBEXPR_BEGIN: {
20549 if (current == NULL) {
20550 /* If we hit an embedded expression and the current node is
20551 * NULL, then this is the start of a new string. We'll set
20552 * the current node to a new interpolated string. */
20553 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
20554 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20555 /* If we hit an embedded expression and the current node is
20556 * a string node, then we'll convert the current into an
20557 * interpolated string and add the string node to the list
20558 * of parts. */
20559 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20560 pm_interpolated_string_node_append(parser, interpolated, current);
20561 current = UP(interpolated);
20562 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20563 /* If we hit an embedded expression and the current node is
20564 * an interpolated string, then we'll just continue on. */
20565 } else {
20566 assert(false && "unreachable");
20567 }
20568
20569 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20570 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
20571 break;
20572 }
20573 default:
20574 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_UPPER_ELEMENT);
20575 parser_lex(parser);
20576 break;
20577 }
20578 }
20579
20580 /* If we have a current node, then we need to append it to the list. */
20581 if (current) {
20582 pm_array_node_elements_append(parser->arena, array, current);
20583 }
20584
20585 pm_token_t closing = parser->current;
20586 if (match1(parser, PM_TOKEN_EOF)) {
20587 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_UPPER_TERM);
20588 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20589 } else {
20590 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM);
20591 }
20592
20593 pm_array_node_close_set(parser, array, &closing);
20594 return array;
20595}
20596
20597static pm_node_t *
20598parse_jump(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20599 parser_lex(parser);
20600
20601 pm_token_t keyword = parser->previous;
20602 pm_arguments_t arguments = { 0 };
20603
20604 if (token_begins_expression_p(parser->current.type) || match2(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)) {
20605 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
20606
20607 if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
20608 pm_token_t next = parser->current;
20609
20610 /* The arguments of a jump start a fresh command context, so a
20611 * `do` inside them binds to the nearest call within the
20612 * arguments rather than to an enclosing command call, as in
20613 * `check 1, case x when 1 then return bar do end end`. */
20614 pm_accepts_block_stack_push(parser, true);
20615 parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
20616 pm_accepts_block_stack_pop(parser);
20617
20618 // Reject `foo && return bar`.
20619 if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) {
20620 PM_PARSER_ERR_TOKEN_FORMAT(parser, &next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(next.type));
20621 }
20622
20623 // Reject a trailing comma, e.g. `return a,`. The arguments
20624 // parser silently accepts a trailing comma only when it is
20625 // immediately followed by the EOF terminator; in every other
20626 // case (e.g. `return a,;`) it reports the dangling comma
20627 // itself. We reject the accepted case here to stay in line
20628 // with the command call argument parsing above.
20629 if (parser->previous.type == PM_TOKEN_COMMA && match1(parser, PM_TOKEN_EOF)) {
20630 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
20631 }
20632 }
20633
20634 // It's possible that we've parsed a block argument through our
20635 // call to parse_arguments. If we found one, we should mark it
20636 // as invalid and destroy it, as we don't have a place for it.
20637 if (arguments.block != NULL) {
20638 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20639 pm_node_unreference(parser, arguments.block);
20640 arguments.block = NULL;
20641 }
20642 }
20643
20644 switch (keyword.type) {
20645 case PM_TOKEN_KEYWORD_BREAK: {
20646 pm_node_t *node = UP(pm_break_node_create(parser, &keyword, arguments.arguments));
20647 if (!parser->partial_script) ensure_valid_jump(parser, node);
20648 return node;
20649 }
20650 case PM_TOKEN_KEYWORD_NEXT: {
20651 pm_node_t *node = UP(pm_next_node_create(parser, &keyword, arguments.arguments));
20652 if (!parser->partial_script) ensure_valid_jump(parser, node);
20653 return node;
20654 }
20655 case PM_TOKEN_KEYWORD_RETURN: {
20656 pm_node_t *node = UP(pm_return_node_create(parser, &keyword, arguments.arguments));
20657 ensure_valid_return(parser, node);
20658 return node;
20659 }
20660 default:
20661 assert(false && "unreachable");
20662 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20663 }
20664}
20665
20666static pm_node_t *
20667parse_not(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20668 pm_token_t message = parser->current;
20669 parser_lex(parser);
20670
20671 pm_arguments_t arguments = { 0 };
20672 pm_node_t *receiver = NULL;
20673
20674 /* The `not` keyword without parentheses is only valid in contexts where it
20675 * would be parsed as an expression (i.e., at or below the `not` binding
20676 * power level). In other contexts (e.g., method arguments, array elements,
20677 * assignment right-hand sides), parentheses are required: `not(x)`. An
20678 * exception is made for endless def bodies, where `not` is valid as both
20679 * `arg` and `command` (e.g., `def f = not 1`, `def f = not foo bar`). */
20680 if (binding_power > PM_BINDING_POWER_NOT && !(flags & PM_PARSE_IN_ENDLESS_DEF) && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
20681 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) {
20682 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->previous), 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN);
20683 } else {
20684 accept1(parser, PM_TOKEN_NEWLINE);
20685 pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER);
20686 }
20687
20688 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
20689 }
20690
20691 accept1(parser, PM_TOKEN_NEWLINE);
20692
20693 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
20694 pm_token_t lparen = parser->previous;
20695
20696 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20697 receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
20698 } else {
20699 arguments.opening_loc = TOK2LOC(parser, &lparen);
20700 receiver = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1));
20701
20702 if (!parser->recovering) {
20703 accept1(parser, PM_TOKEN_NEWLINE);
20704 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20705 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
20706 }
20707 }
20708 } else {
20709 /* The operand of `not` may be a command call, except at the start of
20710 * an endless method body, where it may be one only if the body may
20711 * be: `def f = not foo 1` has a command body, so it is rejected where
20712 * that is not accepted (`if def f = not foo 1; end`). A nested `not`
20713 * is still at the start of the body, so the operand carries the
20714 * endless method context on (`def f = not not foo 1`). */
20715 uint8_t operand_flags = (flags & PM_PARSE_IN_ENDLESS_DEF)
20716 ? (uint8_t) (flags & (PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_IN_ENDLESS_DEF))
20717 : PM_PARSE_ACCEPTS_COMMAND_CALL;
20718 receiver = parse_expression(parser, PM_BINDING_POWER_NOT, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | operand_flags), PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1));
20719 }
20720
20721 return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
20722}
20723
20725parse_preexe(pm_parser_t *parser, pm_binding_power_t binding_power, uint16_t depth) {
20726 pm_node_list_t current_block_exits = { 0 };
20727 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
20728
20729 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20730 pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
20731 }
20732
20733 pm_token_t keyword = parser->current;
20734 parser_lex(parser);
20735
20736 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_BEGIN_UPCASE_BRACE);
20737 pm_token_t opening = parser->previous;
20738 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE, (uint16_t) (depth + 1));
20739
20740 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening);
20741 pm_context_t context = parser->current_context->context;
20742 if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
20743 pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
20744 }
20745
20746 flush_block_exits(parser, previous_block_exits);
20747 return pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
20748}
20749
20751parse_postexe(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20752 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20753 pm_parser_err_current(parser, PM_ERR_STATEMENT_POSTEXE_END);
20754 }
20755
20756 pm_token_t keyword = parser->current;
20757 parser_lex(parser);
20758
20759 if (context_def_p(parser)) {
20760 pm_parser_warn_token(parser, &keyword, PM_WARN_END_IN_METHOD);
20761 }
20762
20763 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_END_UPCASE_BRACE);
20764 pm_token_t opening = parser->previous;
20765 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE, (uint16_t) (depth + 1));
20766
20767 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening);
20768 return pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
20769}
20770
20771static pm_undef_node_t *
20772parse_undef(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20773 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20774 pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF);
20775 }
20776
20777 parser_lex(parser);
20778 pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous);
20779 pm_node_t *name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20780
20781 if (!PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20782 pm_undef_node_append(parser->arena, undef, name);
20783
20784 while (match1(parser, PM_TOKEN_COMMA)) {
20785 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
20786 parser_lex(parser);
20787
20788 name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20789 if (PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20790 break;
20791 }
20792
20793 pm_undef_node_append(parser->arena, undef, name);
20794 }
20795 }
20796
20797 return undef;
20798}
20799
20800static pm_yield_node_t *
20801parse_yield(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20802 pm_token_t keyword = parser->current;
20803 parser_lex(parser);
20804
20805 pm_arguments_t arguments = { 0 };
20806 parse_arguments_list(parser, &arguments, false, flags, (uint16_t) (depth + 1));
20807
20808 /* It's possible that we've parsed a block argument through our call to
20809 * parse_arguments_list. If we found one, we should mark it as invalid and
20810 * destroy it, as we don't have a place for it on the yield node. */
20811 if (arguments.block != NULL) {
20812 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20813 pm_node_unreference(parser, arguments.block);
20814 arguments.block = NULL;
20815 }
20816
20817 pm_yield_node_t *node = pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc);
20818 if (!parser->parsing_eval && !parser->partial_script) ensure_valid_yield(parser, node);
20819
20820 return node;
20821}
20822
20823static pm_node_t *
20824parse_char(pm_parser_t *parser, uint16_t depth) {
20825 pm_node_t *node = UP(pm_string_node_create_current_string(
20826 parser,
20827 &(pm_token_t) {
20828 .type = PM_TOKEN_STRING_BEGIN,
20829 .start = parser->current.start,
20830 .end = parser->current.start + 1
20831 },
20832 &(pm_token_t) {
20833 .type = PM_TOKEN_STRING_CONTENT,
20834 .start = parser->current.start + 1,
20835 .end = parser->current.end
20836 },
20837 NULL
20838 ));
20839
20840 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
20841
20842 /* Skip past the character literal here, since now we have handled
20843 * parser->explicit_encoding correctly. */
20844 parser_lex(parser);
20845
20846 /* Characters can be followed by strings in which case they are
20847 * automatically concatenated. */
20848 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
20849 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
20850 }
20851
20852 return node;
20853}
20854
20855static pm_node_t *
20856parse_constant(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20857 pm_token_t constant = parser->current;
20858 parser_lex(parser);
20859
20860 /* If a constant is immediately followed by parentheses, then this is in
20861 * fact a method call, not a constant read. */
20862 if (
20863 match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
20864 ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
20865 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
20866 match1(parser, PM_TOKEN_BRACE_LEFT)
20867 ) {
20868 pm_arguments_t arguments = { 0 };
20869 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20870 return UP(pm_call_node_fcall_create(parser, &constant, &arguments));
20871 }
20872
20873 return parse_multi_target(parser, UP(pm_constant_read_node_create(parser, &parser->previous)), binding_power, depth);
20874}
20875
20876static pm_node_t *
20877parse_constant_path_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint16_t depth) {
20878 pm_token_t delimiter = parser->current;
20879 parser_lex(parser);
20880
20881 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
20882 return parse_multi_target(parser, UP(pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous)), binding_power, depth);
20883}
20884
20885static pm_call_node_t *
20886parse_unary_plus(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20887 if (binding_power > PM_BINDING_POWER_UNARY) {
20888 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20889 }
20890 parser_lex(parser);
20891
20892 pm_token_t operator = parser->previous;
20893 pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20894 return pm_call_node_unary_create(parser, &operator, receiver, "+@");
20895}
20896
20897static pm_call_node_t *
20898parse_unary_minus(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20899 if (binding_power > PM_BINDING_POWER_UNARY) {
20900 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20901 }
20902 parser_lex(parser);
20903
20904 pm_token_t operator = parser->previous;
20905 pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20906 return pm_call_node_unary_create(parser, &operator, receiver, "-@");
20907}
20908
20909static pm_node_t *
20910parse_unary_minus_number(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20911 pm_token_t operator = parser->current;
20912 parser_lex(parser);
20913
20914 pm_node_t *node = parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20915
20916 if (accept1(parser, PM_TOKEN_STAR_STAR)) {
20917 pm_token_t exponent_operator = parser->previous;
20918 pm_node_t *exponent = parse_expression(parser, pm_binding_powers[exponent_operator.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
20919 node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0));
20920 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20921 } else {
20922 switch (PM_NODE_TYPE(node)) {
20923 case PM_INTEGER_NODE:
20924 case PM_FLOAT_NODE:
20925 case PM_RATIONAL_NODE:
20926 case PM_IMAGINARY_NODE:
20927 parse_negative_numeric(node);
20928 break;
20929 default:
20930 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20931 break;
20932 }
20933 }
20934
20935 return node;
20936}
20937
20938static pm_node_t *
20939parse_unary_star(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
20940 parser_lex(parser);
20941
20942 /* * operators at the beginning of expressions are only valid in the context
20943 * of a multiple assignment. We enforce that here. We'll still lex past it
20944 * though and create an error recovery node. */
20945 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20946 pm_parser_err_prefix(parser, diag_id);
20947 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20948 }
20949
20950 pm_node_t *splat = parse_splat(parser, flags, depth);
20951
20952 if (match1(parser, PM_TOKEN_COMMA)) {
20953 return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20954 } else {
20955 return parse_target_validate(parser, splat, true);
20956 }
20957}
20958
20959static pm_node_t *
20960parse_super(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20961 pm_token_t keyword = parser->current;
20962 parser_lex(parser);
20963
20964 pm_arguments_t arguments = { 0 };
20965 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20966
20967 if (
20968 arguments.opening_loc.length == 0 &&
20969 arguments.arguments == NULL &&
20970 ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE))
20971 ) {
20972 return UP(pm_forwarding_super_node_create(parser, &keyword, &arguments));
20973 }
20974
20975 return UP(pm_super_node_create(parser, &keyword, &arguments));
20976}
20977
20978static pm_range_node_t *
20979parse_range(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20980 pm_token_t operator = parser->current;
20981 parser_lex(parser);
20982
20983 pm_node_t *right = parse_expression(parser, pm_binding_powers[operator.type].left, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
20984
20985 /* Unary .. and ... are special because these are non-associative operators
20986 * that can also be unary operators. In this case we need to explicitly
20987 * reject code that has a .. or ... that follows this expression. */
20988 if (match2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
20989 pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
20990 }
20991
20992 return pm_range_node_create(parser, NULL, &operator, right);
20993}
20994
20995static pm_call_node_t *
20996parse_bang(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20997 if (binding_power > PM_BINDING_POWER_UNARY) {
20998 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20999 }
21000
21001 parser_lex(parser);
21002
21003 pm_token_t operator = parser->previous;
21004 pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0)), PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
21005 pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
21006 return pm_call_node_unary_create(parser, &operator, receiver, "!");
21007}
21008
21009static pm_call_node_t *
21010parse_tilde(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
21011 if (binding_power > PM_BINDING_POWER_UNARY) {
21012 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
21013 }
21014
21015 parser_lex(parser);
21016
21017 pm_token_t operator = parser->previous;
21018 pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
21019 return pm_call_node_unary_create(parser, &operator, receiver, "~");
21020}
21021
21025static PRISM_INLINE pm_node_t *
21026parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
21027 switch (parser->current.type) {
21028 case PM_TOKEN_BRACKET_LEFT_ARRAY:
21029 return UP(parse_array(parser, flags, depth));
21030 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
21031 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
21032 return parse_parentheses(parser, binding_power, flags, depth);
21033 case PM_TOKEN_BRACE_LEFT_HASH:
21034 return UP(parse_hash(parser, depth));
21035 case PM_TOKEN_CHARACTER_LITERAL:
21036 return parse_char(parser, depth);
21037 case PM_TOKEN_CONSTANT:
21038 return parse_constant(parser, binding_power, flags, depth);
21039 case PM_TOKEN_UCOLON_COLON:
21040 return parse_constant_path_prefix(parser, binding_power, depth);
21041 case PM_TOKEN_UDOT_DOT:
21042 case PM_TOKEN_UDOT_DOT_DOT:
21043 return UP(parse_range(parser, flags, depth));
21044 case PM_TOKEN_FLOAT:
21045 parser_lex(parser);
21046 return UP(pm_float_node_create(parser, &parser->previous));
21047 case PM_TOKEN_FLOAT_IMAGINARY:
21048 parser_lex(parser);
21049 return UP(pm_float_node_imaginary_create(parser, &parser->previous));
21050 case PM_TOKEN_FLOAT_RATIONAL:
21051 parser_lex(parser);
21052 return UP(pm_float_node_rational_create(parser, &parser->previous));
21053 case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY:
21054 parser_lex(parser);
21055 return UP(pm_float_node_rational_imaginary_create(parser, &parser->previous));
21056 case PM_TOKEN_IDENTIFIER:
21057 case PM_TOKEN_METHOD_NAME:
21058 return parse_identifier(parser, binding_power, flags, depth);
21059 case PM_TOKEN_HEREDOC_START:
21060 return parse_heredoc(parser, depth);
21061 case PM_TOKEN_CLASS_VARIABLE:
21062 parser_lex(parser);
21063 return parse_multi_target(parser, UP(pm_class_variable_read_node_create(parser, &parser->previous)), binding_power, depth);
21064 case PM_TOKEN_NUMBERED_REFERENCE:
21065 parser_lex(parser);
21066 return parse_multi_target(parser, UP(pm_numbered_reference_read_node_create(parser, &parser->previous)), binding_power, depth);
21067 case PM_TOKEN_GLOBAL_VARIABLE:
21068 parser_lex(parser);
21069 return parse_multi_target(parser, UP(pm_global_variable_read_node_create(parser, &parser->previous)), binding_power, depth);
21070 case PM_TOKEN_BACK_REFERENCE:
21071 parser_lex(parser);
21072 return parse_multi_target(parser, UP(pm_back_reference_read_node_create(parser, &parser->previous)), binding_power, depth);
21073 case PM_TOKEN_INSTANCE_VARIABLE:
21074 parser_lex(parser);
21075 return parse_multi_target(parser, UP(pm_instance_variable_read_node_create(parser, &parser->previous)), binding_power, depth);
21076 case PM_TOKEN_INTEGER: {
21077 pm_node_flags_t base = parser->integer.base;
21078 parser_lex(parser);
21079 return UP(pm_integer_node_create(parser, base, &parser->previous));
21080 }
21081 case PM_TOKEN_INTEGER_IMAGINARY: {
21082 pm_node_flags_t base = parser->integer.base;
21083 parser_lex(parser);
21084 return UP(pm_integer_node_imaginary_create(parser, base, &parser->previous));
21085 }
21086 case PM_TOKEN_INTEGER_RATIONAL: {
21087 pm_node_flags_t base = parser->integer.base;
21088 parser_lex(parser);
21089 return UP(pm_integer_node_rational_create(parser, base, &parser->previous));
21090 }
21091 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: {
21092 pm_node_flags_t base = parser->integer.base;
21093 parser_lex(parser);
21094 return UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous));
21095 }
21096 case PM_TOKEN_KEYWORD___ENCODING__:
21097 parser_lex(parser);
21098 return UP(pm_source_encoding_node_create(parser, &parser->previous));
21099 case PM_TOKEN_KEYWORD___FILE__:
21100 parser_lex(parser);
21101 return UP(pm_source_file_node_create(parser, &parser->previous));
21102 case PM_TOKEN_KEYWORD___LINE__:
21103 parser_lex(parser);
21104 return UP(pm_source_line_node_create(parser, &parser->previous));
21105 case PM_TOKEN_KEYWORD_ALIAS:
21106 return parse_alias(parser, binding_power, flags, depth);
21107 case PM_TOKEN_KEYWORD_CASE:
21108 return parse_case(parser, flags, depth);
21109 case PM_TOKEN_KEYWORD_BEGIN:
21110 return UP(parse_begin(parser, depth));
21111 case PM_TOKEN_KEYWORD_BEGIN_UPCASE:
21112 return UP(parse_preexe(parser, binding_power, depth));
21113 case PM_TOKEN_KEYWORD_BREAK:
21114 case PM_TOKEN_KEYWORD_NEXT:
21115 case PM_TOKEN_KEYWORD_RETURN:
21116 return parse_jump(parser, flags, depth);
21117 case PM_TOKEN_KEYWORD_SUPER:
21118 return parse_super(parser, flags, depth);
21119 case PM_TOKEN_KEYWORD_YIELD:
21120 return UP(parse_yield(parser, flags, depth));
21121 case PM_TOKEN_KEYWORD_CLASS:
21122 return parse_class(parser, flags, depth);
21123 case PM_TOKEN_KEYWORD_DEF:
21124 return parse_def(parser, flags, depth);
21125 case PM_TOKEN_KEYWORD_DEFINED:
21126 return UP(parse_defined(parser, flags, depth));
21127 case PM_TOKEN_KEYWORD_END_UPCASE:
21128 return UP(parse_postexe(parser, binding_power, flags, depth));
21129 case PM_TOKEN_KEYWORD_FALSE:
21130 parser_lex(parser);
21131 return UP(pm_false_node_create(parser, &parser->previous));
21132 case PM_TOKEN_KEYWORD_FOR:
21133 return UP(parse_for(parser, flags, depth));
21134 case PM_TOKEN_KEYWORD_IF:
21135 if (parser_end_of_line_p(parser)) {
21136 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
21137 }
21138
21139 size_t opening_newline_index = token_newline_index(parser);
21140 bool if_after_else = parser->previous.type == PM_TOKEN_KEYWORD_ELSE;
21141 parser_lex(parser);
21142
21143 return parse_conditional(parser, PM_CONTEXT_IF, opening_newline_index, if_after_else, (uint16_t) (depth + 1));
21144 case PM_TOKEN_KEYWORD_UNDEF:
21145 return UP(parse_undef(parser, binding_power, flags, depth));
21146 case PM_TOKEN_KEYWORD_NOT:
21147 return parse_not(parser, binding_power, flags, depth);
21148 case PM_TOKEN_KEYWORD_UNLESS: {
21149 size_t opening_newline_index = token_newline_index(parser);
21150 parser_lex(parser);
21151
21152 return parse_conditional(parser, PM_CONTEXT_UNLESS, opening_newline_index, false, (uint16_t) (depth + 1));
21153 }
21154 case PM_TOKEN_KEYWORD_MODULE:
21155 return parse_module(parser, flags, depth);
21156 case PM_TOKEN_KEYWORD_NIL:
21157 parser_lex(parser);
21158 return UP(pm_nil_node_create(parser, &parser->previous));
21159 case PM_TOKEN_KEYWORD_REDO: {
21160 parser_lex(parser);
21161
21162 pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous));
21163 if (!parser->partial_script) ensure_valid_jump(parser, node);
21164
21165 return node;
21166 }
21167 case PM_TOKEN_KEYWORD_RETRY: {
21168 parser_lex(parser);
21169
21170 pm_node_t *node = UP(pm_retry_node_create(parser, &parser->previous));
21171 ensure_valid_retry(parser, node);
21172
21173 return node;
21174 }
21175 case PM_TOKEN_KEYWORD_SELF:
21176 parser_lex(parser);
21177 return UP(pm_self_node_create(parser, &parser->previous));
21178 case PM_TOKEN_KEYWORD_TRUE:
21179 parser_lex(parser);
21180 return UP(pm_true_node_create(parser, &parser->previous));
21181 case PM_TOKEN_KEYWORD_UNTIL:
21182 return UP(parse_until(parser, flags, depth));
21183 case PM_TOKEN_KEYWORD_WHILE:
21184 return UP(parse_while(parser, flags, depth));
21185 case PM_TOKEN_PERCENT_LOWER_I:
21186 return UP(parse_uninterpolated_symbol_array(parser));
21187 case PM_TOKEN_PERCENT_UPPER_I:
21188 return UP(parse_interpolated_symbol_array(parser, depth));
21189 case PM_TOKEN_PERCENT_LOWER_W:
21190 return UP(parse_uninterpolated_string_array(parser));
21191 case PM_TOKEN_PERCENT_UPPER_W:
21192 return UP(parse_interpolated_string_array(parser, depth));
21193 case PM_TOKEN_REGEXP_BEGIN:
21194 return parse_regexp(parser, depth);
21195 case PM_TOKEN_XSTRING_BEGIN:
21196 case PM_TOKEN_PERCENT_LOWER_X:
21197 return parse_xstring(parser, depth);
21198 case PM_TOKEN_USTAR:
21199 return parse_unary_star(parser, binding_power, flags, diag_id, depth);
21200 case PM_TOKEN_BANG:
21201 return UP(parse_bang(parser, binding_power, flags, depth));
21202 case PM_TOKEN_TILDE:
21203 return UP(parse_tilde(parser, binding_power, flags, depth));
21204 case PM_TOKEN_UMINUS:
21205 return UP(parse_unary_minus(parser, binding_power, flags, depth));
21206 case PM_TOKEN_UMINUS_NUM:
21207 return UP(parse_unary_minus_number(parser, flags, depth));
21208 case PM_TOKEN_MINUS_GREATER:
21209 return UP(parse_lambda(parser, depth));
21210 case PM_TOKEN_UPLUS:
21211 return UP(parse_unary_plus(parser, binding_power, flags, depth));
21212 case PM_TOKEN_STRING_BEGIN:
21213 return parse_strings(parser, NULL, flags & PM_PARSE_ACCEPTS_LABEL, (uint16_t) (depth + 1));
21214 case PM_TOKEN_SYMBOL_BEGIN:
21215 return parse_symbol(parser, PM_LEX_STATE_END, (uint16_t) (depth + 1));
21216 default: {
21217 pm_context_t recoverable = context_recoverable(parser, &parser->current);
21218
21219 if (recoverable != PM_CONTEXT_NONE) {
21220 parser->recovering = true;
21221
21222 // If the given error is not the generic one, then we'll add it
21223 // here because it will provide more context in addition to the
21224 // recoverable error that we will also add.
21225 if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) {
21226 pm_parser_err_prefix(parser, diag_id);
21227 }
21228
21229 // If we get here, then we are assuming this token is closing a
21230 // parent context, so we'll indicate that to the user so that
21231 // they know how we behaved.
21232 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_str(parser->current.type), context_human(recoverable));
21233 } else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) {
21234 // We're going to make a special case here, because "cannot
21235 // parse expression" is pretty generic, and we know here that we
21236 // have an unexpected token.
21237 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_str(parser->current.type));
21238 } else {
21239 pm_parser_err_prefix(parser, diag_id);
21240 }
21241
21242 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
21243 }
21244 }
21245}
21246
21247static pm_node_t *
21248parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth);
21249
21257static void
21258parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
21259 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21260 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21261 parser_lex(parser);
21262 parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21263 }
21264}
21265
21271static void
21272parse_write_value_comma_check(pm_parser_t *parser, const pm_node_t *value) {
21273 if (match1(parser, PM_TOKEN_COMMA) && pm_command_before_comma_p(parser, value)) {
21274 pm_parser_err_current(parser, PM_ERR_INVALID_COMMA);
21275 }
21276}
21277
21287static pm_node_t *
21288parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
21289 /* Where an operator write accepts a command call it also accepts an
21290 * endless method with a command body (`x += def f = foo 1`). */
21291 uint8_t command_flags = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
21292 ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21293 : (previous_binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21294 if (command_flags) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
21295
21296 pm_node_t *value = parse_value_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | command_flags), diag_id, (uint16_t) (depth + 1));
21297
21298 /* An operator write never takes a multi-value right-hand side, so a comma
21299 * after a statement-level one is already reported by the statement that
21300 * contains it. */
21301 if (previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
21302 parse_write_value_comma_check(parser, value);
21303 }
21304
21305 // Assignments whose value is a command call (e.g., a = b c) can only
21306 // be followed by modifiers (if/unless/while/until/rescue) and not by
21307 // operators with higher binding power. If we find one, emit an error
21308 // and skip the operator and its right-hand side.
21309 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER && (pm_command_call_value_p(parser, value) || pm_block_call_p(value))) {
21310 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21311 parser_lex(parser);
21312 parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21313 }
21314
21315 // Contradicting binding powers, the right-hand-side value of the assignment
21316 // allows the `rescue` modifier.
21317 if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21318 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21319
21320 pm_token_t rescue = parser->current;
21321 parser_lex(parser);
21322
21323 // As in parse_assignment_values, the resbody is a `stmt` (permitting a
21324 // multiple assignment / command call) when the rescued value is itself a
21325 // command call, and a plain `arg` otherwise.
21326 bool statement_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21327 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21328
21329 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21330 context_pop(parser);
21331
21332 // A pattern-match resbody is a statement, but here the rescue is nested
21333 // in an assignment value where parse_expression_terminator cannot see
21334 // it, so reject a trailing operator above the modifier level directly.
21335 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21336 parse_rescue_modifier_terminator(parser, flags, depth);
21337 }
21338
21339 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21340 }
21341
21342 return value;
21343}
21344
21349static void
21350parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) {
21351 switch (PM_NODE_TYPE(node)) {
21352 case PM_BEGIN_NODE: {
21353 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
21354 if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements);
21355 break;
21356 }
21357 case PM_LOCAL_VARIABLE_WRITE_NODE: {
21359 pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
21360 break;
21361 }
21362 case PM_PARENTHESES_NODE: {
21363 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
21364 if (cast->body != NULL) parse_assignment_value_local(parser, cast->body);
21365 break;
21366 }
21367 case PM_STATEMENTS_NODE: {
21368 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
21369 const pm_node_t *statement;
21370
21371 PM_NODE_LIST_FOREACH(&cast->body, index, statement) {
21372 parse_assignment_value_local(parser, statement);
21373 }
21374 break;
21375 }
21376 default:
21377 break;
21378 }
21379}
21380
21393static pm_node_t *
21394parse_assignment_values(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
21395 bool statement_level = (previous_binding_power == PM_BINDING_POWER_STATEMENT) || (flags & PM_PARSE_ACCEPTS_STATEMENT);
21396
21397 bool permitted = true;
21398 if (!statement_level && match1(parser, PM_TOKEN_USTAR)) permitted = false;
21399
21400 // A command call (e.g. `x = y z`) is permitted as the value when assigning
21401 // directly (carrying the caller's flag), or in any statement-level context
21402 // — which includes a rescue modifier value via the flag.
21403 uint8_t command_call_flag = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
21404 ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21405 : ((previous_binding_power < PM_BINDING_POWER_MODIFIER || statement_level) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21406
21407 /* Where a single-target write accepts a command call it also accepts an
21408 * endless method with a command body (`x = def f = foo 1`), but a
21409 * multi-write accepts only the command call. */
21410 if (command_call_flag && binding_power != PM_BINDING_POWER_MULTI_ASSIGNMENT + 1) {
21411 command_call_flag |= PM_PARSE_ACCEPTS_COMMAND_DEF;
21412 }
21413
21414 pm_node_t *value = parse_starred_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | command_call_flag), diag_id, (uint16_t) (depth + 1));
21415 if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
21416
21417 parse_assignment_value_local(parser, value);
21418 bool single_value = true;
21419
21420 parse_write_value_comma_check(parser, value);
21421
21422 // Block calls (command call + do block, e.g., `foo bar do end`) cannot
21423 // be followed by a comma to form a multi-value RHS because each element
21424 // of a multi-value assignment must be an `arg`, not a `block_call`.
21425 if (statement_level && !pm_block_call_p(value) && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) {
21426 single_value = false;
21427
21428 pm_array_node_t *array = pm_array_node_create(parser, NULL);
21429 pm_array_node_elements_append(parser->arena, array, value);
21430 value = UP(array);
21431
21432 while (accept1(parser, PM_TOKEN_COMMA)) {
21433 pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1));
21434
21435 pm_array_node_elements_append(parser->arena, array, element);
21436 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
21437
21438 parse_assignment_value_local(parser, element);
21439 }
21440 }
21441
21442 // Assignments whose value is a command call (e.g., a = b c) can only
21443 // be followed by modifiers (if/unless/while/until/rescue) and not by
21444 // operators with higher binding power. If we find one, emit an error
21445 // and skip the operator and its right-hand side.
21446 if (single_value && pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER && (pm_command_call_value_p(parser, value) || pm_block_call_p(value))) {
21447 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21448 parser_lex(parser);
21449 parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21450 }
21451
21452 // Contradicting binding powers, the right-hand-side value of the assignment
21453 // allows the `rescue` modifier.
21454 bool multiple_assignment = (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1));
21455 if ((single_value || multiple_assignment) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21456 bool command_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21457
21458 // A multiple assignment whose value is a command call (`x, y = foo
21459 // bar`) is a complete statement (parse.y: `mlhs '='
21460 // command_call_value`, which has no rescue), so a trailing `rescue`
21461 // modifies the whole assignment rather than the value. Leave it for the
21462 // statement-level rescue instead of binding it to the value here. For a
21463 // non-command value the rescue does bind to the value (parse.y:
21464 // `mlhs '=' mrhs_arg modifier_rescue stmt`).
21465 if (multiple_assignment && command_value) return value;
21466
21467 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21468
21469 pm_token_t rescue = parser->current;
21470 parser_lex(parser);
21471
21472 // The resbody is a `stmt` (parse.y: `command_rhs`/`mlhs '=' mrhs_arg`),
21473 // which permits a multiple assignment and a command call, when this is a
21474 // multiple assignment or the rescued value is itself a command call.
21475 // Otherwise it is a plain `arg` (parse.y: `arg_rhs`).
21476 bool statement_value = multiple_assignment || command_value;
21477 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21478
21479 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21480 context_pop(parser);
21481
21482 // A pattern-match resbody is a statement, but here the rescue is nested
21483 // in an assignment value where parse_expression_terminator cannot see
21484 // it, so reject a trailing operator above the modifier level directly.
21485 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21486 parse_rescue_modifier_terminator(parser, flags, depth);
21487 }
21488
21489 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21490 }
21491
21492 return value;
21493}
21494
21507static pm_node_t *
21508parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth) {
21509 if (statement) {
21510 pm_node_t *value;
21511 bool multiple;
21512
21513 if (match1(parser, PM_TOKEN_USTAR)) {
21514 // A leading splat can only begin a multiple assignment target list.
21515 parser_lex(parser);
21516 value = parse_splat(parser, flags, depth);
21517 multiple = true;
21518 } else {
21519 // The flag lets a single-target assignment take a multiple-value or
21520 // splat right-hand side (`b = c, d` / `b = *c`); a comma _before_ an
21521 // `=`, or a parenthesized target list (`(b, c), d = 1`), instead
21522 // promotes to a multiple assignment target list below.
21523 value = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, flags | PM_PARSE_ACCEPTS_STATEMENT | PM_PARSE_ACCEPTS_COMMAND_DEF, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21524 multiple = match1(parser, PM_TOKEN_COMMA) || PM_NODE_TYPE_P(value, PM_MULTI_TARGET_NODE);
21525 }
21526
21527 if (multiple) {
21528 pm_node_t *target = parse_targets_validate(parser, value, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21529
21530 // A promoted target list is only a valid rescue value as part of a
21531 // complete `targets = values`. parse_targets_validate already
21532 // reports a missing `=` for every terminator except `)` (which it
21533 // permits for an enclosing mlhs paren that does not apply here), so
21534 // reject that case.
21535 if (!match1(parser, PM_TOKEN_EQUAL)) {
21536 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
21537 return target;
21538 }
21539
21540 pm_token_t operator = parser->current;
21541 parser_lex(parser);
21542
21543 pm_node_t *values = parse_assignment_values(parser, PM_BINDING_POWER_STATEMENT, PM_BINDING_POWER_MULTI_ASSIGNMENT + 1, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
21544 value = parse_write(parser, target, &operator, values);
21545 }
21546
21547 // Reject a trailing operator that cannot follow a statement resbody.
21548 // Pattern-match handlers are statements too, but for the bare statement
21549 // form they are reported by parse_expression_terminator instead (which
21550 // keeps its existing error-recovery), so they are excluded here.
21551 if (!PM_NODE_TYPE_P(value, PM_MATCH_REQUIRED_NODE) && !PM_NODE_TYPE_P(value, PM_MATCH_PREDICATE_NODE)) {
21552 parse_rescue_modifier_terminator(parser, flags, depth);
21553 }
21554
21555 return value;
21556 }
21557
21558 // Otherwise the resbody is a plain `arg` (parse.y: `arg modifier_rescue
21559 // arg`), parsed above the `and`/`or`/`not` level so those stay outside it.
21560 return parse_expression(parser, PM_BINDING_POWER_DEFINED, flags, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21561}
21562
21570static void
21571parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) {
21572 if (call_node->arguments != NULL) {
21573 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS);
21574 pm_node_unreference(parser, UP(call_node->arguments));
21575 call_node->arguments = NULL;
21576 }
21577
21578 if (call_node->block != NULL) {
21579 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK);
21580 pm_node_unreference(parser, UP(call_node->block));
21581 call_node->block = NULL;
21582 }
21583}
21584
21585static PRISM_INLINE const uint8_t *
21586pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21587 cursor++;
21588
21589 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21590 uint8_t value = escape_hexadecimal_digit(*cursor);
21591 cursor++;
21592
21593 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21594 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(*cursor));
21595 cursor++;
21596 }
21597
21598 pm_buffer_append_byte(unescaped, value);
21599 } else {
21600 pm_buffer_append_string(unescaped, "\\x", 2);
21601 }
21602
21603 return cursor;
21604}
21605
21606static PRISM_INLINE const uint8_t *
21607pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21608 uint8_t value = (uint8_t) (*cursor - '0');
21609 cursor++;
21610
21611 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21612 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21613 cursor++;
21614
21615 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21616 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21617 cursor++;
21618 }
21619 }
21620
21621 pm_buffer_append_byte(unescaped, value);
21622 return cursor;
21623}
21624
21625static PRISM_INLINE const uint8_t *
21626pm_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) {
21627 const uint8_t *start = cursor - 1;
21628 cursor++;
21629
21630 if (cursor >= end) {
21631 pm_buffer_append_string(unescaped, "\\u", 2);
21632 return cursor;
21633 }
21634
21635 if (*cursor != '{') {
21636 size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4));
21637 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21638
21639 if (!pm_buffer_append_unicode_codepoint(unescaped, value)) {
21640 pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start));
21641 }
21642
21643 return cursor + length;
21644 }
21645
21646 cursor++;
21647 for (;;) {
21648 while (cursor < end && *cursor == ' ') cursor++;
21649
21650 if (cursor >= end) break;
21651 if (*cursor == '}') {
21652 cursor++;
21653 break;
21654 }
21655
21656 size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor);
21657 if (length == 0) {
21658 break;
21659 }
21660 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21661
21662 (void) pm_buffer_append_unicode_codepoint(unescaped, value);
21663 cursor += length;
21664 }
21665
21666 return cursor;
21667}
21668
21669static void
21670pm_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) {
21671 const uint8_t *end = source + length;
21672 pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source));
21673
21674 for (;;) {
21675 if (++cursor >= end) {
21676 pm_buffer_append_byte(unescaped, '\\');
21677 return;
21678 }
21679
21680 switch (*cursor) {
21681 case 'x':
21682 cursor = pm_named_capture_escape_hex(unescaped, cursor, end);
21683 break;
21684 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
21685 cursor = pm_named_capture_escape_octal(unescaped, cursor, end);
21686 break;
21687 case 'u':
21688 cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location);
21689 break;
21690 default:
21691 pm_buffer_append_byte(unescaped, '\\');
21692 break;
21693 }
21694
21695 const uint8_t *next_cursor = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, parser->encoding);
21696 if (next_cursor == NULL) break;
21697
21698 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (next_cursor - cursor));
21699 cursor = next_cursor;
21700 }
21701
21702 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (end - cursor));
21703}
21704
21723
21728static void
21729parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *capture, void *data) {
21730 pm_regexp_name_data_t *name_data = (pm_regexp_name_data_t *) data;
21731 pm_call_node_t *call = name_data->call;
21732 pm_constant_id_set_t *names = &name_data->names;
21733
21734 const uint8_t *source = pm_string_source(capture);
21735 size_t length = pm_string_length(capture);
21736 pm_buffer_t unescaped = { 0 };
21737
21738 // First, we need to handle escapes within the name of the capture group.
21739 // This is because regular expressions have three different representations
21740 // in prism. The first is the plain source code. The second is the
21741 // representation that will be sent to the regular expression engine, which
21742 // is the value of the "unescaped" field. This is poorly named, because it
21743 // actually still contains escapes, just a subset of them that the regular
21744 // expression engine knows how to handle. The third representation is fully
21745 // unescaped, which is what we need.
21746 const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding);
21747 if (PRISM_UNLIKELY(cursor != NULL)) {
21748 pm_named_capture_escape(parser, &unescaped, source, length, cursor, name_data->shared ? NULL : &call->receiver->location);
21749
21750 /*
21751 * Escapes like \u{} write nothing, so the unescaped name can be empty.
21752 * An empty name is never a valid local, and the buffer is unallocated
21753 * (NULL) in that case, so there is no slice to check.
21754 */
21755 if (pm_buffer_length(&unescaped) == 0) return;
21756
21757 source = (const uint8_t *) pm_buffer_value(&unescaped);
21758 length = pm_buffer_length(&unescaped);
21759 }
21760
21761 const uint8_t *start;
21762 const uint8_t *end;
21763 pm_constant_id_t name;
21764
21765 // If the name of the capture group isn't a valid identifier, we do
21766 // not add it to the local table.
21767 if (!pm_slice_is_valid_local(parser, source, source + length)) {
21768 pm_buffer_cleanup(&unescaped);
21769 return;
21770 }
21771
21772 if (name_data->shared) {
21773 // If the unescaped string is a slice of the source, then we can
21774 // copy the names directly. The pointers will line up.
21775 start = source;
21776 end = source + length;
21777 name = pm_parser_constant_id_raw(parser, start, end);
21778 } else {
21779 // Otherwise, the name is a slice of the malloc-ed owned string,
21780 // in which case we need to copy it out into a new string.
21781 start = parser->start + PM_NODE_START(call->receiver);
21782 end = parser->start + PM_NODE_END(call->receiver);
21783
21784 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
21785 memcpy(memory, source, length);
21786 name = pm_parser_constant_id_owned(parser, memory, length);
21787 }
21788
21789 /*
21790 * Add this name to the set of constants if it is valid, not duplicated,
21791 * and not a keyword.
21792 */
21793 if (name != 0 && pm_constant_id_set_insert(&parser->scratch_arena, names, name)) {
21794
21795 int depth;
21796 if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
21797 // If the local is not already a local but it is a keyword, then we
21798 // do not want to add a capture for this.
21799 if (pm_local_is_keyword((const char *) source, length)) {
21800 pm_buffer_cleanup(&unescaped);
21801 return;
21802 }
21803
21804 // If the identifier is not already a local, then we will add it to
21805 // the local table.
21806 pm_parser_local_add(parser, name, start, end, 0);
21807 }
21808
21809 // Here we lazily create the MatchWriteNode since we know we're
21810 // about to add a target.
21811 if (name_data->match == NULL) {
21812 name_data->match = pm_match_write_node_create(parser, call);
21813 }
21814
21815 // Next, create the local variable target and add it to the list of
21816 // targets for the match.
21817 pm_token_t token = { .type = 0, .start = start, .end = end };
21818 pm_location_t token_loc = TOK2LOC(parser, &token);
21819 pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &token_loc, name, depth == -1 ? 0 : (uint32_t) depth));
21820 pm_node_list_append_inline(parser->arena, &name_data->match->targets, target);
21821 }
21822
21823 pm_buffer_cleanup(&unescaped);
21824}
21825
21831static pm_node_t *
21832parse_interpolated_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call, bool extended_mode) {
21833 pm_regexp_name_data_t name_data = {
21834 .call = call,
21835 .match = NULL,
21836 .names = { 0 },
21837 .shared = false,
21838 };
21839
21840 pm_regexp_parse_named_captures(parser, pm_string_source(content), pm_string_length(content), extended_mode, parse_regular_expression_named_capture, &name_data);
21841
21842 if (name_data.match != NULL) {
21843 return UP(name_data.match);
21844 } else {
21845 return UP(call);
21846 }
21847}
21848
21849static pm_if_node_t *
21850parse_ternary(pm_parser_t *parser, pm_node_t *node, uint8_t flags, uint16_t depth) {
21851 context_push(parser, PM_CONTEXT_TERNARY);
21852 pm_node_list_t current_block_exits = { 0 };
21853 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
21854
21855 pm_token_t qmark = parser->current;
21856 parser_lex(parser);
21857
21858 pm_node_t *true_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_TERNARY_EXPRESSION_TRUE, (uint16_t) (depth + 1));
21859
21860 if (parser->recovering) {
21861 // If parsing the true expression of this ternary resulted in a syntax
21862 // error that we can recover from, then we're going to put missing nodes
21863 // and tokens into the remaining places. We want to be sure to do this
21864 // before the `expect` function call to make sure it doesn't
21865 // accidentally move past a ':' token that occurs after the syntax
21866 // error.
21867 pm_token_t colon = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
21868 pm_node_t *false_expression = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &colon), PM_TOKEN_LENGTH(&colon)));
21869
21870 context_pop(parser);
21871 pop_block_exits(parser, previous_block_exits);
21872 return pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
21873 }
21874
21875 accept1(parser, PM_TOKEN_NEWLINE);
21876 expect1(parser, PM_TOKEN_COLON, PM_ERR_TERNARY_COLON);
21877
21878 pm_token_t colon = parser->previous;
21879 pm_node_t *false_expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_TERNARY_EXPRESSION_FALSE, (uint16_t) (depth + 1));
21880
21881 context_pop(parser);
21882 pop_block_exits(parser, previous_block_exits);
21883 return pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
21884}
21885
21886static pm_node_t *
21887parse_constant_path_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, uint8_t flags, uint16_t depth) {
21888 pm_token_t delimiter = parser->current;
21889 parser_lex(parser);
21890
21891 switch (parser->current.type) {
21892 case PM_TOKEN_CONSTANT: {
21893 parser_lex(parser);
21894 pm_node_t *path;
21895
21896 if (
21897 (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
21898 ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type))
21899 ) {
21900 /* If we have a constant immediately following a '::' operator,
21901 * then this can either be a constant path or a method call,
21902 * depending on what follows the constant.
21903 *
21904 * If we have parentheses, then this is a method call. That
21905 * would look like Foo::Bar(). */
21906 pm_token_t message = parser->previous;
21907 pm_arguments_t arguments = { 0 };
21908
21909 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
21910 path = UP(pm_call_node_call_create(parser, node, &delimiter, &message, &arguments));
21911 } else {
21912 /* Otherwise, this is a constant path. That would look like
21913 * Foo::Bar. */
21914 path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
21915 }
21916
21917 return parse_multi_target(parser, path, previous_binding_power, depth);
21918 }
21919 case PM_CASE_OPERATOR:
21920 case PM_CASE_KEYWORD:
21921 case PM_TOKEN_IDENTIFIER:
21922 case PM_TOKEN_METHOD_NAME: {
21923 parser_lex(parser);
21924 pm_token_t message = parser->previous;
21925
21926 /* If we have an identifier following a '::' operator, then it is
21927 * for sure a method call. */
21928 pm_arguments_t arguments = { 0 };
21929 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
21930
21931 pm_call_node_t *call = pm_call_node_call_create(parser, node, &delimiter, &message, &arguments);
21932 return parse_multi_target(parser, UP(call), previous_binding_power, depth);
21933 }
21934 case PM_TOKEN_PARENTHESIS_LEFT: {
21935 /* If we have a parenthesis following a '::' operator, then it is the
21936 * method call shorthand. That would look like Foo::(bar). */
21937 pm_arguments_t arguments = { 0 };
21938 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
21939
21940 return UP(pm_call_node_shorthand_create(parser, node, &delimiter, &arguments));
21941 }
21942 default:
21943 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
21944 return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
21945 }
21946}
21947
21948static pm_node_t *
21949parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
21950 /* Note that we _must_ parse the value before adding the local variables in
21951 * order to properly mirror the behavior of Ruby. For example,
21952 *
21953 * /(?<foo>bar)/ =~ foo
21954 *
21955 * In this case, `foo` should be a method call and not a local yet.
21956 */
21957 pm_token_t operator = parser->current;
21958 parser_lex(parser);
21959
21960 pm_node_t *argument = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21961
21962 // By default, we're going to create a call node and then return it.
21963 pm_call_node_t *call = pm_call_node_binary_create(parser, node, &operator, argument, 0);
21964 pm_node_t *result = UP(call);
21965
21966 /* If the receiver of this =~ is a regular expression node, then we need to
21967 * introduce local variables for it based on its named capture groups. */
21968 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE)) {
21969 /* It's possible to have an interpolated regular expression node that
21970 * only contains strings. This is because it can be split up by a
21971 * heredoc. In this case we need to concat the unescaped strings
21972 * together and then parse them as a regular expression. */
21974
21975 bool interpolated = false;
21976 size_t total_length = 0;
21977
21978 pm_node_t *part;
21979 PM_NODE_LIST_FOREACH(parts, index, part) {
21980 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
21981 total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped);
21982 } else {
21983 interpolated = true;
21984 break;
21985 }
21986 }
21987
21988 if (!interpolated && total_length > 0) {
21989 uint8_t *memory = (uint8_t *) pm_arena_alloc(&parser->scratch_arena, total_length, PRISM_ALIGNOF(uint8_t));
21990 uint8_t *cursor = memory;
21991 PM_NODE_LIST_FOREACH(parts, index, part) {
21992 pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
21993 size_t length = pm_string_length(unescaped);
21994
21995 memcpy(cursor, pm_string_source(unescaped), length);
21996 cursor += length;
21997 }
21998
21999 pm_string_t content;
22000 pm_string_init(&content, memory, total_length);
22001 result = parse_interpolated_regular_expression_named_captures(parser, &content, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
22002 }
22003 } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
22004 /* If we have a regular expression node, then we can parse the named
22005 * captures and validate encoding in one pass. */
22007 pm_regexp_name_data_t name_data = {
22008 .call = call,
22009 .match = NULL,
22010 .names = { 0 },
22011 .shared = pm_string_source(&regexp->unescaped) == parser->start + regexp->content_loc.start,
22012 };
22013
22014 pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data));
22015
22016 if (name_data.match != NULL) {
22017 result = UP(name_data.match);
22018 }
22019 }
22020
22021 return result;
22022}
22023
22024static pm_node_t *
22025parse_equal(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
22026 pm_token_t operator = parser->current;
22027
22028 switch (PM_NODE_TYPE(node)) {
22029 case PM_CALL_NODE: {
22030 // If we have no arguments to the call node and we need this
22031 // to be a target then this is either a method call or a
22032 // local variable write. This _must_ happen before the value
22033 // is parsed because it could be referenced in the value.
22034 pm_call_node_t *call_node = (pm_call_node_t *) node;
22035 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22036 pm_parser_local_add_location(parser, &call_node->message_loc, 0);
22037 }
22038 }
22040 case PM_CASE_WRITABLE: {
22041 // When we have `it = value`, we need to add `it` as a local
22042 // variable before parsing the value, in case the value
22043 // references the variable.
22044 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
22045 pm_parser_local_add_location(parser, &node->location, 0);
22046 }
22047
22048 parser_lex(parser);
22049 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, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
22050
22051 if (PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) && previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
22052 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_MULTI_WRITE);
22053 }
22054
22055 return parse_write(parser, node, &operator, value);
22056 }
22057 case PM_SPLAT_NODE: {
22058 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
22059 pm_multi_target_node_targets_append(parser, multi_target, node);
22060
22061 parser_lex(parser);
22062 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_BINDING_POWER_MULTI_ASSIGNMENT + 1, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
22063 return parse_write(parser, UP(multi_target), &operator, value);
22064 }
22065 case PM_SOURCE_ENCODING_NODE:
22066 case PM_FALSE_NODE:
22067 case PM_SOURCE_FILE_NODE:
22068 case PM_SOURCE_LINE_NODE:
22069 case PM_NIL_NODE:
22070 case PM_SELF_NODE:
22071 case PM_TRUE_NODE: {
22072 // In these special cases, we have specific error messages
22073 // and we will replace them with local variable writes.
22074 parser_lex(parser);
22075 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
22076 return parse_unwriteable_write(parser, node, &operator, value);
22077 }
22078 default:
22079 // In this case we have an = sign, but we don't know what
22080 // it's for. We need to treat it as an error. We'll mark it
22081 // as an error and skip past it.
22082 parser_lex(parser);
22083 pm_parser_err_token(parser, &operator, PM_ERR_EXPRESSION_NOT_WRITABLE);
22084 return node;
22085 }
22086}
22087
22088static pm_node_t *
22089parse_and_equal(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
22090 pm_token_t operator = parser->current;
22091
22092 switch (PM_NODE_TYPE(node)) {
22093 case PM_BACK_REFERENCE_READ_NODE:
22094 case PM_NUMBERED_REFERENCE_READ_NODE:
22095 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
22097 case PM_GLOBAL_VARIABLE_READ_NODE: {
22098 parser_lex(parser);
22099
22100 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22101 pm_node_t *result = UP(pm_global_variable_and_write_node_create(parser, node, &operator, value));
22102
22103 return result;
22104 }
22105 case PM_CLASS_VARIABLE_READ_NODE: {
22106 parser_lex(parser);
22107
22108 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22109 pm_node_t *result = UP(pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &operator, value));
22110
22111 return result;
22112 }
22113 case PM_CONSTANT_PATH_NODE: {
22114 parser_lex(parser);
22115
22116 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22117 pm_node_t *write = UP(pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &operator, value));
22118
22119 return parse_shareable_constant_write(parser, write);
22120 }
22121 case PM_CONSTANT_READ_NODE: {
22122 parser_lex(parser);
22123
22124 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22125 pm_node_t *write = UP(pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &operator, value));
22126
22127 if (context_def_p(parser)) {
22128 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
22129 }
22130
22131 return parse_shareable_constant_write(parser, write);
22132 }
22133 case PM_INSTANCE_VARIABLE_READ_NODE: {
22134 parser_lex(parser);
22135
22136 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22137 pm_node_t *result = UP(pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &operator, value));
22138
22139 return result;
22140 }
22141 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
22142 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
22143 parser_lex(parser);
22144
22145 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22146 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &operator, value, name, 0));
22147
22148 pm_node_unreference(parser, node);
22149 return result;
22150 }
22151 case PM_LOCAL_VARIABLE_READ_NODE: {
22152 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
22153 PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + node->location.start);
22154 pm_node_unreference(parser, node);
22155 }
22156
22158 parser_lex(parser);
22159
22160 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22161 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &operator, value, cast->name, cast->depth));
22162
22163 return result;
22164 }
22165 case PM_CALL_NODE: {
22167
22168 // If we have a vcall (a method with no arguments and no
22169 // receiver that could have been a local variable) then we
22170 // will transform it into a local variable write.
22171 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22172 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
22173 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
22174 parser_lex(parser);
22175
22176 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22177 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &operator, value, constant_id, 0));
22178
22179 return result;
22180 }
22181
22182 // Move past the token here so that we have already added
22183 // the local variable by this point.
22184 parser_lex(parser);
22185
22186 // If there is no call operator and the message is "[]" then
22187 // this is an aref expression, and we can transform it into
22188 // an aset expression.
22189 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
22190 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22191 return UP(pm_index_and_write_node_create(parser, cast, &operator, value));
22192 }
22193
22194 // If this node cannot be writable, then we have an error.
22195 if (pm_call_node_writable_p(parser, cast)) {
22196 parse_write_name(parser, &cast->name);
22197 } else {
22198 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22199 }
22200
22201 parse_call_operator_write(parser, cast, &operator);
22202 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22203 return UP(pm_call_and_write_node_create(parser, cast, &operator, value));
22204 }
22205 case PM_MULTI_WRITE_NODE: {
22206 parser_lex(parser);
22207
22208 pm_parser_err_token(parser, &operator, PM_ERR_AMPAMPEQ_MULTI_ASSIGN);
22209 return node;
22210 }
22211 default:
22212 parser_lex(parser);
22213
22214 // In this case we have an &&= sign, but we don't know what it's for.
22215 // We need to treat it as an error. For now, we'll mark it as an error
22216 // and just skip right past it.
22217 pm_parser_err_token(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
22218 return node;
22219 }
22220}
22221
22222static pm_node_t *
22223parse_or_equal(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
22224 pm_token_t operator = parser->current;
22225
22226 switch (PM_NODE_TYPE(node)) {
22227 case PM_BACK_REFERENCE_READ_NODE:
22228 case PM_NUMBERED_REFERENCE_READ_NODE:
22229 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
22231 case PM_GLOBAL_VARIABLE_READ_NODE: {
22232 parser_lex(parser);
22233
22234 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22235 pm_node_t *result = UP(pm_global_variable_or_write_node_create(parser, node, &operator, value));
22236
22237 return result;
22238 }
22239 case PM_CLASS_VARIABLE_READ_NODE: {
22240 parser_lex(parser);
22241
22242 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22243 pm_node_t *result = UP(pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &operator, value));
22244
22245 return result;
22246 }
22247 case PM_CONSTANT_PATH_NODE: {
22248 parser_lex(parser);
22249
22250 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22251 pm_node_t *write = UP(pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &operator, value));
22252
22253 return parse_shareable_constant_write(parser, write);
22254 }
22255 case PM_CONSTANT_READ_NODE: {
22256 parser_lex(parser);
22257
22258 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22259 pm_node_t *write = UP(pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &operator, value));
22260
22261 if (context_def_p(parser)) {
22262 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
22263 }
22264
22265 return parse_shareable_constant_write(parser, write);
22266 }
22267 case PM_INSTANCE_VARIABLE_READ_NODE: {
22268 parser_lex(parser);
22269
22270 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22271 pm_node_t *result = UP(pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &operator, value));
22272
22273 return result;
22274 }
22275 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
22276 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
22277 parser_lex(parser);
22278
22279 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22280 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &operator, value, name, 0));
22281
22282 pm_node_unreference(parser, node);
22283 return result;
22284 }
22285 case PM_LOCAL_VARIABLE_READ_NODE: {
22286 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
22287 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
22288 pm_node_unreference(parser, node);
22289 }
22290
22292 parser_lex(parser);
22293
22294 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22295 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &operator, value, cast->name, cast->depth));
22296
22297 return result;
22298 }
22299 case PM_CALL_NODE: {
22301
22302 // If we have a vcall (a method with no arguments and no
22303 // receiver that could have been a local variable) then we
22304 // will transform it into a local variable write.
22305 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22306 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
22307 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
22308 parser_lex(parser);
22309
22310 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22311 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &operator, value, constant_id, 0));
22312
22313 return result;
22314 }
22315
22316 // Move past the token here so that we have already added
22317 // the local variable by this point.
22318 parser_lex(parser);
22319
22320 // If there is no call operator and the message is "[]" then
22321 // this is an aref expression, and we can transform it into
22322 // an aset expression.
22323 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
22324 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22325 return UP(pm_index_or_write_node_create(parser, cast, &operator, value));
22326 }
22327
22328 // If this node cannot be writable, then we have an error.
22329 if (pm_call_node_writable_p(parser, cast)) {
22330 parse_write_name(parser, &cast->name);
22331 } else {
22332 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22333 }
22334
22335 parse_call_operator_write(parser, cast, &operator);
22336 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22337 return UP(pm_call_or_write_node_create(parser, cast, &operator, value));
22338 }
22339 case PM_MULTI_WRITE_NODE: {
22340 parser_lex(parser);
22341 pm_parser_err_token(parser, &operator, PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN);
22342 return node;
22343 }
22344 default:
22345 parser_lex(parser);
22346
22347 // In this case we have an ||= sign, but we don't know what it's for.
22348 // We need to treat it as an error. For now, we'll mark it as an error
22349 // and just skip right past it.
22350 pm_parser_err_token(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
22351 return node;
22352 }
22353}
22354
22355static pm_node_t *
22356parse_operator_equal(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
22357 pm_token_t operator = parser->current;
22358
22359 switch (PM_NODE_TYPE(node)) {
22360 case PM_BACK_REFERENCE_READ_NODE:
22361 case PM_NUMBERED_REFERENCE_READ_NODE:
22362 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
22364 case PM_GLOBAL_VARIABLE_READ_NODE: {
22365 parser_lex(parser);
22366
22367 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22368 pm_node_t *result = UP(pm_global_variable_operator_write_node_create(parser, node, &operator, value));
22369
22370 return result;
22371 }
22372 case PM_CLASS_VARIABLE_READ_NODE: {
22373 parser_lex(parser);
22374
22375 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22376 pm_node_t *result = UP(pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &operator, value));
22377
22378 return result;
22379 }
22380 case PM_CONSTANT_PATH_NODE: {
22381 parser_lex(parser);
22382
22383 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22384 pm_node_t *write = UP(pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &operator, value));
22385
22386 return parse_shareable_constant_write(parser, write);
22387 }
22388 case PM_CONSTANT_READ_NODE: {
22389 parser_lex(parser);
22390
22391 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22392 pm_node_t *write = UP(pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &operator, value));
22393
22394 if (context_def_p(parser)) {
22395 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
22396 }
22397
22398 return parse_shareable_constant_write(parser, write);
22399 }
22400 case PM_INSTANCE_VARIABLE_READ_NODE: {
22401 parser_lex(parser);
22402
22403 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22404 pm_node_t *result = UP(pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &operator, value));
22405
22406 return result;
22407 }
22408 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
22409 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
22410 parser_lex(parser);
22411
22412 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22413 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &operator, value, name, 0));
22414
22415 pm_node_unreference(parser, node);
22416 return result;
22417 }
22418 case PM_LOCAL_VARIABLE_READ_NODE: {
22419 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
22420 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
22421 pm_node_unreference(parser, node);
22422 }
22423
22425 parser_lex(parser);
22426
22427 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22428 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &operator, value, cast->name, cast->depth));
22429
22430 return result;
22431 }
22432 case PM_CALL_NODE: {
22433 parser_lex(parser);
22435
22436 // If we have a vcall (a method with no arguments and no
22437 // receiver that could have been a local variable) then we
22438 // will transform it into a local variable write.
22439 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22440 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
22441 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
22442 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22443 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &operator, value, constant_id, 0));
22444
22445 return result;
22446 }
22447
22448 // If there is no call operator and the message is "[]" then
22449 // this is an aref expression, and we can transform it into
22450 // an aset expression.
22451 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
22452 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22453 return UP(pm_index_operator_write_node_create(parser, cast, &operator, value));
22454 }
22455
22456 // If this node cannot be writable, then we have an error.
22457 if (pm_call_node_writable_p(parser, cast)) {
22458 parse_write_name(parser, &cast->name);
22459 } else {
22460 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22461 }
22462
22463 parse_call_operator_write(parser, cast, &operator);
22464 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22465 return UP(pm_call_operator_write_node_create(parser, cast, &operator, value));
22466 }
22467 case PM_MULTI_WRITE_NODE: {
22468 parser_lex(parser);
22469 pm_parser_err_token(parser, &operator, PM_ERR_OPERATOR_MULTI_ASSIGN);
22470 return node;
22471 }
22472 default:
22473 parser_lex(parser);
22474
22475 // In this case we have an operator but we don't know what it's for.
22476 // We need to treat it as an error. For now, we'll mark it as an error
22477 // and just skip right past it.
22478 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_str(parser->current.type));
22479 return node;
22480 }
22481}
22482
22483static pm_node_t *
22484parse_call(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, uint8_t flags, uint16_t depth) {
22485 pm_token_t operator = parser->current;
22486 parser_lex(parser);
22487
22488 pm_arguments_t arguments = { 0 };
22489
22490 // This if statement handles the foo.() syntax.
22491 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
22492 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
22493 return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments));
22494 }
22495
22496 switch (PM_NODE_TYPE(node)) {
22497 case PM_RESCUE_MODIFIER_NODE: {
22499 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22500 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22501 }
22502 break;
22503 }
22504 case PM_AND_NODE: {
22505 pm_and_node_t *cast = (pm_and_node_t *) node;
22506 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22507 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22508 }
22509 break;
22510 }
22511 case PM_OR_NODE: {
22512 pm_or_node_t *cast = (pm_or_node_t *) node;
22513 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22514 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22515 }
22516 break;
22517 }
22518 default:
22519 break;
22520 }
22521
22522 pm_token_t message;
22523
22524 switch (parser->current.type) {
22525 case PM_CASE_OPERATOR:
22526 case PM_CASE_KEYWORD:
22527 case PM_TOKEN_CONSTANT:
22528 case PM_TOKEN_IDENTIFIER:
22529 case PM_TOKEN_METHOD_NAME: {
22530 parser_lex(parser);
22531 message = parser->previous;
22532 break;
22533 }
22534 default: {
22535 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_str(parser->current.type));
22536 message = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
22537 }
22538 }
22539
22540 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22541 pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments);
22542
22543 if (
22544 (previous_binding_power == PM_BINDING_POWER_STATEMENT) &&
22545 arguments.arguments == NULL &&
22546 arguments.opening_loc.length == 0 &&
22547 match1(parser, PM_TOKEN_COMMA)
22548 ) {
22549 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22550 } else {
22551 return UP(call);
22552 }
22553}
22554
22555static pm_node_t *
22556parse_aref(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, uint8_t flags, uint16_t depth) {
22557 parser_lex(parser);
22558
22559 pm_arguments_t arguments = { 0 };
22560 arguments.opening_loc = TOK2LOC(parser, &parser->previous);
22561
22562 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
22563 parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
22564 expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET);
22565 }
22566
22567 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
22568
22569 // If we have a comma after the closing bracket then this is a multiple
22570 // assignment and we should parse the targets.
22571 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22572 pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments);
22573 return parse_targets_validate(parser, UP(aref), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22574 }
22575
22576 // If we're at the end of the arguments, we can now check if there is a
22577 // block node that starts with a {. If there is, then we can parse it and
22578 // add it to the arguments.
22579 pm_block_node_t *block = NULL;
22580 if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
22581 block = parse_block(parser, (uint16_t) (depth + 1));
22582 pm_arguments_validate_block(parser, &arguments, block);
22583 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
22584 block = parse_block(parser, (uint16_t) (depth + 1));
22585 }
22586
22587 if (block != NULL) {
22588 if (arguments.block != NULL) {
22589 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_AFTER_BLOCK);
22590 if (arguments.arguments == NULL) {
22591 arguments.arguments = pm_arguments_node_create(parser);
22592 }
22593 pm_arguments_node_arguments_append(parser->arena, arguments.arguments, arguments.block);
22594 }
22595
22596 arguments.block = UP(block);
22597 }
22598
22599 return UP(pm_call_node_aref_create(parser, node, &arguments));
22600}
22601
22602static PRISM_INLINE pm_node_t *
22603parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
22604 pm_token_t token = parser->current;
22605
22606 switch (token.type) {
22607 case PM_TOKEN_EQUAL:
22608 return parse_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22609 case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL:
22610 return parse_and_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22611 case PM_TOKEN_PIPE_PIPE_EQUAL:
22612 return parse_or_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22613 case PM_TOKEN_AMPERSAND_EQUAL:
22614 case PM_TOKEN_CARET_EQUAL:
22615 case PM_TOKEN_GREATER_GREATER_EQUAL:
22616 case PM_TOKEN_LESS_LESS_EQUAL:
22617 case PM_TOKEN_MINUS_EQUAL:
22618 case PM_TOKEN_PERCENT_EQUAL:
22619 case PM_TOKEN_PIPE_EQUAL:
22620 case PM_TOKEN_PLUS_EQUAL:
22621 case PM_TOKEN_SLASH_EQUAL:
22622 case PM_TOKEN_STAR_EQUAL:
22623 case PM_TOKEN_STAR_STAR_EQUAL:
22624 return parse_operator_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22625 case PM_TOKEN_AMPERSAND_AMPERSAND:
22626 case PM_TOKEN_KEYWORD_AND: {
22627 parser_lex(parser);
22628
22629 pm_node_t *right = parse_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (parser->previous.type == PM_TOKEN_KEYWORD_AND ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0)), PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22630 return UP(pm_and_node_create(parser, node, &token, right));
22631 }
22632 case PM_TOKEN_KEYWORD_OR:
22633 case PM_TOKEN_PIPE_PIPE: {
22634 parser_lex(parser);
22635
22636 pm_node_t *right = parse_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (parser->previous.type == PM_TOKEN_KEYWORD_OR ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0)), PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22637 return UP(pm_or_node_create(parser, node, &token, right));
22638 }
22639 case PM_TOKEN_EQUAL_TILDE:
22640 return parse_match(parser, node, binding_power, flags, depth);
22641 case PM_TOKEN_UAMPERSAND:
22642 case PM_TOKEN_USTAR:
22643 case PM_TOKEN_USTAR_STAR:
22644 // The only times this will occur are when we are in an error state,
22645 // but we'll put them in here so that errors can propagate.
22646 case PM_TOKEN_BANG_EQUAL:
22647 case PM_TOKEN_BANG_TILDE:
22648 case PM_TOKEN_EQUAL_EQUAL:
22649 case PM_TOKEN_EQUAL_EQUAL_EQUAL:
22650 case PM_TOKEN_LESS_EQUAL_GREATER:
22651 case PM_TOKEN_CARET:
22652 case PM_TOKEN_PIPE:
22653 case PM_TOKEN_AMPERSAND:
22654 case PM_TOKEN_GREATER_GREATER:
22655 case PM_TOKEN_LESS_LESS:
22656 case PM_TOKEN_MINUS:
22657 case PM_TOKEN_PLUS:
22658 case PM_TOKEN_PERCENT:
22659 case PM_TOKEN_SLASH:
22660 case PM_TOKEN_STAR:
22661 case PM_TOKEN_STAR_STAR: {
22662 parser_lex(parser);
22663 pm_token_t operator = parser->previous;
22664 switch (PM_NODE_TYPE(node)) {
22665 case PM_RESCUE_MODIFIER_NODE: {
22667 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22668 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22669 }
22670 break;
22671 }
22672 case PM_AND_NODE: {
22673 pm_and_node_t *cast = (pm_and_node_t *) node;
22674 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22675 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22676 }
22677 break;
22678 }
22679 case PM_OR_NODE: {
22680 pm_or_node_t *cast = (pm_or_node_t *) node;
22681 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22682 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22683 }
22684 break;
22685 }
22686 default:
22687 break;
22688 }
22689
22690 pm_node_t *argument = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22691 return UP(pm_call_node_binary_create(parser, node, &token, argument, 0));
22692 }
22693 case PM_TOKEN_GREATER:
22694 case PM_TOKEN_GREATER_EQUAL:
22695 case PM_TOKEN_LESS:
22696 case PM_TOKEN_LESS_EQUAL: {
22697 if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) {
22698 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON);
22699 }
22700
22701 parser_lex(parser);
22702 pm_node_t *argument = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22703 return UP(pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON));
22704 }
22705 case PM_TOKEN_AMPERSAND_DOT:
22706 case PM_TOKEN_DOT:
22707 return parse_call(parser, node, previous_binding_power, flags, depth);
22708 case PM_TOKEN_DOT_DOT:
22709 case PM_TOKEN_DOT_DOT_DOT: {
22710 parser_lex(parser);
22711
22712 pm_node_t *right = NULL;
22713 if (token_begins_expression_p(parser->current.type)) {
22714 right = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22715 }
22716
22717 return UP(pm_range_node_create(parser, node, &token, right));
22718 }
22719 case PM_TOKEN_KEYWORD_IF_MODIFIER: {
22720 pm_token_t keyword = parser->current;
22721 parser_lex(parser);
22722
22723 pm_node_t *predicate = parse_value_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CONDITIONAL_IF_PREDICATE, (uint16_t) (depth + 1));
22724 return UP(pm_if_node_modifier_create(parser, node, &keyword, predicate));
22725 }
22726 case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: {
22727 pm_token_t keyword = parser->current;
22728 parser_lex(parser);
22729
22730 pm_node_t *predicate = parse_value_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CONDITIONAL_UNLESS_PREDICATE, (uint16_t) (depth + 1));
22731 return UP(pm_unless_node_modifier_create(parser, node, &keyword, predicate));
22732 }
22733 case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: {
22734 parser_lex(parser);
22735 pm_statements_node_t *statements = pm_statements_node_create(parser);
22736 pm_statements_node_body_append(parser, statements, node, true);
22737
22738 pm_node_t *predicate = parse_value_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CONDITIONAL_UNTIL_PREDICATE, (uint16_t) (depth + 1));
22739 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));
22740 }
22741 case PM_TOKEN_KEYWORD_WHILE_MODIFIER: {
22742 parser_lex(parser);
22743 pm_statements_node_t *statements = pm_statements_node_create(parser);
22744 pm_statements_node_body_append(parser, statements, node, true);
22745
22746 pm_node_t *predicate = parse_value_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_CONDITIONAL_WHILE_PREDICATE, (uint16_t) (depth + 1));
22747 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));
22748 }
22749 case PM_TOKEN_QUESTION_MARK:
22750 return UP(parse_ternary(parser, node, flags, depth));
22751 case PM_TOKEN_COLON_COLON:
22752 return parse_constant_path_infix(parser, node, previous_binding_power, flags, depth);
22753 case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: {
22754 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
22755 parser_lex(parser);
22756 accept1(parser, PM_TOKEN_NEWLINE);
22757
22758 pm_node_t *value = parse_rescue_modifier_value(parser, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), previous_binding_power == PM_BINDING_POWER_STATEMENT, (uint16_t) (depth + 1));
22759 context_pop(parser);
22760
22761 return UP(pm_rescue_modifier_node_create(parser, node, &token, value));
22762 }
22763 case PM_TOKEN_BRACKET_LEFT:
22764 return parse_aref(parser, node, previous_binding_power, flags, depth);
22765 case PM_TOKEN_KEYWORD_IN: {
22766 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22767 parser->pattern_matching_newlines = true;
22768
22769 pm_token_t operator = parser->current;
22770 parser->command_start = false;
22771 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22772 parser_lex(parser);
22773
22774 pm_constant_id_set_t captures = { 0 };
22775 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));
22776
22777 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22778
22779 return UP(pm_match_predicate_node_create(parser, node, pattern, &operator));
22780 }
22781 case PM_TOKEN_EQUAL_GREATER: {
22782 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22783 parser->pattern_matching_newlines = true;
22784
22785 pm_token_t operator = parser->current;
22786 parser->command_start = false;
22787 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22788 parser_lex(parser);
22789
22790 pm_constant_id_set_t captures = { 0 };
22791 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));
22792
22793 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22794
22795 return UP(pm_match_required_node_create(parser, node, pattern, &operator));
22796 }
22797 default:
22798 assert(false && "unreachable");
22799 return NULL;
22800 }
22801}
22802
22803#undef PM_PARSE_PATTERN_SINGLE
22804#undef PM_PARSE_PATTERN_TOP
22805#undef PM_PARSE_PATTERN_MULTI
22806
22819static bool
22820parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) {
22821 pm_binding_power_t left = pm_binding_powers[parser->current.type].left;
22822
22823 switch (PM_NODE_TYPE(node)) {
22824 case PM_MULTI_WRITE_NODE:
22825 case PM_RETURN_NODE:
22826 case PM_BREAK_NODE:
22827 case PM_NEXT_NODE:
22828 return left > PM_BINDING_POWER_MODIFIER;
22829 case PM_CLASS_VARIABLE_WRITE_NODE:
22830 case PM_CONSTANT_PATH_WRITE_NODE:
22831 case PM_CONSTANT_WRITE_NODE:
22832 case PM_GLOBAL_VARIABLE_WRITE_NODE:
22833 case PM_INSTANCE_VARIABLE_WRITE_NODE:
22834 case PM_LOCAL_VARIABLE_WRITE_NODE:
22835 return PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && left > PM_BINDING_POWER_MODIFIER;
22836 case PM_CALL_NODE: {
22837 // Calls with an implicit array on the right-hand side are
22838 // statements and can only be followed by modifiers.
22839 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)) {
22840 return left > PM_BINDING_POWER_MODIFIER;
22841 }
22842
22843 // Command-style calls (including block commands like
22844 // `foo bar do end`) can only be followed by composition
22845 // (and/or) and modifier (if/unless/etc.) operators.
22846 if (pm_command_call_value_p(parser, node)) {
22847 return left > PM_BINDING_POWER_COMPOSITION;
22848 }
22849
22850 // A block call (command with do-block, or any call chained
22851 // from one) can only be followed by call chaining (., ::,
22852 // &.), composition (and/or), and modifier operators.
22853 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22854 }
22855 case PM_SUPER_NODE:
22856 case PM_YIELD_NODE:
22857 // Command-style super/yield (without parens) can only be followed
22858 // by composition and modifier operators.
22859 if (pm_command_call_value_p(parser, node)) {
22860 return left > PM_BINDING_POWER_COMPOSITION;
22861 }
22862
22863 /* A super carrying a do-block is a block call, so it may also be
22864 * followed by call chaining (`.`, `::`, `&.`). */
22865 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22866 case PM_DEF_NODE:
22867 // An endless method whose body is a command-style call (e.g.,
22868 // `def f = foo bar`) is a command write and can only be
22869 // followed by modifiers.
22870 return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(parser, node);
22871 case PM_RESCUE_MODIFIER_NODE:
22872 // A rescue modifier whose handler is a pattern match (=> or in)
22873 // produces a statement and cannot be followed by operators above
22874 // the modifier level.
22875 if (left > PM_BINDING_POWER_MODIFIER) {
22877 pm_node_t *rescue_expression = cast->rescue_expression;
22878 return PM_NODE_TYPE_P(rescue_expression, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(rescue_expression, PM_MATCH_PREDICATE_NODE);
22879 }
22880 return false;
22881 default:
22882 return false;
22883 }
22884}
22885
22894static pm_node_t *
22895parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
22896 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
22897 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
22898 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
22899 }
22900
22901 pm_node_t *node = parse_expression_prefix(parser, binding_power, flags, diag_id, depth);
22902
22903 // Some prefix nodes are statements and can only be followed by modifiers
22904 // (if/unless/while/until/rescue) or nothing at all. We check these cheaply
22905 // here before entering the infix loop.
22906 switch (PM_NODE_TYPE(node)) {
22907 case PM_ERROR_RECOVERY_NODE:
22908 return node;
22909 case PM_PRE_EXECUTION_NODE:
22910 return node;
22911 case PM_POST_EXECUTION_NODE:
22912 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
22913 case PM_ALIAS_METHOD_NODE:
22914 case PM_UNDEF_NODE:
22915 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
22916 return node;
22917 }
22918 break;
22919 case PM_CALL_NODE:
22920 case PM_SUPER_NODE:
22921 case PM_YIELD_NODE:
22922 case PM_DEF_NODE:
22923 if (parse_expression_terminator(parser, node)) {
22924 return node;
22925 }
22926 break;
22927 case PM_SYMBOL_NODE:
22928 if (pm_symbol_node_label_p(parser, node)) {
22929 return node;
22930 }
22931 break;
22932 default:
22933 break;
22934 }
22935
22936 // Look and see if the next token can be parsed as an infix operator. If it
22937 // can, then we'll parse it using parse_expression_infix.
22938 pm_binding_powers_t current_binding_powers;
22939 pm_token_type_t current_token_type;
22940
22941 while (
22942 current_token_type = parser->current.type,
22943 current_binding_powers = pm_binding_powers[current_token_type],
22944 binding_power <= current_binding_powers.left &&
22945 current_binding_powers.binary
22946 ) {
22947 node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, flags, (uint16_t) (depth + 1));
22948 if (parse_expression_terminator(parser, node)) return node;
22949
22950 // If the operator is nonassoc and we should not be able to parse the
22951 // upcoming infix operator, break.
22952 if (current_binding_powers.nonassoc) {
22953 // If we are about to parse another non-associative operator at the
22954 // same precedence as the one we just parsed, then we need to add an
22955 // error. This covers chaining the same operator (`1 == 2 == 3`) as
22956 // well as different operators that share a precedence, since they
22957 // are equally non-associative with one another (`1 == 2 != 3`,
22958 // `1...2..3`).
22959 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22960 if (next_binding_powers.nonassoc && next_binding_powers.left == current_binding_powers.left) {
22961 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_str(parser->current.type), pm_token_str(current_token_type));
22962 break;
22963 }
22964
22965 // If this is an endless range, then we need to reject a couple of
22966 // additional operators because it violates the normal operator
22967 // precedence rules. Those patterns are:
22968 //
22969 // 1.. & 2
22970 // 1.. * 2
22971 //
22972 if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) {
22973 if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) {
22974 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_NON_ASSOCIATIVE_OPERATOR, pm_token_str(parser->current.type), pm_token_str(current_token_type));
22975 break;
22976 }
22977
22978 if (PM_BINDING_POWER_TERM <= next_binding_powers.left) {
22979 break;
22980 }
22981 } else if (current_binding_powers.left <= next_binding_powers.left) {
22982 break;
22983 }
22984 }
22985
22986 if (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) {
22987 // A command-style method call is only accepted on method chains.
22988 // Thus, we check whether the parsed node can continue method chains.
22989 // The method chain can continue if the parsed node is one of the following five kinds:
22990 // (1) index access: foo[1]
22991 // (2) attribute access: foo.bar
22992 // (3) method call with parenthesis: foo.bar(1)
22993 // (4) method call with a block: foo.bar do end
22994 // (5) constant path: foo::Bar
22995 switch (node->type) {
22996 case PM_CALL_NODE: {
22998 if (
22999 // (1) foo[1]
23000 !(
23001 cast->call_operator_loc.length == 0 &&
23002 cast->message_loc.length > 0 &&
23003 parser->start[cast->message_loc.start] == '[' &&
23004 parser->start[cast->message_loc.start + cast->message_loc.length - 1] == ']'
23005 ) &&
23006 // (2) foo.bar
23007 !(
23008 cast->call_operator_loc.length > 0 &&
23009 cast->arguments == NULL &&
23010 cast->block == NULL &&
23011 cast->opening_loc.length == 0
23012 ) &&
23013 // (3) foo.bar(1)
23014 !(
23015 cast->call_operator_loc.length > 0 &&
23016 cast->opening_loc.length > 0
23017 ) &&
23018 // (4) foo.bar do end
23019 !(
23020 cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
23021 )
23022 ) {
23023 flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
23024 }
23025 break;
23026 }
23027 // (5) foo::Bar
23028 case PM_CONSTANT_PATH_NODE:
23029 break;
23030 default:
23031 flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
23032 break;
23033 }
23034 }
23035
23036 if (context_terminator(parser->current_context->context, &parser->current)) {
23037 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
23038 if (
23039 !next_binding_powers.binary ||
23040 binding_power > next_binding_powers.left ||
23041 (PM_NODE_TYPE_P(node, PM_CALL_NODE) && pm_call_node_command_p((pm_call_node_t *) node))
23042 ) {
23043 return node;
23044 }
23045 }
23046 }
23047
23048 return node;
23049}
23050
23055static pm_statements_node_t *
23056wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
23057 if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) {
23058 if (statements == NULL) {
23059 statements = pm_statements_node_create(parser);
23060 }
23061
23062 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
23063 pm_arguments_node_arguments_append(
23064 parser->arena,
23065 arguments,
23066 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)))
23067 );
23068
23069 pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create(
23070 parser,
23071 arguments,
23072 pm_parser_constant_id_constant(parser, "print", 5)
23073 )), true);
23074 }
23075
23076 if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) {
23077 if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) {
23078 if (statements == NULL) {
23079 statements = pm_statements_node_create(parser);
23080 }
23081
23082 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
23083 pm_arguments_node_arguments_append(
23084 parser->arena,
23085 arguments,
23086 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)))
23087 );
23088
23089 pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2));
23090 pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, UP(receiver), "split", arguments);
23091
23092 pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create(
23093 parser,
23094 pm_parser_constant_id_constant(parser, "$F", 2),
23095 UP(call)
23096 );
23097
23098 pm_statements_node_body_prepend(parser->arena, statements, UP(write));
23099 }
23100
23101 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
23102 pm_arguments_node_arguments_append(
23103 parser->arena,
23104 arguments,
23105 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)))
23106 );
23107
23108 if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) {
23109 pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser);
23110 pm_keyword_hash_node_elements_append(parser->arena, keywords, UP(pm_assoc_node_create(
23111 parser,
23112 UP(pm_symbol_node_synthesized_create(parser, "chomp")),
23113 NULL,
23114 UP(pm_true_node_synthesized_create(parser))
23115 )));
23116
23117 pm_arguments_node_arguments_append(parser->arena, arguments, UP(keywords));
23118 pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS);
23119 }
23120
23121 pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
23122 pm_statements_node_body_append(parser, wrapped_statements, UP(pm_while_node_synthesized_create(
23123 parser,
23124 UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))),
23125 statements
23126 )), true);
23127
23128 statements = wrapped_statements;
23129 }
23130
23131 return statements;
23132}
23133
23137static pm_node_t *
23138parse_program(pm_parser_t *parser) {
23139 // If the current scope is NULL, then we want to push a new top level scope.
23140 // The current scope could exist in the event that we are parsing an eval
23141 // and the user has passed into scopes that already exist.
23142 if (parser->current_scope == NULL) {
23143 pm_parser_scope_push(parser, true);
23144 }
23145
23146 pm_node_list_t current_block_exits = { 0 };
23147 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
23148
23149 parser_lex(parser);
23150 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN, 0);
23151
23152 if (statements != NULL && !parser->parsing_eval) {
23153 // If we have statements, then the top-level statement should be
23154 // explicitly checked as well. We have to do this here because
23155 // everywhere else we check all but the last statement.
23156 assert(statements->body.size > 0);
23157 pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]);
23158 }
23159
23160 pm_constant_id_list_t locals;
23161 pm_locals_order(parser, &parser->current_scope->locals, &locals, true);
23162 pm_parser_scope_pop(parser);
23163
23164 // At the top level, see if we need to wrap the statements in a program
23165 // node with a while loop based on the options.
23166 if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
23167 statements = wrap_statements(parser, statements);
23168 } else {
23169 flush_block_exits(parser, previous_block_exits);
23170 }
23171
23172 // If this is an empty file, then we're still going to parse all of the
23173 // statements in order to gather up all of the comments and such. Here we'll
23174 // correct the location information.
23175 if (statements == NULL) {
23176 statements = pm_statements_node_create(parser);
23177 statements->base.location = (pm_location_t) { 0 };
23178 }
23179
23180 return UP(pm_program_node_create(parser, &locals, statements));
23181}
23182
23183/******************************************************************************/
23184/* External functions */
23185/******************************************************************************/
23186
23196static const char *
23197pm_strnstr(const char *big, const char *little, size_t big_length) {
23198 size_t little_length = strlen(little);
23199
23200 for (const char *max = big + big_length - little_length; big <= max; big++) {
23201 if (*big == *little && memcmp(big, little, little_length) == 0) return big;
23202 }
23203
23204 return NULL;
23205}
23206
23207#ifdef _WIN32
23208#define pm_parser_warn_shebang_carriage_return(parser, start, length) ((void) 0)
23209#else
23215static void
23216pm_parser_warn_shebang_carriage_return(pm_parser_t *parser, const uint8_t *start, size_t length) {
23217 if (length > 2 && start[length - 2] == '\r' && start[length - 1] == '\n') {
23218 pm_parser_warn(parser, U32(start - parser->start), U32(length), PM_WARN_SHEBANG_CARRIAGE_RETURN);
23219 }
23220}
23221#endif
23222
23227static void
23228pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const char *engine, size_t length) {
23229 const char *switches = pm_strnstr(engine, " -", length);
23230 if (switches == NULL) return;
23231
23232 pm_options_t next_options = *options;
23233 options->shebang_callback(
23234 &next_options,
23235 (const uint8_t *) (switches + 1),
23236 length - ((size_t) (switches - engine)) - 1,
23237 options->shebang_callback_data
23238 );
23239
23240 size_t encoding_length;
23241 if ((encoding_length = pm_string_length(&next_options.encoding)) > 0) {
23242 const uint8_t *encoding_source = pm_string_source(&next_options.encoding);
23243 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
23244 }
23245
23246 parser->command_line = next_options.command_line;
23247 parser->frozen_string_literal = next_options.frozen_string_literal;
23248}
23249
23256#ifndef PM_CONSTANT_POOL_MIN_CAPACITY
23257#define PM_CONSTANT_POOL_MIN_CAPACITY 4
23258#endif
23259
23263void
23264pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) {
23265 assert(arena != NULL);
23266 assert(source != NULL);
23267
23268 *parser = (pm_parser_t) {
23269 .arena = arena,
23270 .metadata_arena = { 0 },
23271 .node_id = 0,
23272 .lex_state = PM_LEX_STATE_BEG,
23273 .enclosure_nesting = 0,
23274 .lambda_enclosure_nesting = -1,
23275 .brace_nesting = 0,
23276 .do_loop_stack = 0,
23277 .accepts_block_stack = 0,
23278 .lex_modes = {
23279 .current = &parser->lex_modes.base,
23280 .base = { .mode = PM_LEX_DEFAULT },
23281 },
23282 .start = source,
23283 .end = source + size,
23284 .previous = { .type = PM_TOKEN_EOF, .start = source, .end = source },
23285 .current = { .type = PM_TOKEN_EOF, .start = source, .end = source },
23286 .next_start = NULL,
23287 .heredoc_end = NULL,
23288 .data_loc = { 0 },
23289 .comment_list = { 0 },
23290 .magic_comment_list = { 0 },
23291 .warning_list = { 0 },
23292 .error_list = { 0 },
23293 .current_scope = NULL,
23294 .current_context = NULL,
23295 .encoding = PM_ENCODING_UTF_8_ENTRY,
23296 .encoding_changed_callback = NULL,
23297 .encoding_comment_start = source,
23298 .lex_callback = { 0 },
23299 .filepath = PM_STRING_EMPTY,
23300 .constant_pool = { 0 },
23301 .line_offsets = { 0 },
23302 .integer = { 0 },
23303 .current_string = PM_STRING_EMPTY,
23304 .start_line = 1,
23305 .explicit_encoding = NULL,
23306 .command_line = 0,
23307 .parsing_eval = false,
23308 .partial_script = false,
23309 .attach_comments = false,
23310 .command_start = true,
23311 .recovering = false,
23312 .continuable = true,
23313 .encoding_locked = false,
23314 .encoding_changed = false,
23315 .pattern_matching_newlines = false,
23316 .in_keyword_arg = false,
23317 .current_block_exits = NULL,
23318 .semantic_token_seen = false,
23319 .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET,
23320 .warn_mismatched_indentation = true
23321 };
23322
23323 /* Pre-size the arenas based on input size to reduce the number of block
23324 * allocations (and the kernel page zeroing they trigger). The ratios were
23325 * measured empirically: AST arena ~3.3x input, metadata arena ~1.1x input.
23326 * The reserve call is a no-op when the capacity is at or below the default
23327 * arena block size, so small inputs don't waste an extra allocation. */
23328 if (size <= SIZE_MAX / 4) pm_arena_reserve(arena, size * 4);
23329 if (size <= SIZE_MAX / 5 * 4) pm_arena_reserve(&parser->metadata_arena, size + size / 4);
23330
23331 /* Initialize the constant pool. Measured across 1532 Ruby stdlib files, the
23332 * bytes/constant ratio has a median of ~56 and a 90th percentile of ~135.
23333 * We use 120 as a balance between over-allocation waste and resize
23334 * frequency. Resizes are cheap with arena allocation, so we lean toward
23335 * under-estimating. */
23336 uint32_t constant_size = ((uint32_t) size) / 120;
23337 if (constant_size < PM_CONSTANT_POOL_MIN_CAPACITY) constant_size = PM_CONSTANT_POOL_MIN_CAPACITY;
23338 pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size);
23339
23340 /* Initialize the line offset list. Similar to the constant pool, we are
23341 * going to estimate the number of newlines that we will need based on the
23342 * size of the input. */
23343 size_t newline_size = size / 22;
23344 pm_line_offset_list_init(&parser->metadata_arena, &parser->line_offsets, newline_size < 4 ? 4 : newline_size);
23345
23346 // If options were provided to this parse, establish them here.
23347 if (options != NULL) {
23348 // filepath option
23349 if (pm_string_length(&options->filepath) > 0) {
23350 parser->filepath = options->filepath;
23351 }
23352
23353 // line option
23354 parser->start_line = options->line;
23355
23356 // encoding option
23357 size_t encoding_length = pm_string_length(&options->encoding);
23358 if (encoding_length > 0) {
23359 const uint8_t *encoding_source = pm_string_source(&options->encoding);
23360 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
23361 }
23362
23363 // encoding_locked option
23364 parser->encoding_locked = options->encoding_locked;
23365
23366 // frozen_string_literal option
23367 parser->frozen_string_literal = options->frozen_string_literal;
23368
23369 // command_line option
23370 parser->command_line = options->command_line;
23371
23372 // version option
23373 parser->version = options->version;
23374
23375 // partial_script
23376 parser->partial_script = options->partial_script;
23377
23378 // attach_comments
23379 parser->attach_comments = options->attach_comments;
23380
23381 // scopes option
23382 parser->parsing_eval = options->scopes_count > 0;
23383 if (parser->parsing_eval) parser->warn_mismatched_indentation = false;
23384
23385 for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
23386 const pm_options_scope_t *scope = pm_options_scope(options, scope_index);
23387 pm_parser_scope_push(parser, scope_index == 0);
23388
23389 // Scopes given from the outside are not allowed to have numbered
23390 // parameters.
23391 parser->current_scope->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED;
23392
23393 for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
23394 const pm_string_t *local = pm_options_scope_local(scope, local_index);
23395
23396 const uint8_t *source = pm_string_source(local);
23397 size_t length = pm_string_length(local);
23398
23399 uint8_t *allocated = (uint8_t *) pm_arena_alloc(&parser->metadata_arena, length, 1);
23400 memcpy(allocated, source, length);
23401 pm_parser_local_add_owned(parser, allocated, length);
23402 }
23403 }
23404 }
23405
23406 // Now that we have established the user-provided options, check if
23407 // a version was given and parse as the latest version otherwise.
23408 if (parser->version == PM_OPTIONS_VERSION_UNSET) {
23409 parser->version = PM_OPTIONS_VERSION_LATEST;
23410 }
23411
23412 pm_accepts_block_stack_push(parser, true);
23413
23414 // Skip past the UTF-8 BOM if it exists.
23415 if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) {
23416 parser->current.end += 3;
23417 parser->encoding_comment_start += 3;
23418
23419 if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
23420 parser->encoding = PM_ENCODING_UTF_8_ENTRY;
23421 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
23422 }
23423 }
23424
23425 // If the -x command line flag is set, or the first shebang of the file does
23426 // not include "ruby", then we'll search for a shebang that does include
23427 // "ruby" and start parsing from there.
23428 bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
23429
23430 // If the first two bytes of the source are a shebang, then we will do a bit
23431 // of extra processing.
23432 //
23433 // First, we'll indicate that the encoding comment is at the end of the
23434 // shebang. This means that when a shebang is present the encoding comment
23435 // can begin on the second line.
23436 //
23437 // Second, we will check if the shebang includes "ruby". If it does, then we
23438 // we will start parsing from there. We will also potentially warning the
23439 // user if there is a carriage return at the end of the shebang. We will
23440 // also potentially call the shebang callback if this is the main script to
23441 // allow the caller to parse the shebang and find any command-line options.
23442 // If the shebang does not include "ruby" and this is the main script being
23443 // parsed, then we will start searching the file for a shebang that does
23444 // contain "ruby" as if -x were passed on the command line.
23445 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
23446 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end);
23447
23448 if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') {
23449 const char *engine;
23450
23451 if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) {
23452 if (newline != NULL) {
23453 parser->encoding_comment_start = newline + 1;
23454
23455 if (options == NULL || options->main_script) {
23456 pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1);
23457 }
23458 }
23459
23460 if (options != NULL && options->main_script && options->shebang_callback != NULL) {
23461 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start)));
23462 }
23463
23464 search_shebang = false;
23465 } else if (options != NULL && options->main_script && !parser->parsing_eval) {
23466 search_shebang = true;
23467 }
23468 }
23469
23470 // Here we're going to find the first shebang that includes "ruby" and start
23471 // parsing from there.
23472 if (search_shebang) {
23473 // If a shebang that includes "ruby" is not found, then we're going to a
23474 // a load error to the list of errors on the parser.
23475 bool found_shebang = false;
23476
23477 // This is going to point to the start of each line as we check it.
23478 // We'll maintain a moving window looking at each line at they come.
23479 const uint8_t *cursor = parser->start;
23480
23481 // The newline pointer points to the end of the current line that we're
23482 // considering. If it is NULL, then we're at the end of the file.
23483 const uint8_t *newline = next_newline(cursor, parser->end - cursor);
23484
23485 while (newline != NULL) {
23486 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
23487
23488 cursor = newline + 1;
23489 newline = next_newline(cursor, parser->end - cursor);
23490
23491 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - cursor);
23492 if (length > 2 && cursor[0] == '#' && cursor[1] == '!') {
23493 const char *engine;
23494 if ((engine = pm_strnstr((const char *) cursor, "ruby", length)) != NULL) {
23495 found_shebang = true;
23496
23497 if (newline != NULL) {
23498 pm_parser_warn_shebang_carriage_return(parser, cursor, length + 1);
23499 parser->encoding_comment_start = newline + 1;
23500 }
23501
23502 if (options != NULL && options->shebang_callback != NULL) {
23503 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) cursor)));
23504 }
23505
23506 break;
23507 }
23508 }
23509 }
23510
23511 if (found_shebang) {
23512 parser->previous = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23513 parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23514 } else {
23515 pm_parser_err(parser, 0, 0, PM_ERR_SCRIPT_NOT_FOUND);
23516 pm_line_offset_list_clear(&parser->line_offsets);
23517 }
23518 }
23519
23520 // The encoding comment can start after any amount of inline whitespace, so
23521 // here we'll advance it to the first non-inline-whitespace character so
23522 // that it is ready for future comparisons.
23523 parser->encoding_comment_start += pm_strspn_inline_whitespace(parser->encoding_comment_start, parser->end - parser->encoding_comment_start);
23524}
23525
23534pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) {
23535 pm_parser_t *parser = (pm_parser_t *) xmalloc(sizeof(pm_parser_t));
23536 if (parser == NULL) PRISM_OOM();
23537
23538 pm_parser_init(arena, parser, source, size, options);
23539 return parser;
23540}
23541
23545void
23546pm_parser_cleanup(pm_parser_t *parser) {
23547 pm_arena_cleanup(&parser->metadata_arena);
23548 pm_arena_cleanup(&parser->scratch_arena);
23549}
23550
23554void
23556 pm_parser_cleanup(parser);
23557 xfree_sized(parser, sizeof(pm_parser_t));
23558}
23559
23565static bool
23566pm_parse_err_is_fatal(pm_diagnostic_id_t diag_id) {
23567 switch (diag_id) {
23568 case PM_ERR_ARRAY_EXPRESSION_AFTER_STAR:
23569 case PM_ERR_BEGIN_UPCASE_BRACE:
23570 case PM_ERR_CLASS_VARIABLE_BARE:
23571 case PM_ERR_END_UPCASE_BRACE:
23572 case PM_ERR_ESCAPE_INVALID_HEXADECIMAL:
23573 case PM_ERR_ESCAPE_INVALID_UNICODE_LIST:
23574 case PM_ERR_ESCAPE_INVALID_UNICODE_SHORT:
23575 case PM_ERR_EXPRESSION_NOT_WRITABLE:
23576 case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF:
23577 case PM_ERR_FLOAT_PARSE:
23578 case PM_ERR_GLOBAL_VARIABLE_BARE:
23579 case PM_ERR_HASH_KEY:
23580 case PM_ERR_HEREDOC_IDENTIFIER:
23581 case PM_ERR_INSTANCE_VARIABLE_BARE:
23582 case PM_ERR_INVALID_BLOCK_EXIT:
23583 case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT:
23584 case PM_ERR_INVALID_FLOAT_EXPONENT:
23585 case PM_ERR_INVALID_NUMBER_BINARY:
23586 case PM_ERR_INVALID_NUMBER_DECIMAL:
23587 case PM_ERR_INVALID_NUMBER_HEXADECIMAL:
23588 case PM_ERR_INVALID_NUMBER_OCTAL:
23589 case PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING:
23590 case PM_ERR_NO_LOCAL_VARIABLE:
23591 case PM_ERR_PARAMETER_ORDER:
23592 case PM_ERR_STATEMENT_UNDEF:
23593 case PM_ERR_VOID_EXPRESSION:
23594 return true;
23595 default:
23596 return false;
23597 }
23598}
23599
23633static void
23634pm_parse_continuable(pm_parser_t *parser) {
23635 // If there are no errors then there is nothing to continue.
23636 if (parser->error_list.size == 0) {
23637 parser->continuable = false;
23638 return;
23639 }
23640
23641 if (!parser->continuable) return;
23642
23643 size_t source_length = (size_t) (parser->end - parser->start);
23644
23645 // First pass: check if there are any non-stray, non-fatal errors.
23646 bool has_non_stray_error = false;
23647 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23648 if (error->diag_id != PM_ERR_UNEXPECTED_TOKEN_IGNORE && error->diag_id != PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT && !pm_parse_err_is_fatal(error->diag_id)) {
23649 has_non_stray_error = true;
23650 break;
23651 }
23652 }
23653
23654 // Second pass: check each error. We track the minimum source position
23655 // among non-stray, non-fatal errors seen so far in list order, which
23656 // lets us detect cascade stray tokens.
23657 size_t non_stray_min_start = SIZE_MAX;
23658
23659 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23660 size_t error_start = (size_t) error->location.start;
23661 size_t error_end = error_start + (size_t) error->location.length;
23662 bool at_eof = error_end >= source_length;
23663
23664 // Fatal errors are non-continuable unless they occur at EOF.
23665 if (pm_parse_err_is_fatal(error->diag_id) && !at_eof) {
23666 parser->continuable = false;
23667 return;
23668 }
23669
23670 // Track non-stray, non-fatal error positions in list order.
23671 if (error->diag_id != PM_ERR_UNEXPECTED_TOKEN_IGNORE &&
23672 error->diag_id != PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT) {
23673 if (error_start < non_stray_min_start) non_stray_min_start = error_start;
23674 continue;
23675 }
23676
23677 // This is a stray token. Determine if it is a cascade effect
23678 // of a preceding error or genuinely stray.
23679
23680 // Rule (a): a non-stray error was seen earlier in the list at a
23681 // strictly earlier position — this stray is a cascade effect.
23682 if (non_stray_min_start < error_start) continue;
23683
23684 // Rule (b): this stray is at EOF with valid code before it.
23685 // Single-byte stray tokens at EOF (like `\` for line continuation)
23686 // are likely truncated tokens. Multi-byte stray tokens (like the
23687 // keyword `end`) need additional evidence that they are cascade
23688 // effects (i.e. non-stray errors exist elsewhere).
23689 if (at_eof && error_start > 0) {
23690 // Exception: closing delimiters at EOF are genuinely stray.
23691 if (error->location.length == 1) {
23692 const uint8_t *byte = parser->start + error_start;
23693 if (*byte == ')' || *byte == ']' || *byte == '}') {
23694 parser->continuable = false;
23695 return;
23696 }
23697
23698 // Single-byte non-delimiter stray at EOF: cascade.
23699 continue;
23700 }
23701
23702 // Multi-byte stray at EOF: cascade only if there are
23703 // non-stray errors (evidence of a preceding parse failure).
23704 if (has_non_stray_error) continue;
23705 }
23706
23707 // Rule (c): a stray `=` at the start of a line could be the
23708 // beginning of an embedded document (`=begin`). The remaining
23709 // bytes after `=` parse as an identifier, so the error is not
23710 // at EOF, but the construct is genuinely incomplete.
23711 if (error->location.length == 1) {
23712 const uint8_t *byte = parser->start + error_start;
23713 if (*byte == '=' && (error_start == 0 || *(byte - 1) == '\n')) continue;
23714 }
23715
23716 // This stray token is genuinely non-continuable.
23717 parser->continuable = false;
23718 return;
23719 }
23720}
23721
23725pm_node_t *
23727 pm_node_t *node = parse_program(parser);
23728 pm_parse_continuable(parser);
23729 if (parser->attach_comments) pm_comments_attach(parser, node);
23730 pm_arena_cleanup(&parser->scratch_arena);
23731 return node;
23732}
23733
23740pm_node_t *
23741pm_parse_stream(pm_parser_t **parser, pm_arena_t *arena, pm_source_t *source, const pm_options_t *options) {
23742 bool eof = pm_source_stream_read(source);
23743
23744 pm_parser_t *tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23745 pm_node_t *node = pm_parse(tmp);
23746
23747 while (!eof && tmp->error_list.size > 0) {
23748 eof = pm_source_stream_read(source);
23749
23750 pm_parser_free(tmp);
23751 pm_arena_cleanup(arena);
23752
23753 tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23754 node = pm_parse(tmp);
23755 }
23756
23757 *parser = tmp;
23758 return node;
23759}
23760
23761#undef PM_CASE_KEYWORD
23762#undef PM_CASE_OPERATOR
23763#undef PM_CASE_WRITABLE
23764#undef PM_STRING_EMPTY
23765
23766// We optionally support serializing to a binary string. For systems that don't
23767// want or need this functionality, it can be turned off with the
23768// PRISM_EXCLUDE_SERIALIZATION define.
23769#ifndef PRISM_EXCLUDE_SERIALIZATION
23770
23771static PRISM_INLINE void
23772pm_serialize_header(pm_buffer_t *buffer) {
23773 pm_buffer_append_string(buffer, "PRISM", 5);
23774 pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR);
23775 pm_buffer_append_byte(buffer, PRISM_VERSION_MINOR);
23776 pm_buffer_append_byte(buffer, PRISM_VERSION_PATCH);
23777 pm_buffer_append_byte(buffer, PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS ? 1 : 0);
23778}
23779
23783void
23784pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
23785 pm_serialize_header(buffer);
23786 pm_serialize_content(parser, node, buffer);
23787 pm_buffer_append_byte(buffer, '\0');
23788}
23789
23794void
23795pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23796 pm_options_t options = { 0 };
23797 pm_options_read(&options, data);
23798
23799 pm_arena_t arena = { 0 };
23800 pm_parser_t parser;
23801 pm_parser_init(&arena, &parser, source, size, &options);
23802
23803 pm_node_t *node = pm_parse(&parser);
23804
23805 pm_serialize_header(buffer);
23806 pm_serialize_content(&parser, node, buffer);
23807 pm_buffer_append_byte(buffer, '\0');
23808
23809 pm_parser_cleanup(&parser);
23810 pm_arena_cleanup(&arena);
23811 pm_options_cleanup(&options);
23812}
23813
23818void
23819pm_serialize_parse_stream(pm_buffer_t *buffer, pm_source_t *source, const char *data) {
23820 pm_arena_t arena = { 0 };
23821 pm_parser_t *parser;
23822 pm_options_t options = { 0 };
23823 pm_options_read(&options, data);
23824
23825 pm_node_t *node = pm_parse_stream(&parser, &arena, source, &options);
23826 pm_serialize_header(buffer);
23827 pm_serialize_content(parser, node, buffer);
23828 pm_buffer_append_byte(buffer, '\0');
23829
23830 pm_parser_free(parser);
23831 pm_arena_cleanup(&arena);
23832 pm_options_cleanup(&options);
23833}
23834
23843int8_t
23844pm_serialize_parse_errors_format(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data, pm_errors_format_type_t format_type) {
23845 pm_options_t options = { 0 };
23846 pm_options_read(&options, data);
23847
23848 pm_arena_t arena = { 0 };
23849 pm_parser_t parser;
23850 pm_parser_init(&arena, &parser, source, size, &options);
23851
23852 pm_parse(&parser);
23853
23854 int8_t result = -1;
23855 if (parser.error_list.size > 0) {
23856 const char *encoding_name = parser.encoding->name;
23857 pm_buffer_append_string(buffer, encoding_name, strlen(encoding_name));
23858 pm_buffer_append_byte(buffer, '\0');
23859
23860 result = (int8_t) pm_errors_format(&parser, buffer, format_type);
23861 }
23862
23863 pm_parser_cleanup(&parser);
23864 pm_arena_cleanup(&arena);
23865 pm_options_cleanup(&options);
23866
23867 return result;
23868}
23869
23873void
23874pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23875 pm_options_t options = { 0 };
23876 pm_options_read(&options, data);
23877
23878 pm_arena_t arena = { 0 };
23879 pm_parser_t parser;
23880 pm_parser_init(&arena, &parser, source, size, &options);
23881
23882 pm_parse(&parser);
23883 pm_serialize_header(buffer);
23884 pm_serialize_encoding(parser.encoding, buffer);
23885 pm_buffer_append_varsint(buffer, parser.start_line);
23886 pm_buffer_append_uint64(buffer, pm_parser_checksum(&parser));
23887 pm_serialize_line_offset_list(&parser.line_offsets, buffer);
23888 pm_serialize_comment_list(&parser.comment_list, buffer);
23889
23890 pm_parser_cleanup(&parser);
23891 pm_arena_cleanup(&arena);
23892 pm_options_cleanup(&options);
23893}
23894
23895#endif
#define PRISM_ALIGNOF
Get the alignment requirement of a type.
Definition align.h:15
#define PRISM_UNLIKELY(x)
The compiler should predicate that this branch will not be taken.
Definition branch.h:17
pm_comment_type_t
This is the type of a comment that we've found while parsing.
Definition comments.h:22
uint32_t pm_constant_id_t
A constant id is a unique identifier for a constant in the constant pool.
pm_errors_format_type_t
The type of formatting to use when formatting errors.
A header file that defines macros to exclude certain features of the prism library.
#define PRISM_FALLTHROUGH
We use -Wimplicit-fallthrough to guard potentially unintended fall-through between cases of a switch.
Definition fallthrough.h:15
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
int len
Length of the buffer.
Definition io.h:8
#define PRISM_INLINE
Old Visual Studio versions do not support the inline keyword, so we need to define it to be __inline.
Definition inline.h:12
VALUE type(ANYARGS)
ANYARGS-ed function type.
T * cast(pm_node_t *node) noexcept
Cast the given node, which must not be null and must be of the given struct, to that struct.
Definition node.hpp:50
static const uint8_t PM_OPTIONS_COMMAND_LINE_N
A bit representing whether or not the command line -n option was set.
Definition options.h:100
#define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
String literals should not be frozen.
Definition options.h:35
#define PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
String literals should be made frozen.
Definition options.h:46
#define PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET
String literals may be frozen or mutable depending on the implementation default.
Definition options.h:41
static const uint8_t PM_OPTIONS_COMMAND_LINE_P
A bit representing whether or not the command line -p option was set.
Definition options.h:106
PRISM_NODISCARD PRISM_EXPORTED_FUNCTION pm_parser_t * pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) PRISM_NONNULL(1)
Allocate and initialize a parser with the given start and end pointers.
Definition prism.c:23534
PRISM_EXPORTED_FUNCTION void pm_parser_free(pm_parser_t *parser) PRISM_NONNULL(1)
Free both the memory held by the given parser and the parser itself.
Definition prism.c:23555
PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse(pm_parser_t *parser) PRISM_NONNULL(1)
Initiate the parser with the given parser.
Definition prism.c:23726
std::string_view constant(const pm_parser_t *parser PRISM_LIFETIMEBOUND, pm_constant_id_t constant_id) noexcept
The name of the constant with the given id, which must not be zero.
Definition parser.hpp:64
pm_visit_result_t
The value returned by a visitor callback, which determines where the walk goes next.
Definition node.h:49
@ PM_VISIT_SKIP
Continue the walk without visiting this node's children.
Definition node.h:51
@ PM_VISIT_DESCEND
Continue the walk by visiting this node's children.
Definition node.h:54
#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:22
The version of the Prism library.
#define PRISM_VERSION
The version of the Prism library as a constant string.
Definition version.h:33
#define PRISM_VERSION_PATCH
The patch version of the Prism library as an int.
Definition version.h:28
#define PRISM_VERSION_MINOR
The minor version of the Prism library as an int.
Definition version.h:23
#define PRISM_VERSION_MAJOR
The major version of the Prism library as an int.
Definition version.h:18
The functions related to serializing the AST to a binary format.
Functions for parsing streams.
AndNode.
Definition ast.h:1318
ArgumentsNode.
Definition ast.h:1378
pm_node_t base
The embedded base node.
Definition ast.h:1380
struct pm_node_list arguments
ArgumentsNode::arguments.
Definition ast.h:1390
This is a special out parameter to the parse_arguments_list function that includes opening and closin...
Definition prism.c:1750
pm_node_t * block
The optional block attached to the call.
Definition prism.c:1761
bool has_forwarding
The flag indicating whether this arguments list has forwarding argument.
Definition prism.c:1764
pm_location_t opening_loc
The optional location of the opening parenthesis or bracket.
Definition prism.c:1752
pm_arguments_node_t * arguments
The lazily-allocated optional arguments node.
Definition prism.c:1755
pm_location_t closing_loc
The optional location of the closing parenthesis or bracket.
Definition prism.c:1758
ArrayNode.
Definition ast.h:1408
struct pm_node_list elements
ArrayNode::elements.
Definition ast.h:1417
ArrayPatternNode.
Definition ast.h:1468
PM_NODE_ALIGNAS struct pm_node * constant
ArrayPatternNode::constant.
Definition ast.h:1486
pm_location_t opening_loc
ArrayPatternNode::opening_loc.
Definition ast.h:1526
pm_location_t closing_loc
ArrayPatternNode::closing_loc.
Definition ast.h:1536
AssocNode.
Definition ast.h:1551
PM_NODE_ALIGNAS struct pm_node * value
AssocNode::value.
Definition ast.h:1582
PM_NODE_ALIGNAS struct pm_node * key
AssocNode::key.
Definition ast.h:1569
AssocSplatNode.
Definition ast.h:1607
BeginNode.
Definition ast.h:1674
PM_NODE_ALIGNAS struct pm_rescue_node * rescue_clause
BeginNode::rescue_clause.
Definition ast.h:1706
pm_node_t base
The embedded base node.
Definition ast.h:1676
This struct represents a set of binding powers used for a given token.
Definition prism.c:12703
bool binary
Whether or not this token can be used as a binary operator.
Definition prism.c:12711
pm_binding_power_t left
The left binding power.
Definition prism.c:12705
bool nonassoc
Whether or not this token can be used as non-associative binary operator.
Definition prism.c:12717
pm_binding_power_t right
The right binding power.
Definition prism.c:12708
BlockLocalVariableNode.
Definition ast.h:1791
BlockNode.
Definition ast.h:1818
BlockParametersNode.
Definition ast.h:1946
CallNode.
Definition ast.h:2170
pm_location_t opening_loc
CallNode::opening_loc.
Definition ast.h:2231
pm_location_t closing_loc
CallNode::closing_loc.
Definition ast.h:2251
pm_constant_id_t name
CallNode::name.
Definition ast.h:2211
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
CallNode::arguments.
Definition ast.h:2241
pm_location_t equal_loc
CallNode::equal_loc.
Definition ast.h:2264
pm_location_t call_operator_loc
CallNode::call_operator_loc.
Definition ast.h:2201
pm_location_t message_loc
CallNode::message_loc.
Definition ast.h:2221
PM_NODE_ALIGNAS struct pm_node * block
CallNode::block.
Definition ast.h:2274
PM_NODE_ALIGNAS struct pm_node * receiver
CallNode::receiver.
Definition ast.h:2188
CaseMatchNode.
Definition ast.h:2605
struct pm_node_list conditions
CaseMatchNode::conditions.
Definition ast.h:2627
CaseNode.
Definition ast.h:2674
struct pm_node_list conditions
CaseNode::conditions.
Definition ast.h:2696
ClassVariableReadNode.
Definition ast.h:2963
ClassVariableTargetNode.
Definition ast.h:2991
ClassVariableWriteNode.
Definition ast.h:3013
A list of constant IDs.
ConstantPathNode.
Definition ast.h:3222
ConstantPathTargetNode.
Definition ast.h:3357
ConstantReadNode.
Definition ast.h:3450
ConstantTargetNode.
Definition ast.h:3478
ConstantWriteNode.
Definition ast.h:3500
DefNode.
Definition ast.h:3562
DefinedNode.
Definition ast.h:3639
ElseNode.
Definition ast.h:3676
EnsureNode.
Definition ast.h:3771
FindPatternNode.
Definition ast.h:3850
pm_location_t opening_loc
FindPatternNode::opening_loc.
Definition ast.h:3914
PM_NODE_ALIGNAS struct pm_node * constant
FindPatternNode::constant.
Definition ast.h:3862
pm_location_t closing_loc
FindPatternNode::closing_loc.
Definition ast.h:3927
FlipFlopNode.
Definition ast.h:3945
FloatNode.
Definition ast.h:3977
ForNode.
Definition ast.h:4001
ForwardingParameterNode.
Definition ast.h:4110
GlobalVariableReadNode.
Definition ast.h:4283
GlobalVariableTargetNode.
Definition ast.h:4311
GlobalVariableWriteNode.
Definition ast.h:4333
HashNode.
Definition ast.h:4394
struct pm_node_list elements
HashNode::elements.
Definition ast.h:4419
HashPatternNode.
Definition ast.h:4453
PM_NODE_ALIGNAS struct pm_node * constant
HashPatternNode::constant.
Definition ast.h:4468
pm_location_t opening_loc
HashPatternNode::opening_loc.
Definition ast.h:4507
pm_location_t closing_loc
HashPatternNode::closing_loc.
Definition ast.h:4520
IfNode.
Definition ast.h:4541
ImaginaryNode.
Definition ast.h:4646
InNode.
Definition ast.h:4722
InstanceVariableReadNode.
Definition ast.h:5125
InstanceVariableTargetNode.
Definition ast.h:5153
InstanceVariableWriteNode.
Definition ast.h:5175
IntegerNode.
Definition ast.h:5242
InterpolatedMatchLastLineNode.
Definition ast.h:5279
InterpolatedRegularExpressionNode.
Definition ast.h:5324
InterpolatedStringNode.
Definition ast.h:5360
InterpolatedSymbolNode.
Definition ast.h:5392
InterpolatedXStringNode.
Definition ast.h:5424
KeywordHashNode.
Definition ast.h:5493
A Ruby keyword, along with the lex state that it transitions to and the token types that it produces.
Definition prism.c:8506
LambdaNode.
Definition ast.h:5551
int32_t line
The line number.
uint32_t * offsets
The list of offsets.
size_t size
The number of offsets in the list.
LocalVariableReadNode.
Definition ast.h:5729
uint32_t depth
LocalVariableReadNode::depth.
Definition ast.h:5759
pm_constant_id_t name
LocalVariableReadNode::name.
Definition ast.h:5746
LocalVariableTargetNode.
Definition ast.h:5777
LocalVariableWriteNode.
Definition ast.h:5804
This struct represents a slice in the source code, defined by an offset and a length.
Definition ast.h:579
uint32_t start
The offset of the location from the start of the source.
Definition ast.h:581
uint32_t length
The length of the location.
Definition ast.h:584
MatchLastLineNode.
Definition ast.h:5895
MatchWriteNode.
Definition ast.h:6050
struct pm_node_list targets
MatchWriteNode::targets.
Definition ast.h:6062
MultiTargetNode.
Definition ast.h:6129
pm_location_t lparen_loc
MultiTargetNode::lparen_loc.
Definition ast.h:6186
struct pm_node_list lefts
MultiTargetNode::lefts.
Definition ast.h:6146
pm_location_t rparen_loc
MultiTargetNode::rparen_loc.
Definition ast.h:6196
MultiWriteNode.
Definition ast.h:6211
A list of nodes in the source, most often used for lists of children.
Definition ast.h:592
size_t size
The number of nodes in the list.
Definition ast.h:594
struct pm_node ** nodes
The nodes in the list.
Definition ast.h:600
This is the base structure that represents a node in the syntax tree.
Definition ast.h:1090
pm_node_type_t type
This represents the type of the node.
Definition ast.h:1095
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1115
OptionalParameterNode.
Definition ast.h:6505
OrNode.
Definition ast.h:6542
ParametersNode.
Definition ast.h:6596
PM_NODE_ALIGNAS struct pm_node * block
ParametersNode::block.
Definition ast.h:6633
PM_NODE_ALIGNAS struct pm_node * rest
ParametersNode::rest.
Definition ast.h:6613
PM_NODE_ALIGNAS struct pm_node * keyword_rest
ParametersNode::keyword_rest.
Definition ast.h:6628
ParenthesesNode.
Definition ast.h:6651
PostExecutionNode.
Definition ast.h:6777
PreExecutionNode.
Definition ast.h:6814
RangeNode.
Definition ast.h:6881
RationalNode.
Definition ast.h:6938
State for processing named capture groups found during regexp parsing.
Definition prism.c:21706
bool shared
Whether the content being parsed points into the original source.
Definition prism.c:21721
pm_constant_id_set_t names
The set of capture names found so far.
Definition prism.c:21714
pm_call_node_t * call
The call node wrapping the regular expression node (for =~).
Definition prism.c:21708
pm_match_write_node_t * match
The match write node being built, or NULL if no captures found yet.
Definition prism.c:21711
In order to properly set a regular expression's encoding and to validate the byte sequence for the un...
Definition prism.c:9886
pm_buffer_t regexp_buffer
The buffer holding the regexp source.
Definition prism.c:9891
pm_token_buffer_t base
The embedded base buffer.
Definition prism.c:9888
RegularExpressionNode.
Definition ast.h:7003
pm_string_t unescaped
RegularExpressionNode::unescaped.
Definition ast.h:7025
pm_location_t content_loc
RegularExpressionNode::content_loc.
Definition ast.h:7015
RequiredParameterNode.
Definition ast.h:7075
RescueModifierNode.
Definition ast.h:7097
RescueNode.
Definition ast.h:7134
PM_NODE_ALIGNAS struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7171
pm_location_t then_keyword_loc
RescueNode::then_keyword_loc.
Definition ast.h:7161
SplatNode.
Definition ast.h:7424
PM_NODE_ALIGNAS struct pm_node * expression
SplatNode::expression.
Definition ast.h:7436
StatementsNode.
Definition ast.h:7451
struct pm_node_list body
StatementsNode::body.
Definition ast.h:7458
pm_node_t base
The embedded base node.
Definition ast.h:7453
StringNode.
Definition ast.h:7485
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7507
pm_location_t content_loc
StringNode::content_loc.
Definition ast.h:7497
A view of a sequence of bytes.
Definition stringy.h:23
size_t length
The length of the string in bytes of memory.
Definition stringy.h:28
SuperNode.
Definition ast.h:7527
pm_location_t lparen_loc
SuperNode::lparen_loc.
Definition ast.h:7539
PM_NODE_ALIGNAS struct pm_node * block
SuperNode::block.
Definition ast.h:7556
SymbolNode.
Definition ast.h:7579
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7601
When we're lexing certain types (strings, symbols, lists, etc.) we have string content associated wit...
Definition prism.c:9860
pm_buffer_t buffer
The buffer that we're using to keep track of the string content.
Definition prism.c:9865
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:9871
This struct represents a token in the Ruby source.
Definition ast.h:551
const uint8_t * end
A pointer to the end location of the token in the source.
Definition ast.h:559
const uint8_t * start
A pointer to the start location of the token in the source.
Definition ast.h:556
pm_token_type_t type
The type of the token.
Definition ast.h:553
UndefNode.
Definition ast.h:7633
UnlessNode.
Definition ast.h:7663
UntilNode.
Definition ast.h:7753
WhenNode.
Definition ast.h:7797
WhileNode.
Definition ast.h:7840
XStringNode.
Definition ast.h:7886
YieldNode.
Definition ast.h:7923
#define PRISM_UNUSED
GCC will warn if you specify a function or parameter that is unused at runtime.
Definition unused.h:13