Ruby 4.1.0dev (2026-10-17 revision 31a3aa4e46dd33b5a08708d687ed9f9d3253b55a)
prism.c
4
5#include "prism/internal/allocator.h"
6#include "prism/internal/arena.h"
7#include "prism/internal/bit.h"
8#include "prism/internal/buffer.h"
9#include "prism/internal/char.h"
10#include "prism/internal/comments.h"
11#include "prism/internal/constant_pool.h"
12#include "prism/internal/diagnostic.h"
13#include "prism/internal/encoding.h"
14#include "prism/internal/integer.h"
15#include "prism/internal/isinf.h"
16#include "prism/internal/line_offset_list.h"
17#include "prism/internal/list.h"
18#include "prism/internal/magic_comments.h"
19#include "prism/internal/memchr.h"
20#include "prism/internal/node.h"
21#include "prism/internal/options.h"
22#include "prism/internal/parser.h"
23#include "prism/internal/regexp.h"
24#include "prism/internal/serialize.h"
25#include "prism/internal/source.h"
26#include "prism/internal/static_literals.h"
27#include "prism/internal/stringy.h"
28#include "prism/internal/strncasecmp.h"
29#include "prism/internal/strpbrk.h"
30#include "prism/internal/tokens.h"
31
32#include "prism/excludes.h"
33#include "prism/serialize.h"
34#include "prism/stream.h"
35#include "prism/version.h"
36
37#include <assert.h>
38#include <errno.h>
39#include <limits.h>
40#include <locale.h>
41#include <math.h>
42#include <stdio.h>
43#include <stdlib.h>
44
50#ifndef PRISM_DEPTH_MAXIMUM
51 #define PRISM_DEPTH_MAXIMUM 10000
52#endif
53
58#define PM_CONCATENATE(left, right) left ## right
59
65#if defined(_Static_assert)
66# define PM_STATIC_ASSERT(line, condition, message) _Static_assert(condition, message)
67#else
68# define PM_STATIC_ASSERT(line, condition, message) typedef char PM_CONCATENATE(static_assert_, line)[(condition) ? 1 : -1]
69#endif
70
75#if defined(__GNUC__) || defined(__clang__)
77 #define PRISM_LIKELY(x) __builtin_expect(!!(x), 1)
78
80 #define PRISM_UNLIKELY(x) __builtin_expect(!!(x), 0)
81#else
83 #define PRISM_LIKELY(x) (x)
84
86 #define PRISM_UNLIKELY(x) (x)
87#endif
88
92const char *
93pm_version(void) {
94 return PRISM_VERSION;
95}
96
101#define PM_TAB_WHITESPACE_SIZE 8
102
103// Macros for min/max.
104#define MIN(a,b) (((a)<(b))?(a):(b))
105#define MAX(a,b) (((a)>(b))?(a):(b))
106
107/******************************************************************************/
108/* Helpful AST-related macros */
109/******************************************************************************/
110
111#define U32(value_) ((uint32_t) (value_))
112
113#define FL PM_NODE_FLAGS
114#define UP PM_NODE_UPCAST
115
116#define PM_TOKEN_START(parser_, token_) U32((token_)->start - (parser_)->start)
117#define PM_TOKEN_END(parser_, token_) U32((token_)->end - (parser_)->start)
118#define PM_TOKEN_LENGTH(token_) U32((token_)->end - (token_)->start)
119#define PM_TOKENS_LENGTH(left_, right_) U32((right_)->end - (left_)->start)
120
121#define PM_NODE_START(node_) (UP(node_)->location.start)
122#define PM_NODE_LENGTH(node_) (UP(node_)->location.length)
123#define PM_NODE_END(node_) (UP(node_)->location.start + UP(node_)->location.length)
124#define PM_NODES_LENGTH(left_, right_) (PM_NODE_END(right_) - PM_NODE_START(left_))
125
126#define PM_TOKEN_NODE_LENGTH(parser_, token_, node_) (PM_NODE_END(node_) - PM_TOKEN_START(parser_, token_))
127#define PM_NODE_TOKEN_LENGTH(parser_, node_, token_) (PM_TOKEN_END(parser_, token_) - PM_NODE_START(node_))
128
129#define PM_NODE_START_SET_NODE(left_, right_) (PM_NODE_START(left_) = PM_NODE_START(right_))
130#define PM_NODE_START_SET_TOKEN(parser_, node_, token_) (PM_NODE_START(node_) = PM_TOKEN_START(parser_, token_))
131#define PM_NODE_LENGTH_SET_NODE(left_, right_) (PM_NODE_LENGTH(left_) = PM_NODE_END(right_) - PM_NODE_START(left_))
132#define PM_NODE_LENGTH_SET_TOKEN(parser_, node_, token_) (PM_NODE_LENGTH(node_) = PM_TOKEN_END(parser_, token_) - PM_NODE_START(node_))
133#define PM_NODE_LENGTH_SET_LOCATION(node_, location_) (PM_NODE_LENGTH(node_) = PM_LOCATION_END(location_) - PM_NODE_START(node_))
134
143pm_location_init(uint32_t start, uint32_t length) {
144 pm_location_t location = { .start = start, .length = length };
145 return location;
146}
147
148#define PM_LOCATION_INIT(start_, length_) pm_location_init((start_), (length_))
149#define PM_LOCATION_INIT_UNSET PM_LOCATION_INIT(0, 0)
150#define PM_LOCATION_INIT_TOKEN(parser_, token_) PM_LOCATION_INIT(PM_TOKEN_START(parser_, token_), PM_TOKEN_LENGTH(token_))
151#define PM_LOCATION_INIT_NODE(node_) UP(node_)->location
152
153#define PM_LOCATION_INIT_TOKENS(parser_, left_, right_) PM_LOCATION_INIT(PM_TOKEN_START(parser_, left_), PM_TOKENS_LENGTH(left_, right_))
154#define PM_LOCATION_INIT_NODES(left_, right_) PM_LOCATION_INIT(PM_NODE_START(left_), PM_NODES_LENGTH(left_, right_))
155#define PM_LOCATION_INIT_TOKEN_NODE(parser_, token_, node_) PM_LOCATION_INIT(PM_TOKEN_START(parser_, token_), PM_TOKEN_NODE_LENGTH(parser_, token_, node_))
156#define PM_LOCATION_INIT_NODE_TOKEN(parser_, node_, token_) PM_LOCATION_INIT(PM_NODE_START(node_), PM_NODE_TOKEN_LENGTH(parser_, node_, token_))
157
158#define TOK2LOC(parser_, token_) PM_LOCATION_INIT_TOKEN(parser_, token_)
159#define NTOK2LOC(parser_, token_) ((token_) == NULL ? PM_LOCATION_INIT_UNSET : TOK2LOC(parser_, token_))
160#define NTOK2PTR(token_) ((token_).start == NULL ? NULL : &(token_))
161
162/******************************************************************************/
163/* Lex mode manipulations */
164/******************************************************************************/
165
170static PRISM_INLINE uint8_t
171lex_mode_incrementor(const uint8_t start) {
172 switch (start) {
173 case '(':
174 case '[':
175 case '{':
176 case '<':
177 return start;
178 default:
179 return '\0';
180 }
181}
182
187static PRISM_INLINE uint8_t
188lex_mode_terminator(const uint8_t start) {
189 switch (start) {
190 case '(':
191 return ')';
192 case '[':
193 return ']';
194 case '{':
195 return '}';
196 case '<':
197 return '>';
198 default:
199 return start;
200 }
201}
202
208static void
209lex_mode_push(pm_parser_t *parser, pm_lex_mode_t lex_mode) {
210 lex_mode.prev = parser->lex_modes.current;
211 parser->lex_modes.index++;
212
213 if (parser->lex_modes.index > PM_LEX_STACK_SIZE - 1) {
214 parser->lex_modes.current = (pm_lex_mode_t *) xmalloc(sizeof(pm_lex_mode_t));
215 if (PRISM_UNLIKELY(parser->lex_modes.current == NULL)) {
216 fprintf(stderr, "prism: out of memory; aborting\n");
217 abort();
218 }
219
220 *parser->lex_modes.current = lex_mode;
221 } else {
222 parser->lex_modes.stack[parser->lex_modes.index] = lex_mode;
223 parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index];
224 }
225}
226
230static PRISM_INLINE void
231lex_mode_push_list(pm_parser_t *parser, bool interpolation, uint8_t delimiter) {
232 uint8_t incrementor = lex_mode_incrementor(delimiter);
233 uint8_t terminator = lex_mode_terminator(delimiter);
234
235 pm_lex_mode_t lex_mode = {
236 .mode = PM_LEX_LIST,
237 .as.list = {
238 .nesting = 0,
239 .interpolation = interpolation,
240 .started = false,
241 .separated = false,
242 .incrementor = incrementor,
243 .terminator = terminator
244 }
245 };
246
247 // These are the places where we need to split up the content of the list.
248 // We'll use strpbrk to find the first of these characters.
249 uint8_t *breakpoints = lex_mode.as.list.breakpoints;
250 memset(breakpoints, 0, PM_STRPBRK_CACHE_SIZE);
251 memcpy(breakpoints, "\\ \t\f\r\v\n", sizeof("\\ \t\f\r\v\n") - 1);
252 size_t index = 7;
253
254 // Now we'll add the terminator to the list of breakpoints. If the
255 // terminator is not already a NULL byte, add it to the list.
256 if (terminator != '\0') {
257 breakpoints[index++] = terminator;
258 }
259
260 // If interpolation is allowed, then we're going to check for the #
261 // character. Otherwise we'll only look for escapes and the terminator.
262 if (interpolation) {
263 breakpoints[index++] = '#';
264 }
265
266 // If there is an incrementor, then we'll check for that as well.
267 if (incrementor != '\0') {
268 breakpoints[index++] = incrementor;
269 }
270
271 parser->explicit_encoding = NULL;
272 lex_mode_push(parser, lex_mode);
273}
274
280static PRISM_INLINE void
281lex_mode_push_list_eof(pm_parser_t *parser) {
282 lex_mode_push_list(parser, false, '\0');
283}
284
288static PRISM_INLINE void
289lex_mode_push_regexp(pm_parser_t *parser, uint8_t incrementor, uint8_t terminator) {
290 pm_lex_mode_t lex_mode = {
291 .mode = PM_LEX_REGEXP,
292 .as.regexp = {
293 .nesting = 0,
294 .incrementor = incrementor,
295 .terminator = terminator
296 }
297 };
298
299 // These are the places where we need to split up the content of the
300 // regular expression. We'll use strpbrk to find the first of these
301 // characters.
302 uint8_t *breakpoints = lex_mode.as.regexp.breakpoints;
303 memset(breakpoints, 0, PM_STRPBRK_CACHE_SIZE);
304 memcpy(breakpoints, "\r\n\\#", sizeof("\r\n\\#") - 1);
305 size_t index = 4;
306
307 // First we'll add the terminator.
308 if (terminator != '\0') {
309 breakpoints[index++] = terminator;
310 }
311
312 // Next, if there is an incrementor, then we'll check for that as well.
313 if (incrementor != '\0') {
314 breakpoints[index++] = incrementor;
315 }
316
317 parser->explicit_encoding = NULL;
318 lex_mode_push(parser, lex_mode);
319}
320
324static PRISM_INLINE void
325lex_mode_push_string(pm_parser_t *parser, bool interpolation, bool label_allowed, uint8_t incrementor, uint8_t terminator) {
326 pm_lex_mode_t lex_mode = {
327 .mode = PM_LEX_STRING,
328 .as.string = {
329 .nesting = 0,
330 .interpolation = interpolation,
331 .label_allowed = label_allowed,
332 .incrementor = incrementor,
333 .terminator = terminator
334 }
335 };
336
337 // These are the places where we need to split up the content of the
338 // string. We'll use strpbrk to find the first of these characters.
339 uint8_t *breakpoints = lex_mode.as.string.breakpoints;
340 memset(breakpoints, 0, PM_STRPBRK_CACHE_SIZE);
341 memcpy(breakpoints, "\r\n\\", sizeof("\r\n\\") - 1);
342 size_t index = 3;
343
344 // Now add in the terminator. If the terminator is not already a NULL byte,
345 // then we'll add it.
346 if (terminator != '\0') {
347 breakpoints[index++] = terminator;
348 }
349
350 // If interpolation is allowed, then we're going to check for the #
351 // character. Otherwise we'll only look for escapes and the terminator.
352 if (interpolation) {
353 breakpoints[index++] = '#';
354 }
355
356 // If we have an incrementor, then we'll add that in as a breakpoint as
357 // well.
358 if (incrementor != '\0') {
359 breakpoints[index++] = incrementor;
360 }
361
362 parser->explicit_encoding = NULL;
363 lex_mode_push(parser, lex_mode);
364}
365
371static PRISM_INLINE void
372lex_mode_push_string_eof(pm_parser_t *parser) {
373 lex_mode_push_string(parser, false, false, '\0', '\0');
374}
375
381static void
382lex_mode_pop(pm_parser_t *parser) {
383 if (parser->lex_modes.index == 0) {
384 parser->lex_modes.current->mode = PM_LEX_DEFAULT;
385 } else if (parser->lex_modes.index < PM_LEX_STACK_SIZE) {
386 parser->lex_modes.index--;
387 parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index];
388 } else {
389 parser->lex_modes.index--;
390 pm_lex_mode_t *prev = parser->lex_modes.current->prev;
391 xfree_sized(parser->lex_modes.current, sizeof(pm_lex_mode_t));
392 parser->lex_modes.current = prev;
393 }
394}
395
399static PRISM_INLINE bool
400lex_state_p(const pm_parser_t *parser, pm_lex_state_t state) {
401 return parser->lex_state & state;
402}
403
404typedef enum {
405 PM_IGNORED_NEWLINE_NONE = 0,
406 PM_IGNORED_NEWLINE_ALL,
407 PM_IGNORED_NEWLINE_PATTERN
408} pm_ignored_newline_type_t;
409
410static PRISM_INLINE pm_ignored_newline_type_t
411lex_state_ignored_p(pm_parser_t *parser) {
412 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);
413
414 if (ignored) {
415 return PM_IGNORED_NEWLINE_ALL;
416 } else if ((parser->lex_state & ~((unsigned int) PM_LEX_STATE_LABEL)) == (PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED)) {
417 return PM_IGNORED_NEWLINE_PATTERN;
418 } else {
419 return PM_IGNORED_NEWLINE_NONE;
420 }
421}
422
423static PRISM_INLINE bool
424lex_state_beg_p(pm_parser_t *parser) {
425 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));
426}
427
428static PRISM_INLINE bool
429lex_state_arg_p(pm_parser_t *parser) {
430 return lex_state_p(parser, PM_LEX_STATE_ARG_ANY);
431}
432
433static PRISM_INLINE bool
434lex_state_spcarg_p(pm_parser_t *parser, bool space_seen) {
435 if (parser->current.end >= parser->end) {
436 return false;
437 }
438 return lex_state_arg_p(parser) && space_seen && !pm_char_is_whitespace(*parser->current.end);
439}
440
441static PRISM_INLINE bool
442lex_state_end_p(pm_parser_t *parser) {
443 return lex_state_p(parser, PM_LEX_STATE_END_ANY);
444}
445
449static PRISM_INLINE bool
450lex_state_operator_p(pm_parser_t *parser) {
451 return lex_state_p(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_DOT);
452}
453
458static PRISM_INLINE void
459lex_state_set(pm_parser_t *parser, pm_lex_state_t state) {
460 parser->lex_state = state;
461}
462
463#ifndef PM_DEBUG_LOGGING
468#define PM_DEBUG_LOGGING 0
469#endif
470
471#if PM_DEBUG_LOGGING
472PRISM_UNUSED static void
473debug_state(pm_parser_t *parser) {
474 fprintf(stderr, "STATE: ");
475 bool first = true;
476
477 if (parser->lex_state == PM_LEX_STATE_NONE) {
478 fprintf(stderr, "NONE\n");
479 return;
480 }
481
482#define CHECK_STATE(state) \
483 if (parser->lex_state & state) { \
484 if (!first) fprintf(stderr, "|"); \
485 fprintf(stderr, "%s", #state); \
486 first = false; \
487 }
488
489 CHECK_STATE(PM_LEX_STATE_BEG)
490 CHECK_STATE(PM_LEX_STATE_END)
491 CHECK_STATE(PM_LEX_STATE_ENDARG)
492 CHECK_STATE(PM_LEX_STATE_ENDFN)
493 CHECK_STATE(PM_LEX_STATE_ARG)
494 CHECK_STATE(PM_LEX_STATE_CMDARG)
495 CHECK_STATE(PM_LEX_STATE_MID)
496 CHECK_STATE(PM_LEX_STATE_FNAME)
497 CHECK_STATE(PM_LEX_STATE_DOT)
498 CHECK_STATE(PM_LEX_STATE_CLASS)
499 CHECK_STATE(PM_LEX_STATE_LABEL)
500 CHECK_STATE(PM_LEX_STATE_LABELED)
501 CHECK_STATE(PM_LEX_STATE_FITEM)
502
503#undef CHECK_STATE
504
505 fprintf(stderr, "\n");
506}
507
508static void
509debug_lex_state_set(pm_parser_t *parser, pm_lex_state_t state, char const * caller_name, int line_number) {
510 fprintf(stderr, "Caller: %s:%d\nPrevious: ", caller_name, line_number);
511 debug_state(parser);
512 lex_state_set(parser, state);
513 fprintf(stderr, "Now: ");
514 debug_state(parser);
515 fprintf(stderr, "\n");
516}
517
518#define lex_state_set(parser, state) debug_lex_state_set(parser, state, __func__, __LINE__)
519#endif
520
521/******************************************************************************/
522/* Command-line macro helpers */
523/******************************************************************************/
524
526#define PM_PARSER_COMMAND_LINE_OPTION(parser, option) ((parser)->command_line & (option))
527
529#define PM_PARSER_COMMAND_LINE_OPTION_A(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_A)
530
532#define PM_PARSER_COMMAND_LINE_OPTION_E(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_E)
533
535#define PM_PARSER_COMMAND_LINE_OPTION_L(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_L)
536
538#define PM_PARSER_COMMAND_LINE_OPTION_N(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_N)
539
541#define PM_PARSER_COMMAND_LINE_OPTION_P(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_P)
542
544#define PM_PARSER_COMMAND_LINE_OPTION_X(parser) PM_PARSER_COMMAND_LINE_OPTION(parser, PM_OPTIONS_COMMAND_LINE_X)
545
546/******************************************************************************/
547/* Diagnostic-related functions */
548/******************************************************************************/
549
553static PRISM_INLINE void
554pm_parser_err(pm_parser_t *parser, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id) {
555 pm_diagnostic_list_append(&parser->metadata_arena, &parser->error_list, start, length, diag_id);
556}
557
562static PRISM_INLINE void
563pm_parser_err_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) {
564 pm_parser_err(parser, PM_TOKEN_START(parser, token), PM_TOKEN_LENGTH(token), diag_id);
565}
566
571static PRISM_INLINE void
572pm_parser_err_current(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
573 pm_parser_err_token(parser, &parser->current, diag_id);
574}
575
580static PRISM_INLINE void
581pm_parser_err_previous(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
582 pm_parser_err_token(parser, &parser->previous, diag_id);
583}
584
589static PRISM_INLINE void
590pm_parser_err_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) {
591 pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), diag_id);
592}
593
597#define PM_PARSER_ERR_FORMAT(parser_, start_, length_, diag_id_, ...) \
598 pm_diagnostic_list_append_format(&(parser_)->metadata_arena, &(parser_)->error_list, start_, length_, diag_id_, __VA_ARGS__)
599
604#define PM_PARSER_ERR_NODE_FORMAT(parser_, node_, diag_id_, ...) \
605 PM_PARSER_ERR_FORMAT(parser_, PM_NODE_START(node_), PM_NODE_LENGTH(node_), diag_id_, __VA_ARGS__)
606
611#define PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser_, node_, diag_id_) \
612 PM_PARSER_ERR_NODE_FORMAT(parser_, node_, diag_id_, (int) PM_NODE_LENGTH(node_), (const char *) (parser_->start + PM_NODE_START(node_)))
613
618#define PM_PARSER_ERR_TOKEN_FORMAT(parser_, token_, diag_id, ...) \
619 PM_PARSER_ERR_FORMAT(parser_, PM_TOKEN_START(parser_, token_), PM_TOKEN_LENGTH(token_), diag_id, __VA_ARGS__)
620
625#define PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser_, token_, diag_id_) \
626 PM_PARSER_ERR_TOKEN_FORMAT(parser_, token_, diag_id_, (int) PM_TOKEN_LENGTH(token_), (const char *) (token_)->start)
627
631static PRISM_INLINE void
632pm_parser_warn(pm_parser_t *parser, uint32_t start, uint32_t length, pm_diagnostic_id_t diag_id) {
633 pm_diagnostic_list_append(&parser->metadata_arena, &parser->warning_list, start, length, diag_id);
634}
635
640static PRISM_INLINE void
641pm_parser_warn_token(pm_parser_t *parser, const pm_token_t *token, pm_diagnostic_id_t diag_id) {
642 pm_parser_warn(parser, PM_TOKEN_START(parser, token), PM_TOKEN_LENGTH(token), diag_id);
643}
644
649static PRISM_INLINE void
650pm_parser_warn_node(pm_parser_t *parser, const pm_node_t *node, pm_diagnostic_id_t diag_id) {
651 pm_parser_warn(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), diag_id);
652}
653
658#define PM_PARSER_WARN_FORMAT(parser_, start_, length_, diag_id_, ...) \
659 pm_diagnostic_list_append_format(&(parser_)->metadata_arena, &(parser_)->warning_list, start_, length_, diag_id_, __VA_ARGS__)
660
665#define PM_PARSER_WARN_TOKEN_FORMAT(parser_, token_, diag_id_, ...) \
666 PM_PARSER_WARN_FORMAT(parser_, PM_TOKEN_START(parser_, token_), PM_TOKEN_LENGTH(token_), diag_id_, __VA_ARGS__)
667
672#define PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser_, token_, diag_id_) \
673 PM_PARSER_WARN_TOKEN_FORMAT(parser_, token_, diag_id_, (int) PM_TOKEN_LENGTH(token_), (const char *) (token_)->start)
674
679#define PM_PARSER_WARN_NODE_FORMAT(parser_, node_, diag_id_, ...) \
680 PM_PARSER_WARN_FORMAT(parser_, PM_NODE_START(node_), PM_NODE_LENGTH(node_), diag_id_, __VA_ARGS__)
681
687static void
688pm_parser_err_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
689 PM_PARSER_ERR_FORMAT(
690 parser,
691 U32(ident_start - parser->start),
692 U32(ident_length),
693 PM_ERR_HEREDOC_TERM,
694 (int) ident_length,
695 (const char *) ident_start
696 );
697}
698
699/******************************************************************************/
700/* Scope-related functions */
701/******************************************************************************/
702
706#define PM_LOCALS_REUSE_CAPACITY 16
707
711static void
712pm_parser_scope_push(pm_parser_t *parser, bool closed) {
713 pm_scope_t *scope = parser->free_scopes;
714 pm_locals_t locals = { 0 };
715
716 if (scope != NULL) {
717 parser->free_scopes = scope->previous;
718
719 /* Reuse the locals table of the popped scope when it is small, so that
720 * most scopes allocate no locals storage at all. Large tables are
721 * dropped instead, because every scope that reuses a table pays to
722 * clear it and to scan its full capacity when the locals are ordered. */
723 if (scope->locals.capacity > 0 && scope->locals.capacity <= PM_LOCALS_REUSE_CAPACITY) {
724 locals.capacity = scope->locals.capacity;
725 locals.locals = scope->locals.locals;
726 memset(locals.locals, 0, locals.capacity * sizeof(pm_local_t));
727 }
728 } else {
729 scope = (pm_scope_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_scope_t), PRISM_ALIGNOF(pm_scope_t));
730 }
731
732 *scope = (pm_scope_t) {
733 .previous = parser->current_scope,
734 .locals = locals,
735 .parameters = PM_SCOPE_PARAMETERS_NONE,
736 .implicit_parameters = { 0 },
737 .shareable_constant = parser->current_scope == NULL ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant,
738 .closed = closed
739 };
740
741 parser->current_scope = scope;
742}
743
748static bool
749pm_parser_scope_toplevel_p(pm_parser_t *parser) {
750 pm_scope_t *scope = parser->current_scope;
751
752 do {
753 if (scope->previous == NULL) return true;
754 if (scope->closed) return false;
755 } while ((scope = scope->previous) != NULL);
756
757 assert(false && "unreachable");
758 return true;
759}
760
764static pm_scope_t *
765pm_parser_scope_find(pm_parser_t *parser, uint32_t depth) {
766 pm_scope_t *scope = parser->current_scope;
767
768 while (depth-- > 0) {
769 assert(scope != NULL);
770 scope = scope->previous;
771 }
772
773 return scope;
774}
775
776typedef enum {
777 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS,
778 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT,
779 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL
780} pm_scope_forwarding_param_check_result_t;
781
782static pm_scope_forwarding_param_check_result_t
783pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const uint8_t mask) {
784 pm_scope_t *scope = parser->current_scope;
785 bool conflict = false;
786
787 while (scope != NULL) {
788 if (scope->parameters & mask) {
789 if (scope->closed) {
790 if (conflict) {
791 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT;
792 } else {
793 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS;
794 }
795 }
796
797 conflict = true;
798 }
799
800 if (scope->closed) break;
801 scope = scope->previous;
802 }
803
804 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL;
805}
806
807static void
808pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) {
809 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK)) {
810 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
811 // Pass.
812 break;
813 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
814 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_AMPERSAND);
815 break;
816 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
817 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND);
818 break;
819 }
820}
821
822static void
823pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) {
824 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS)) {
825 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
826 // Pass.
827 break;
828 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
829 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR);
830 break;
831 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
832 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR);
833 break;
834 }
835}
836
837static void
838pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t *token) {
839 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_ALL)) {
840 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
841 // Pass.
842 break;
843 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
844 // This shouldn't happen, because ... is not allowed in the
845 // declaration of blocks. If we get here, we assume we already have
846 // an error for this.
847 break;
848 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
849 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES);
850 break;
851 }
852}
853
854static void
855pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) {
856 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS)) {
857 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
858 // Pass.
859 break;
860 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
861 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR_STAR);
862 break;
863 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
864 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR);
865 break;
866 }
867}
868
872static PRISM_INLINE pm_shareable_constant_value_t
873pm_parser_scope_shareable_constant_get(pm_parser_t *parser) {
874 return parser->current_scope->shareable_constant;
875}
876
881static void
882pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constant_value_t shareable_constant) {
883 pm_scope_t *scope = parser->current_scope;
884
885 do {
886 scope->shareable_constant = shareable_constant;
887 } while (!scope->closed && (scope = scope->previous) != NULL);
888}
889
890/******************************************************************************/
891/* Local variable-related functions */
892/******************************************************************************/
893
897#define PM_LOCALS_HASH_THRESHOLD 5
898
903static void
904pm_locals_resize(pm_arena_t *arena, pm_locals_t *locals) {
905 uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2);
906 assert(next_capacity > locals->capacity);
907
908 pm_local_t *next_locals = (pm_local_t *) pm_arena_zalloc(arena, next_capacity * sizeof(pm_local_t), PRISM_ALIGNOF(pm_local_t));
909
910 if (next_capacity < PM_LOCALS_HASH_THRESHOLD) {
911 if (locals->size > 0) {
912 memcpy(next_locals, locals->locals, locals->size * sizeof(pm_local_t));
913 }
914 } else {
915 // If we just switched from a list to a hash, then we need to fill in
916 // the hash values of all of the locals.
917 bool hash_needed = (locals->capacity <= PM_LOCALS_HASH_THRESHOLD);
918 uint32_t mask = next_capacity - 1;
919
920 for (uint32_t index = 0; index < locals->capacity; index++) {
921 pm_local_t *local = &locals->locals[index];
922
923 if (local->name != PM_CONSTANT_ID_UNSET) {
924 if (hash_needed) local->hash = pm_constant_id_hash(local->name);
925
926 uint32_t hash = local->hash;
927 while (next_locals[hash & mask].name != PM_CONSTANT_ID_UNSET) hash++;
928 next_locals[hash & mask] = *local;
929 }
930 }
931 }
932
933 locals->locals = next_locals;
934 locals->capacity = next_capacity;
935}
936
952static bool
953pm_locals_write(pm_arena_t *arena, pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint32_t length, uint32_t reads) {
954 if (locals->size >= (locals->capacity / 4 * 3)) {
955 pm_locals_resize(arena, locals);
956 }
957
958 locals->bloom |= (1u << (name & 31));
959
960 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
961 for (uint32_t index = 0; index < locals->capacity; index++) {
962 pm_local_t *local = &locals->locals[index];
963
964 if (local->name == PM_CONSTANT_ID_UNSET) {
965 *local = (pm_local_t) {
966 .name = name,
967 .location = { .start = start, .length = length },
968 .index = locals->size++,
969 .reads = reads,
970 .hash = 0
971 };
972 return true;
973 } else if (local->name == name) {
974 return false;
975 }
976 }
977 } else {
978 uint32_t mask = locals->capacity - 1;
979 uint32_t hash = pm_constant_id_hash(name);
980 uint32_t initial_hash = hash;
981
982 do {
983 pm_local_t *local = &locals->locals[hash & mask];
984
985 if (local->name == PM_CONSTANT_ID_UNSET) {
986 *local = (pm_local_t) {
987 .name = name,
988 .location = { .start = start, .length = length },
989 .index = locals->size++,
990 .reads = reads,
991 .hash = initial_hash
992 };
993 return true;
994 } else if (local->name == name) {
995 return false;
996 } else {
997 hash++;
998 }
999 } while ((hash & mask) != initial_hash);
1000 }
1001
1002 assert(false && "unreachable");
1003 return true;
1004}
1005
1010static uint32_t
1011pm_locals_find(pm_locals_t *locals, pm_constant_id_t name) {
1012 if (!(locals->bloom & (1u << (name & 31)))) return UINT32_MAX;
1013
1014 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
1015 for (uint32_t index = 0; index < locals->size; index++) {
1016 pm_local_t *local = &locals->locals[index];
1017 if (local->name == name) return index;
1018 }
1019 } else {
1020 uint32_t mask = locals->capacity - 1;
1021 uint32_t hash = pm_constant_id_hash(name);
1022 uint32_t initial_hash = hash & mask;
1023
1024 do {
1025 pm_local_t *local = &locals->locals[hash & mask];
1026
1027 if (local->name == PM_CONSTANT_ID_UNSET) {
1028 return UINT32_MAX;
1029 } else if (local->name == name) {
1030 return hash & mask;
1031 } else {
1032 hash++;
1033 }
1034 } while ((hash & mask) != initial_hash);
1035 }
1036
1037 return UINT32_MAX;
1038}
1039
1044static void
1045pm_locals_read(pm_locals_t *locals, pm_constant_id_t name) {
1046 uint32_t index = pm_locals_find(locals, name);
1047 assert(index != UINT32_MAX);
1048
1049 pm_local_t *local = &locals->locals[index];
1050 assert(local->reads < UINT32_MAX);
1051
1052 local->reads++;
1053}
1054
1059static void
1060pm_locals_unread(pm_locals_t *locals, pm_constant_id_t name) {
1061 uint32_t index = pm_locals_find(locals, name);
1062 assert(index != UINT32_MAX);
1063
1064 pm_local_t *local = &locals->locals[index];
1065 assert(local->reads > 0);
1066
1067 local->reads--;
1068}
1069
1073static uint32_t
1074pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) {
1075 uint32_t index = pm_locals_find(locals, name);
1076 assert(index != UINT32_MAX);
1077
1078 return locals->locals[index].reads;
1079}
1080
1089static void
1090pm_locals_order(pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) {
1091 pm_constant_id_list_init_capacity(parser->arena, list, locals->size);
1092
1093 // If we're still below the threshold for switching to a hash, then we only
1094 // need to loop over the locals until we hit the size because the locals are
1095 // stored in a list.
1096 uint32_t capacity = locals->capacity < PM_LOCALS_HASH_THRESHOLD ? locals->size : locals->capacity;
1097
1098 // We will only warn for unused variables if we're not at the top level, or
1099 // if we're parsing a file outside of eval or -e.
1100 bool warn_unused = !toplevel || (!parser->parsing_eval && !PM_PARSER_COMMAND_LINE_OPTION_E(parser));
1101
1102 for (uint32_t index = 0; index < capacity; index++) {
1103 pm_local_t *local = &locals->locals[index];
1104
1105 if (local->name != PM_CONSTANT_ID_UNSET) {
1106 pm_constant_id_list_insert(list, (size_t) local->index, local->name);
1107
1108 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))) {
1109 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name);
1110
1111 if (constant->length >= 1 && *constant->start != '_') {
1112 PM_PARSER_WARN_FORMAT(
1113 parser,
1114 local->location.start,
1115 local->location.length,
1116 PM_WARN_UNUSED_LOCAL_VARIABLE,
1117 (int) constant->length,
1118 (const char *) constant->start
1119 );
1120 }
1121 }
1122 }
1123 }
1124}
1125
1126/******************************************************************************/
1127/* Node-related functions */
1128/******************************************************************************/
1129
1134pm_parser_constant_id_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
1135 /* Fast path: if this is the same token as the last lookup (same pointer
1136 * range), return the cached result. */
1137 if (start == parser->constant_cache.start && end == parser->constant_cache.end) {
1138 return parser->constant_cache.id;
1139 }
1140
1141 pm_constant_id_t id = pm_constant_pool_insert_shared(&parser->metadata_arena, &parser->constant_pool, start, (size_t) (end - start));
1142
1143 parser->constant_cache.start = start;
1144 parser->constant_cache.end = end;
1145 parser->constant_cache.id = id;
1146
1147 return id;
1148}
1149
1154pm_parser_constant_id_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
1155 return pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, start, length);
1156}
1157
1162pm_parser_constant_id_constant(pm_parser_t *parser, const char *start, size_t length) {
1163 return pm_constant_pool_insert_constant(&parser->metadata_arena, &parser->constant_pool, (const uint8_t *) start, length);
1164}
1165
1170pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
1171 return pm_parser_constant_id_raw(parser, token->start, token->end);
1172}
1173
1178#define PM_CASE_VOID_VALUE PM_RETURN_NODE: case PM_BREAK_NODE: case PM_NEXT_NODE: \
1179 case PM_REDO_NODE: case PM_RETRY_NODE: case PM_MATCH_REQUIRED_NODE
1180
1186static pm_node_t *
1187pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
1188 pm_node_t *void_node = NULL;
1189
1190 while (node != NULL) {
1191 switch (PM_NODE_TYPE(node)) {
1192 case PM_CASE_VOID_VALUE:
1193 return void_node != NULL ? void_node : node;
1194 case PM_MATCH_PREDICATE_NODE:
1195 return NULL;
1196 case PM_BEGIN_NODE: {
1197 pm_begin_node_t *cast = (pm_begin_node_t *) node;
1198
1199 if (cast->ensure_clause != NULL) {
1200 if (cast->rescue_clause != NULL) {
1201 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->rescue_clause));
1202 if (vn != NULL) return vn;
1203 }
1204
1205 if (cast->statements != NULL) {
1206 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1207 if (vn != NULL) return vn;
1208 }
1209
1210 node = UP(cast->ensure_clause);
1211 } else if (cast->rescue_clause != NULL) {
1212 // https://bugs.ruby-lang.org/issues/21669
1213 if (cast->else_clause == NULL || parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1214 if (cast->statements == NULL) return NULL;
1215
1216 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1217 if (vn == NULL) return NULL;
1218 if (void_node == NULL) void_node = vn;
1219 }
1220
1221 for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) {
1222 pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements));
1223
1224 if (vn == NULL) {
1225 // https://bugs.ruby-lang.org/issues/21669
1226 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1227 return NULL;
1228 }
1229 void_node = NULL;
1230 break;
1231 }
1232 }
1233
1234 if (cast->else_clause != NULL) {
1235 node = UP(cast->else_clause);
1236
1237 // https://bugs.ruby-lang.org/issues/21669
1238 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1239 pm_node_t *vn = pm_check_value_expression(parser, node);
1240 if (vn != NULL) return vn;
1241 }
1242 } else {
1243 return void_node;
1244 }
1245 } else {
1246 node = UP(cast->statements);
1247 }
1248
1249 break;
1250 }
1251 case PM_CASE_NODE: {
1252 // https://bugs.ruby-lang.org/issues/21669
1253 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1254 return NULL;
1255 }
1256
1257 pm_case_node_t *cast = (pm_case_node_t *) node;
1258 if (cast->else_clause == NULL) return NULL;
1259
1260 pm_node_t *condition;
1261 PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1262 assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
1263
1264 pm_when_node_t *cast = (pm_when_node_t *) condition;
1265 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1266 if (vn == NULL) return NULL;
1267 if (void_node == NULL) void_node = vn;
1268 }
1269
1270 node = UP(cast->else_clause);
1271 break;
1272 }
1273 case PM_CASE_MATCH_NODE: {
1274 // https://bugs.ruby-lang.org/issues/21669
1275 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1276 return NULL;
1277 }
1278
1280 if (cast->else_clause == NULL) return NULL;
1281
1282 pm_node_t *condition;
1283 PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1284 assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
1285
1286 pm_in_node_t *cast = (pm_in_node_t *) condition;
1287 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1288 if (vn == NULL) return NULL;
1289 if (void_node == NULL) void_node = vn;
1290 }
1291
1292 node = UP(cast->else_clause);
1293 break;
1294 }
1295 case PM_ENSURE_NODE: {
1296 pm_ensure_node_t *cast = (pm_ensure_node_t *) node;
1297 node = UP(cast->statements);
1298 break;
1299 }
1300 case PM_PARENTHESES_NODE: {
1302 node = UP(cast->body);
1303 break;
1304 }
1305 case PM_STATEMENTS_NODE: {
1307
1308 // https://bugs.ruby-lang.org/issues/21669
1309 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1310 pm_node_t *body_part;
1311 PM_NODE_LIST_FOREACH(&cast->body, index, body_part) {
1312 switch (PM_NODE_TYPE(body_part)) {
1313 case PM_CASE_VOID_VALUE:
1314 if (void_node == NULL) {
1315 void_node = body_part;
1316 }
1317 return void_node;
1318 default: break;
1319 }
1320 }
1321 }
1322
1323 node = cast->body.nodes[cast->body.size - 1];
1324 break;
1325 }
1326 case PM_IF_NODE: {
1327 pm_if_node_t *cast = (pm_if_node_t *) node;
1328 if (cast->statements == NULL || cast->subsequent == NULL) {
1329 return NULL;
1330 }
1331 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1332 if (vn == NULL) {
1333 return NULL;
1334 }
1335 if (void_node == NULL) {
1336 void_node = vn;
1337 }
1338 node = cast->subsequent;
1339 break;
1340 }
1341 case PM_UNLESS_NODE: {
1342 pm_unless_node_t *cast = (pm_unless_node_t *) node;
1343 if (cast->statements == NULL || cast->else_clause == NULL) {
1344 return NULL;
1345 }
1346 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1347 if (vn == NULL) {
1348 return NULL;
1349 }
1350 if (void_node == NULL) {
1351 void_node = vn;
1352 }
1353 node = UP(cast->else_clause);
1354 break;
1355 }
1356 case PM_ELSE_NODE: {
1357 pm_else_node_t *cast = (pm_else_node_t *) node;
1358 node = UP(cast->statements);
1359 break;
1360 }
1361 case PM_AND_NODE:
1362 case PM_OR_NODE:
1363 // The left operand of an and/or node was already checked for a
1364 // value when the node was created, so descending into it again
1365 // would re-report the same void value and, in a chain such as
1366 // `a && a && ...`, walk the whole left branch on every operator,
1367 // which is quadratic in the length of the chain.
1368 return NULL;
1369 case PM_LOCAL_VARIABLE_WRITE_NODE: {
1371
1372 pm_scope_t *scope = parser->current_scope;
1373 for (uint32_t depth = 0; depth < cast->depth; depth++) scope = scope->previous;
1374
1375 pm_locals_read(&scope->locals, cast->name);
1376 return NULL;
1377 }
1378 default:
1379 return NULL;
1380 }
1381 }
1382
1383 return NULL;
1384}
1385
1386static PRISM_INLINE void
1387pm_assert_value_expression(pm_parser_t *parser, pm_node_t *node) {
1388 pm_node_t *void_node = pm_check_value_expression(parser, node);
1389 if (void_node != NULL) {
1390 pm_parser_err_node(parser, void_node, PM_ERR_VOID_EXPRESSION);
1391 }
1392}
1393
1397static void
1398pm_void_statement_check(pm_parser_t *parser, const pm_node_t *node) {
1399 const char *type = NULL;
1400 int length = 0;
1401
1402 switch (PM_NODE_TYPE(node)) {
1403 case PM_BACK_REFERENCE_READ_NODE:
1404 case PM_CLASS_VARIABLE_READ_NODE:
1405 case PM_GLOBAL_VARIABLE_READ_NODE:
1406 case PM_INSTANCE_VARIABLE_READ_NODE:
1407 case PM_LOCAL_VARIABLE_READ_NODE:
1408 case PM_NUMBERED_REFERENCE_READ_NODE:
1409 type = "a variable";
1410 length = 10;
1411 break;
1412 case PM_CALL_NODE: {
1413 const pm_call_node_t *cast = (const pm_call_node_t *) node;
1414 if (cast->call_operator_loc.length > 0 || cast->message_loc.length == 0) break;
1415
1416 const pm_constant_t *message = pm_constant_pool_id_to_constant(&parser->constant_pool, cast->name);
1417 switch (message->length) {
1418 case 1:
1419 switch (message->start[0]) {
1420 case '+':
1421 case '-':
1422 case '*':
1423 case '/':
1424 case '%':
1425 case '|':
1426 case '^':
1427 case '&':
1428 case '>':
1429 case '<':
1430 type = (const char *) message->start;
1431 length = 1;
1432 break;
1433 }
1434 break;
1435 case 2:
1436 switch (message->start[1]) {
1437 case '=':
1438 if (message->start[0] == '<' || message->start[0] == '>' || message->start[0] == '!' || message->start[0] == '=') {
1439 type = (const char *) message->start;
1440 length = 2;
1441 }
1442 break;
1443 case '@':
1444 if (message->start[0] == '+' || message->start[0] == '-') {
1445 type = (const char *) message->start;
1446 length = 2;
1447 }
1448 break;
1449 case '*':
1450 if (message->start[0] == '*') {
1451 type = (const char *) message->start;
1452 length = 2;
1453 }
1454 break;
1455 }
1456 break;
1457 case 3:
1458 if (memcmp(message->start, "<=>", 3) == 0) {
1459 type = "<=>";
1460 length = 3;
1461 }
1462 break;
1463 }
1464
1465 break;
1466 }
1467 case PM_CONSTANT_PATH_NODE:
1468 type = "::";
1469 length = 2;
1470 break;
1471 case PM_CONSTANT_READ_NODE:
1472 type = "a constant";
1473 length = 10;
1474 break;
1475 case PM_DEFINED_NODE:
1476 type = "defined?";
1477 length = 8;
1478 break;
1479 case PM_FALSE_NODE:
1480 type = "false";
1481 length = 5;
1482 break;
1483 case PM_FLOAT_NODE:
1484 case PM_IMAGINARY_NODE:
1485 case PM_INTEGER_NODE:
1486 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1487 case PM_INTERPOLATED_STRING_NODE:
1488 case PM_RATIONAL_NODE:
1489 case PM_REGULAR_EXPRESSION_NODE:
1490 case PM_SOURCE_ENCODING_NODE:
1491 case PM_SOURCE_FILE_NODE:
1492 case PM_SOURCE_LINE_NODE:
1493 case PM_STRING_NODE:
1494 case PM_SYMBOL_NODE:
1495 type = "a literal";
1496 length = 9;
1497 break;
1498 case PM_NIL_NODE:
1499 type = "nil";
1500 length = 3;
1501 break;
1502 case PM_RANGE_NODE: {
1503 const pm_range_node_t *cast = (const pm_range_node_t *) node;
1504
1505 if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) {
1506 type = "...";
1507 length = 3;
1508 } else {
1509 type = "..";
1510 length = 2;
1511 }
1512
1513 break;
1514 }
1515 case PM_SELF_NODE:
1516 type = "self";
1517 length = 4;
1518 break;
1519 case PM_TRUE_NODE:
1520 type = "true";
1521 length = 4;
1522 break;
1523 default:
1524 break;
1525 }
1526
1527 if (type != NULL) {
1528 PM_PARSER_WARN_NODE_FORMAT(parser, node, PM_WARN_VOID_STATEMENT, length, type);
1529 }
1530}
1531
1536static void
1537pm_void_statements_check(pm_parser_t *parser, const pm_statements_node_t *node, bool last_value) {
1538 assert(node->body.size > 0);
1539 const size_t size = node->body.size - (last_value ? 1 : 0);
1540 for (size_t index = 0; index < size; index++) {
1541 pm_void_statement_check(parser, node->body.nodes[index]);
1542 }
1543}
1544
1550typedef enum {
1551 PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL,
1552 PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP,
1553 PM_CONDITIONAL_PREDICATE_TYPE_NOT
1554} pm_conditional_predicate_type_t;
1555
1559static void
1560pm_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) {
1561 switch (type) {
1562 case PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL:
1563 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "condition");
1564 break;
1565 case PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP:
1566 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "flip-flop");
1567 break;
1568 case PM_CONDITIONAL_PREDICATE_TYPE_NOT:
1569 break;
1570 }
1571}
1572
1577static bool
1578pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) {
1579 switch (PM_NODE_TYPE(node)) {
1580 case PM_ARRAY_NODE: {
1581 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1582
1583 const pm_array_node_t *cast = (const pm_array_node_t *) node;
1584 for (size_t index = 0; index < cast->elements.size; index++) {
1585 if (!pm_conditional_predicate_warn_write_literal_p(cast->elements.nodes[index])) return false;
1586 }
1587
1588 return true;
1589 }
1590 case PM_HASH_NODE: {
1591 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1592
1593 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
1594 for (size_t index = 0; index < cast->elements.size; index++) {
1595 const pm_node_t *element = cast->elements.nodes[index];
1596 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return false;
1597
1598 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1599 if (!pm_conditional_predicate_warn_write_literal_p(assoc->key) || !pm_conditional_predicate_warn_write_literal_p(assoc->value)) return false;
1600 }
1601
1602 return true;
1603 }
1604 case PM_FALSE_NODE:
1605 case PM_FLOAT_NODE:
1606 case PM_IMAGINARY_NODE:
1607 case PM_INTEGER_NODE:
1608 case PM_NIL_NODE:
1609 case PM_RATIONAL_NODE:
1610 case PM_REGULAR_EXPRESSION_NODE:
1611 case PM_SOURCE_ENCODING_NODE:
1612 case PM_SOURCE_FILE_NODE:
1613 case PM_SOURCE_LINE_NODE:
1614 case PM_STRING_NODE:
1615 case PM_SYMBOL_NODE:
1616 case PM_TRUE_NODE:
1617 return true;
1618 default:
1619 return false;
1620 }
1621}
1622
1627static PRISM_INLINE void
1628pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) {
1629 if (pm_conditional_predicate_warn_write_literal_p(node)) {
1630 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);
1631 }
1632}
1633
1646static void
1647pm_conditional_predicate(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type) {
1648 switch (PM_NODE_TYPE(node)) {
1649 case PM_AND_NODE: {
1650 pm_and_node_t *cast = (pm_and_node_t *) node;
1651 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1652 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1653 break;
1654 }
1655 case PM_OR_NODE: {
1656 pm_or_node_t *cast = (pm_or_node_t *) node;
1657 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1658 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1659 break;
1660 }
1661 case PM_PARENTHESES_NODE: {
1663
1664 if ((cast->body != NULL) && PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE)) {
1665 pm_statements_node_t *statements = (pm_statements_node_t *) cast->body;
1666 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1667 }
1668
1669 break;
1670 }
1671 case PM_BEGIN_NODE: {
1672 pm_begin_node_t *cast = (pm_begin_node_t *) node;
1673 if (cast->statements != NULL) {
1674 pm_statements_node_t *statements = cast->statements;
1675 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1676 }
1677 break;
1678 }
1679 case PM_RANGE_NODE: {
1680 pm_range_node_t *cast = (pm_range_node_t *) node;
1681
1682 if (cast->left != NULL) pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1683 if (cast->right != NULL) pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1684
1685 // Here we change the range node into a flip flop node. We can do
1686 // this since the nodes are exactly the same except for the type.
1687 // We're only asserting against the size when we should probably
1688 // assert against the entire layout, but we'll assume tests will
1689 // catch this.
1690 assert(sizeof(pm_range_node_t) == sizeof(pm_flip_flop_node_t));
1691 node->type = PM_FLIP_FLOP_NODE;
1692
1693 break;
1694 }
1695 case PM_REGULAR_EXPRESSION_NODE:
1696 // Here we change the regular expression node into a match last line
1697 // node. We can do this since the nodes are exactly the same except
1698 // for the type.
1700 node->type = PM_MATCH_LAST_LINE_NODE;
1701
1702 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1703 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "regex ");
1704 }
1705
1706 break;
1707 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1708 // Here we change the interpolated regular expression node into an
1709 // interpolated match last line node. We can do this since the nodes
1710 // are exactly the same except for the type.
1712 node->type = PM_INTERPOLATED_MATCH_LAST_LINE_NODE;
1713
1714 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1715 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "regex ");
1716 }
1717
1718 break;
1719 case PM_INTEGER_NODE:
1720 if (type == PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP) {
1721 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1722 pm_parser_warn_node(parser, node, PM_WARN_INTEGER_IN_FLIP_FLOP);
1723 }
1724 } else {
1725 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1726 }
1727 break;
1728 case PM_STRING_NODE:
1729 case PM_SOURCE_FILE_NODE:
1730 case PM_INTERPOLATED_STRING_NODE:
1731 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "string ");
1732 break;
1733 case PM_SYMBOL_NODE:
1734 case PM_INTERPOLATED_SYMBOL_NODE:
1735 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "symbol ");
1736 break;
1737 case PM_SOURCE_LINE_NODE:
1738 case PM_SOURCE_ENCODING_NODE:
1739 case PM_FLOAT_NODE:
1740 case PM_RATIONAL_NODE:
1741 case PM_IMAGINARY_NODE:
1742 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1743 break;
1744 case PM_CLASS_VARIABLE_WRITE_NODE:
1745 pm_conditional_predicate_warn_write_literal(parser, ((pm_class_variable_write_node_t *) node)->value);
1746 break;
1747 case PM_CONSTANT_WRITE_NODE:
1748 pm_conditional_predicate_warn_write_literal(parser, ((pm_constant_write_node_t *) node)->value);
1749 break;
1750 case PM_GLOBAL_VARIABLE_WRITE_NODE:
1751 pm_conditional_predicate_warn_write_literal(parser, ((pm_global_variable_write_node_t *) node)->value);
1752 break;
1753 case PM_INSTANCE_VARIABLE_WRITE_NODE:
1754 pm_conditional_predicate_warn_write_literal(parser, ((pm_instance_variable_write_node_t *) node)->value);
1755 break;
1756 case PM_LOCAL_VARIABLE_WRITE_NODE:
1757 pm_conditional_predicate_warn_write_literal(parser, ((pm_local_variable_write_node_t *) node)->value);
1758 break;
1759 case PM_MULTI_WRITE_NODE:
1760 pm_conditional_predicate_warn_write_literal(parser, ((pm_multi_write_node_t *) node)->value);
1761 break;
1762 default:
1763 break;
1764 }
1765}
1766
1789
1793static PRISM_INLINE const pm_location_t *
1794pm_arguments_end(pm_arguments_t *arguments) {
1795 if (arguments->block != NULL) {
1796 uint32_t end = PM_NODE_END(arguments->block);
1797
1798 if (arguments->closing_loc.length > 0) {
1799 uint32_t arguments_end = PM_LOCATION_END(&arguments->closing_loc);
1800 if (arguments_end > end) {
1801 return &arguments->closing_loc;
1802 }
1803 }
1804 return &arguments->block->location;
1805 }
1806 if (arguments->closing_loc.length > 0) {
1807 return &arguments->closing_loc;
1808 }
1809 if (arguments->arguments != NULL) {
1810 return &arguments->arguments->base.location;
1811 }
1812 if (arguments->opening_loc.length > 0) {
1813 return &arguments->opening_loc;
1814 }
1815 return NULL;
1816}
1817
1822static void
1823pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_block_node_t *block) {
1824 // First, check that we have arguments and that we don't have a closing
1825 // location for them.
1826 if (arguments->arguments == NULL || arguments->closing_loc.length > 0) {
1827 return;
1828 }
1829
1830 // Next, check that we don't have a single parentheses argument. This would
1831 // look like:
1832 //
1833 // foo (1) {}
1834 //
1835 // In this case, it's actually okay for the block to be attached to the
1836 // call, even though it looks like it's attached to the argument.
1837 if (arguments->arguments->arguments.size == 1 && PM_NODE_TYPE_P(arguments->arguments->arguments.nodes[0], PM_PARENTHESES_NODE)) {
1838 return;
1839 }
1840
1841 // If we didn't hit a case before this check, then at this point we need to
1842 // add a syntax error.
1843 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_UNEXPECTED_BLOCK);
1844}
1845
1846/******************************************************************************/
1847/* Basic character checks */
1848/******************************************************************************/
1849
1856static PRISM_INLINE size_t
1857char_is_identifier_start(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
1858 if (n <= 0) return 0;
1859
1860 if (parser->encoding_changed) {
1861 size_t width;
1862
1863 if ((width = parser->encoding->alpha_char(b, n)) != 0) {
1864 return width;
1865 } else if (*b == '_') {
1866 return 1;
1867 } else if (*b >= 0x80) {
1868 return parser->encoding->char_width(b, n);
1869 } else {
1870 return 0;
1871 }
1872 } else if (*b < 0x80) {
1873 return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT ? 1 : 0) || (*b == '_');
1874 } else {
1875 return pm_encoding_utf_8_char_width(b, n);
1876 }
1877}
1878
1883static PRISM_INLINE size_t
1884char_is_identifier_utf8(const uint8_t *b, ptrdiff_t n) {
1885 if (n <= 0) {
1886 return 0;
1887 } else if (*b < 0x80) {
1888 return (*b == '_') || (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT ? 1 : 0);
1889 } else {
1890 return pm_encoding_utf_8_char_width(b, n);
1891 }
1892}
1893
1907#if defined(PRISM_HAS_NEON)
1908#include <arm_neon.h>
1909
1910static PRISM_INLINE size_t
1911scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
1912 const uint8_t *cursor = start;
1913
1914 // Nibble-based lookup tables for classifying [a-zA-Z0-9_].
1915 // Each high nibble is assigned a unique bit; the low nibble table
1916 // contains the OR of bits for all high nibbles that have an
1917 // identifier character at that low nibble position. A byte is an
1918 // identifier character iff (low_lut[lo] & high_lut[hi]) != 0.
1919 static const uint8_t low_lut_data[16] = {
1920 0x15, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
1921 0x1F, 0x1F, 0x1E, 0x0A, 0x0A, 0x0A, 0x0A, 0x0E
1922 };
1923 static const uint8_t high_lut_data[16] = {
1924 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10,
1925 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1926 };
1927 const uint8x16_t low_lut = vld1q_u8(low_lut_data);
1928 const uint8x16_t high_lut = vld1q_u8(high_lut_data);
1929 const uint8x16_t mask_0f = vdupq_n_u8(0x0F);
1930
1931 while (cursor + 16 <= end) {
1932 uint8x16_t v = vld1q_u8(cursor);
1933
1934 uint8x16_t lo_class = vqtbl1q_u8(low_lut, vandq_u8(v, mask_0f));
1935 uint8x16_t hi_class = vqtbl1q_u8(high_lut, vshrq_n_u8(v, 4));
1936 uint8x16_t ident = vandq_u8(lo_class, hi_class);
1937
1938 // Fast check: if the per-byte minimum is nonzero, every byte matched.
1939 if (vminvq_u8(ident) != 0) {
1940 cursor += 16;
1941 continue;
1942 }
1943
1944 // Find the first non-identifier byte (zero in ident).
1945 uint8x16_t is_zero = vceqq_u8(ident, vdupq_n_u8(0));
1946 uint64_t lo = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 0);
1947
1948 if (lo != 0) {
1949 cursor += pm_ctzll(lo) / 8;
1950 } else {
1951 uint64_t hi = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 1);
1952 cursor += 8 + pm_ctzll(hi) / 8;
1953 }
1954
1955 return (size_t) (cursor - start);
1956 }
1957
1958 return (size_t) (cursor - start);
1959}
1960
1961#elif defined(PRISM_HAS_SSE2)
1962#include <emmintrin.h>
1963
1964static PRISM_INLINE size_t
1965scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
1966 const uint8_t *cursor = start;
1967
1968 while (cursor + 16 <= end) {
1969 __m128i v = _mm_loadu_si128((const __m128i *) cursor);
1970 __m128i zero = _mm_setzero_si128();
1971
1972 // Unsigned range check via saturating subtraction:
1973 // byte >= lo ⟺ saturate(lo - byte) == 0
1974 // byte <= hi ⟺ saturate(byte - hi) == 0
1975
1976 // Fold case: OR with 0x20 maps A-Z to a-z.
1977 __m128i lowered = _mm_or_si128(v, _mm_set1_epi8(0x20));
1978 __m128i letter = _mm_and_si128(
1979 _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x61), lowered), zero),
1980 _mm_cmpeq_epi8(_mm_subs_epu8(lowered, _mm_set1_epi8(0x7A)), zero));
1981
1982 __m128i digit = _mm_and_si128(
1983 _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x30), v), zero),
1984 _mm_cmpeq_epi8(_mm_subs_epu8(v, _mm_set1_epi8(0x39)), zero));
1985
1986 __m128i underscore = _mm_cmpeq_epi8(v, _mm_set1_epi8(0x5F));
1987
1988 __m128i ident = _mm_or_si128(_mm_or_si128(letter, digit), underscore);
1989 int mask = _mm_movemask_epi8(ident);
1990
1991 if (mask == 0xFFFF) {
1992 cursor += 16;
1993 continue;
1994 }
1995
1996 cursor += pm_ctzll((uint64_t) (~mask & 0xFFFF));
1997 return (size_t) (cursor - start);
1998 }
1999
2000 return (size_t) (cursor - start);
2001}
2002
2003// The SWAR path uses pm_ctzll to find the first non-matching byte within a
2004// word, which only yields the correct byte index on little-endian targets.
2005// We gate on a positive little-endian check so that unknown-endianness
2006// platforms safely fall through to the no-op fallback.
2007#elif defined(PRISM_HAS_SWAR)
2008
2018static PRISM_INLINE size_t
2019scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
2020 static const uint64_t ones = 0x0101010101010101ULL;
2021 static const uint64_t highs = 0x8080808080808080ULL;
2022 const uint8_t *cursor = start;
2023
2024 while (cursor + 8 <= end) {
2025 uint64_t word;
2026 memcpy(&word, cursor, 8);
2027
2028 // Bail on any non-ASCII byte.
2029 if (word & highs) break;
2030
2031 uint64_t digit = ((word | highs) - ones * 0x30) & ((ones * 0x39 | highs) - word) & highs;
2032
2033 // Fold upper- and lowercase together by forcing bit 5 (OR 0x20),
2034 // then check the lowercase range once. A-Z maps to a-z; the
2035 // only non-letter byte that could alias into [0x61,0x7A] is one
2036 // whose original value was in [0x41,0x5A] — which is exactly
2037 // the uppercase letters we want to match.
2038 uint64_t lowered = word | (ones * 0x20);
2039 uint64_t letter = ((lowered | highs) - ones * 0x61) & ((ones * 0x7A | highs) - lowered) & highs;
2040
2041 // Standard SWAR "has zero byte" idiom on (word XOR 0x5F) to find
2042 // bytes equal to underscore. Safe from cross-byte borrows because
2043 // the ASCII guard above ensures all bytes are < 0x80.
2044 uint64_t xor_us = word ^ (ones * 0x5F);
2045 uint64_t underscore = (xor_us - ones) & ~xor_us & highs;
2046
2047 uint64_t ident = digit | letter | underscore;
2048
2049 if (ident == highs) {
2050 cursor += 8;
2051 continue;
2052 }
2053
2054 // Find the first non-identifier byte. On little-endian the first
2055 // byte sits in the least-significant position.
2056 uint64_t not_ident = ~ident & highs;
2057 cursor += pm_ctzll(not_ident) / 8;
2058 return (size_t) (cursor - start);
2059 }
2060
2061 return (size_t) (cursor - start);
2062}
2063
2064#else
2065
2066// No-op fallback for big-endian or other unsupported platforms.
2067// The caller's byte-at-a-time loop handles everything.
2068#define scan_identifier_ascii(start, end) ((size_t) 0)
2069
2070#endif
2071
2077static PRISM_INLINE size_t
2078char_is_identifier(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
2079 if (n <= 0) {
2080 return 0;
2081 } else if (parser->encoding_changed) {
2082 size_t width;
2083
2084 if ((width = parser->encoding->alnum_char(b, n)) != 0) {
2085 return width;
2086 } else if (*b == '_') {
2087 return 1;
2088 } else if (*b >= 0x80) {
2089 return parser->encoding->char_width(b, n);
2090 } else {
2091 return 0;
2092 }
2093 } else {
2094 return char_is_identifier_utf8(b, n);
2095 }
2096}
2097
2098// Here we're defining a perfect hash for the characters that are allowed in
2099// global names. This is used to quickly check the next character after a $ to
2100// see if it's a valid character for a global name.
2101#define BIT(c, idx) (((c) / 32 - 1 == idx) ? (1U << ((c) % 32)) : 0)
2102#define PUNCT(idx) ( \
2103 BIT('~', idx) | BIT('*', idx) | BIT('$', idx) | BIT('?', idx) | \
2104 BIT('!', idx) | BIT('@', idx) | BIT('/', idx) | BIT('\\', idx) | \
2105 BIT(';', idx) | BIT(',', idx) | BIT('.', idx) | BIT('=', idx) | \
2106 BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \
2107 BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \
2108 BIT('0', idx))
2109
2110const unsigned int pm_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { PUNCT(0), PUNCT(1), PUNCT(2) };
2111
2112#undef BIT
2113#undef PUNCT
2114
2115static PRISM_INLINE bool
2116char_is_global_name_punctuation(const uint8_t b) {
2117 const unsigned int i = (const unsigned int) b;
2118 if (i <= 0x20 || 0x7e < i) return false;
2119
2120 return (pm_global_name_punctuation_hash[(i - 0x20) / 32] >> (i % 32)) & 1;
2121}
2122
2123static PRISM_INLINE bool
2124token_is_setter_name(pm_token_t *token) {
2125 return (
2126 (token->type == PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL) ||
2127 ((token->type == PM_TOKEN_IDENTIFIER) &&
2128 (token->end - token->start >= 2) &&
2129 (token->end[-1] == '='))
2130 );
2131}
2132
2136static bool
2137pm_local_is_keyword(const char *source, size_t length) {
2138#define KEYWORD(name) if (memcmp(source, name, length) == 0) return true
2139
2140 switch (length) {
2141 case 2:
2142 switch (source[0]) {
2143 case 'd': KEYWORD("do"); return false;
2144 case 'i': KEYWORD("if"); KEYWORD("in"); return false;
2145 case 'o': KEYWORD("or"); return false;
2146 default: return false;
2147 }
2148 case 3:
2149 switch (source[0]) {
2150 case 'a': KEYWORD("and"); return false;
2151 case 'd': KEYWORD("def"); return false;
2152 case 'e': KEYWORD("end"); return false;
2153 case 'f': KEYWORD("for"); return false;
2154 case 'n': KEYWORD("nil"); KEYWORD("not"); return false;
2155 default: return false;
2156 }
2157 case 4:
2158 switch (source[0]) {
2159 case 'c': KEYWORD("case"); return false;
2160 case 'e': KEYWORD("else"); return false;
2161 case 'n': KEYWORD("next"); return false;
2162 case 'r': KEYWORD("redo"); return false;
2163 case 's': KEYWORD("self"); return false;
2164 case 't': KEYWORD("then"); KEYWORD("true"); return false;
2165 case 'w': KEYWORD("when"); return false;
2166 default: return false;
2167 }
2168 case 5:
2169 switch (source[0]) {
2170 case 'a': KEYWORD("alias"); return false;
2171 case 'b': KEYWORD("begin"); KEYWORD("break"); return false;
2172 case 'c': KEYWORD("class"); return false;
2173 case 'e': KEYWORD("elsif"); return false;
2174 case 'f': KEYWORD("false"); return false;
2175 case 'r': KEYWORD("retry"); return false;
2176 case 's': KEYWORD("super"); return false;
2177 case 'u': KEYWORD("undef"); KEYWORD("until"); return false;
2178 case 'w': KEYWORD("while"); return false;
2179 case 'y': KEYWORD("yield"); return false;
2180 default: return false;
2181 }
2182 case 6:
2183 switch (source[0]) {
2184 case 'e': KEYWORD("ensure"); return false;
2185 case 'm': KEYWORD("module"); return false;
2186 case 'r': KEYWORD("rescue"); KEYWORD("return"); return false;
2187 case 'u': KEYWORD("unless"); return false;
2188 default: return false;
2189 }
2190 case 8:
2191 KEYWORD("__LINE__");
2192 KEYWORD("__FILE__");
2193 return false;
2194 case 12:
2195 KEYWORD("__ENCODING__");
2196 return false;
2197 default:
2198 return false;
2199 }
2200
2201#undef KEYWORD
2202}
2203
2204/******************************************************************************/
2205/* Node flag handling functions */
2206/******************************************************************************/
2207
2211static PRISM_INLINE void
2212pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) {
2213 node->flags |= flag;
2214}
2215
2219static PRISM_INLINE void
2220pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) {
2221 node->flags &= (pm_node_flags_t) ~flag;
2222}
2223
2227static PRISM_INLINE void
2228pm_node_flag_set_repeated_parameter(pm_node_t *node) {
2229 assert(PM_NODE_TYPE(node) == PM_BLOCK_LOCAL_VARIABLE_NODE ||
2230 PM_NODE_TYPE(node) == PM_BLOCK_PARAMETER_NODE ||
2231 PM_NODE_TYPE(node) == PM_KEYWORD_REST_PARAMETER_NODE ||
2232 PM_NODE_TYPE(node) == PM_OPTIONAL_KEYWORD_PARAMETER_NODE ||
2233 PM_NODE_TYPE(node) == PM_OPTIONAL_PARAMETER_NODE ||
2234 PM_NODE_TYPE(node) == PM_REQUIRED_KEYWORD_PARAMETER_NODE ||
2235 PM_NODE_TYPE(node) == PM_REQUIRED_PARAMETER_NODE ||
2236 PM_NODE_TYPE(node) == PM_REST_PARAMETER_NODE);
2237
2238 pm_node_flag_set(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER);
2239}
2240
2241/******************************************************************************/
2242/* Node creation functions */
2243/******************************************************************************/
2244
2250#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)
2251
2255static PRISM_INLINE pm_node_flags_t
2256pm_regular_expression_flags_create(pm_parser_t *parser, const pm_token_t *closing) {
2257 pm_node_flags_t flags = 0;
2258
2259 if (closing->type == PM_TOKEN_REGEXP_END) {
2260 pm_buffer_t unknown_flags = { 0 };
2261
2262 // The closing delimiter is normally a single byte, so the options
2263 // follow it. A `\r\n` newline delimiter is two bytes, however, so we
2264 // skip past it to avoid misreading the trailing `\n` as an option.
2265 const uint8_t *flag = closing->start + 1;
2266 if ((closing->end - closing->start) >= 2 && closing->start[0] == '\r' && closing->start[1] == '\n') {
2267 flag++;
2268 }
2269
2270 for (; flag < closing->end; flag++) {
2271 switch (*flag) {
2272 case 'i': flags |= PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE; break;
2273 case 'm': flags |= PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE; break;
2274 case 'x': flags |= PM_REGULAR_EXPRESSION_FLAGS_EXTENDED; break;
2275 case 'o': flags |= PM_REGULAR_EXPRESSION_FLAGS_ONCE; break;
2276
2277 case 'e': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_EUC_JP); break;
2278 case 'n': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT); break;
2279 case 's': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J); break;
2280 case 'u': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_UTF_8); break;
2281
2282 default: pm_buffer_append_byte(&unknown_flags, *flag);
2283 }
2284 }
2285
2286 size_t unknown_flags_length = pm_buffer_length(&unknown_flags);
2287 if (unknown_flags_length != 0) {
2288 const char *word = unknown_flags_length >= 2 ? "options" : "option";
2289 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_REGEXP_UNKNOWN_OPTIONS, word, unknown_flags_length, pm_buffer_value(&unknown_flags));
2290 }
2291 pm_buffer_cleanup(&unknown_flags);
2292 }
2293
2294 return flags;
2295}
2296
2297#undef PM_REGULAR_EXPRESSION_ENCODING_MASK
2298
2299static pm_statements_node_t *
2300pm_statements_node_create(pm_parser_t *parser);
2301
2302static void
2303pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline);
2304
2305static size_t
2306pm_statements_node_body_length(pm_statements_node_t *node);
2307
2312static PRISM_INLINE void
2313pm_integer_arena_move(pm_arena_t *arena, pm_integer_t *integer) {
2314 if (integer->values != NULL) {
2315 size_t byte_size = integer->length * sizeof(uint32_t);
2316 uint32_t *old_values = integer->values;
2317 integer->values = (uint32_t *) pm_arena_memdup(arena, old_values, byte_size, PRISM_ALIGNOF(uint32_t));
2318 xfree(old_values);
2319 }
2320}
2321
2325static pm_error_recovery_node_t *
2326pm_error_recovery_node_create(pm_parser_t *parser, uint32_t start, uint32_t length) {
2327 return pm_error_recovery_node_new(
2328 parser->arena,
2329 ++parser->node_id,
2330 0,
2331 ((pm_location_t) { .start = start, .length = length }),
2332 NULL
2333 );
2334}
2335
2339static pm_error_recovery_node_t *
2340pm_error_recovery_node_create_unexpected(pm_parser_t *parser, pm_node_t *unexpected) {
2341 return pm_error_recovery_node_new(
2342 parser->arena,
2343 ++parser->node_id,
2344 0,
2345 unexpected->location,
2346 unexpected
2347 );
2348}
2349
2353static pm_alias_global_variable_node_t *
2354pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
2355 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
2356
2357 return pm_alias_global_variable_node_new(
2358 parser->arena,
2359 ++parser->node_id,
2360 0,
2361 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name),
2362 new_name,
2363 old_name,
2364 TOK2LOC(parser, keyword)
2365 );
2366}
2367
2371static pm_alias_method_node_t *
2372pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
2373 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
2374
2375 return pm_alias_method_node_new(
2376 parser->arena,
2377 ++parser->node_id,
2378 0,
2379 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name),
2380 new_name,
2381 old_name,
2382 TOK2LOC(parser, keyword)
2383 );
2384}
2385
2389static pm_alternation_pattern_node_t *
2390pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node_t *right, const pm_token_t *operator) {
2391 return pm_alternation_pattern_node_new(
2392 parser->arena,
2393 ++parser->node_id,
2394 0,
2395 PM_LOCATION_INIT_NODES(left, right),
2396 left,
2397 right,
2398 TOK2LOC(parser, operator)
2399 );
2400}
2401
2405static pm_and_node_t *
2406pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
2407 pm_assert_value_expression(parser, left);
2408
2409 return pm_and_node_new(
2410 parser->arena,
2411 ++parser->node_id,
2412 0,
2413 PM_LOCATION_INIT_NODES(left, right),
2414 left,
2415 right,
2416 TOK2LOC(parser, operator)
2417 );
2418}
2419
2423static pm_arguments_node_t *
2424pm_arguments_node_create(pm_parser_t *parser) {
2425 return pm_arguments_node_new(
2426 parser->arena,
2427 ++parser->node_id,
2428 0,
2429 PM_LOCATION_INIT_UNSET,
2430 ((pm_node_list_t) { 0 })
2431 );
2432}
2433
2437static size_t
2438pm_arguments_node_size(pm_arguments_node_t *node) {
2439 return node->arguments.size;
2440}
2441
2445static void
2446pm_arguments_node_arguments_append(pm_arena_t *arena, pm_arguments_node_t *node, pm_node_t *argument) {
2447 if (pm_arguments_node_size(node) == 0) {
2448 PM_NODE_START_SET_NODE(node, argument);
2449 }
2450
2451 if (PM_NODE_END(node) < PM_NODE_END(argument)) {
2452 PM_NODE_LENGTH_SET_NODE(node, argument);
2453 }
2454
2455 pm_node_list_append(arena, &node->arguments, argument);
2456
2457 if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) {
2458 if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
2459 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS);
2460 } else {
2461 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT);
2462 }
2463 }
2464}
2465
2469static pm_array_node_t *
2470pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) {
2471 if (opening == NULL) {
2472 return pm_array_node_new(
2473 parser->arena,
2474 ++parser->node_id,
2475 PM_NODE_FLAG_STATIC_LITERAL,
2476 PM_LOCATION_INIT_UNSET,
2477 ((pm_node_list_t) { 0 }),
2478 ((pm_location_t) { 0 }),
2479 ((pm_location_t) { 0 })
2480 );
2481 } else {
2482 return pm_array_node_new(
2483 parser->arena,
2484 ++parser->node_id,
2485 PM_NODE_FLAG_STATIC_LITERAL,
2486 PM_LOCATION_INIT_TOKEN(parser, opening),
2487 ((pm_node_list_t) { 0 }),
2488 TOK2LOC(parser, opening),
2489 TOK2LOC(parser, opening)
2490 );
2491 }
2492}
2493
2497static PRISM_INLINE void
2498pm_array_node_elements_append(pm_arena_t *arena, pm_array_node_t *node, pm_node_t *element) {
2499 if (!node->elements.size && !node->opening_loc.length) {
2500 PM_NODE_START_SET_NODE(node, element);
2501 }
2502
2503 pm_node_list_append(arena, &node->elements, element);
2504 PM_NODE_LENGTH_SET_NODE(node, element);
2505
2506 // If the element is not a static literal, then the array is not a static
2507 // literal. Turn that flag off.
2508 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)) {
2509 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
2510 }
2511
2512 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
2513 pm_node_flag_set(UP(node), PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT);
2514 }
2515}
2516
2520static void
2521pm_array_node_close_set(const pm_parser_t *parser, pm_array_node_t *node, const pm_token_t *closing) {
2522 assert(closing->type == PM_TOKEN_BRACKET_RIGHT || closing->type == PM_TOKEN_STRING_END || closing->type == 0);
2523 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
2524 node->closing_loc = TOK2LOC(parser, closing);
2525}
2526
2531static pm_array_pattern_node_t *
2532pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *nodes) {
2533 pm_array_pattern_node_t *node = pm_array_pattern_node_new(
2534 parser->arena,
2535 ++parser->node_id,
2536 0,
2537 PM_LOCATION_INIT_NODES(nodes->nodes[0], nodes->nodes[nodes->size - 1]),
2538 NULL,
2539 ((pm_node_list_t) { 0 }),
2540 NULL,
2541 ((pm_node_list_t) { 0 }),
2542 ((pm_location_t) { 0 }),
2543 ((pm_location_t) { 0 })
2544 );
2545
2546 // For now we're going to just copy over each pointer manually. This could be
2547 // much more efficient, as we could instead resize the node list.
2548 bool found_rest = false;
2549 pm_node_t *child;
2550
2551 PM_NODE_LIST_FOREACH(nodes, index, child) {
2552 if (!found_rest && (PM_NODE_TYPE_P(child, PM_SPLAT_NODE) || PM_NODE_TYPE_P(child, PM_IMPLICIT_REST_NODE))) {
2553 node->rest = child;
2554 found_rest = true;
2555 } else if (found_rest) {
2556 pm_node_list_append(parser->arena, &node->posts, child);
2557 } else {
2558 pm_node_list_append(parser->arena, &node->requireds, child);
2559 }
2560 }
2561
2562 return node;
2563}
2564
2568static pm_array_pattern_node_t *
2569pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) {
2570 return pm_array_pattern_node_new(
2571 parser->arena,
2572 ++parser->node_id,
2573 0,
2574 PM_LOCATION_INIT_NODE(rest),
2575 NULL,
2576 ((pm_node_list_t) { 0 }),
2577 rest,
2578 ((pm_node_list_t) { 0 }),
2579 ((pm_location_t) { 0 }),
2580 ((pm_location_t) { 0 })
2581 );
2582}
2583
2588static pm_array_pattern_node_t *
2589pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, const pm_token_t *opening, const pm_token_t *closing) {
2590 return pm_array_pattern_node_new(
2591 parser->arena,
2592 ++parser->node_id,
2593 0,
2594 PM_LOCATION_INIT_NODE_TOKEN(parser, constant, closing),
2595 constant,
2596 ((pm_node_list_t) { 0 }),
2597 NULL,
2598 ((pm_node_list_t) { 0 }),
2599 TOK2LOC(parser, opening),
2600 TOK2LOC(parser, closing)
2601 );
2602}
2603
2608static pm_array_pattern_node_t *
2609pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
2610 return pm_array_pattern_node_new(
2611 parser->arena,
2612 ++parser->node_id,
2613 0,
2614 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
2615 NULL,
2616 ((pm_node_list_t) { 0 }),
2617 NULL,
2618 ((pm_node_list_t) { 0 }),
2619 TOK2LOC(parser, opening),
2620 TOK2LOC(parser, closing)
2621 );
2622}
2623
2624static PRISM_INLINE void
2625pm_array_pattern_node_requireds_append(pm_arena_t *arena, pm_array_pattern_node_t *node, pm_node_t *inner) {
2626 pm_node_list_append(arena, &node->requireds, inner);
2627}
2628
2632static pm_assoc_node_t *
2633pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *operator, pm_node_t *value) {
2634 uint32_t end;
2635
2636 if (value != NULL && PM_NODE_END(value) > PM_NODE_END(key)) {
2637 end = PM_NODE_END(value);
2638 } else if (operator != NULL) {
2639 end = PM_TOKEN_END(parser, operator);
2640 } else {
2641 end = PM_NODE_END(key);
2642 }
2643
2644 // Hash string keys will be frozen, so we can mark them as frozen here so
2645 // that the compiler picks them up and also when we check for static literal
2646 // on the keys it gets factored in.
2647 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
2648 key->flags |= PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL;
2649 }
2650
2651 // If the key and value of this assoc node are both static literals, then
2652 // we can mark this node as a static literal.
2653 pm_node_flags_t flags = 0;
2654 if (
2655 !PM_NODE_TYPE_P(key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(key, PM_HASH_NODE) && !PM_NODE_TYPE_P(key, PM_RANGE_NODE) &&
2656 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)
2657 ) {
2658 flags = key->flags & value->flags & PM_NODE_FLAG_STATIC_LITERAL;
2659 }
2660
2661 return pm_assoc_node_new(
2662 parser->arena,
2663 ++parser->node_id,
2664 flags,
2665 ((pm_location_t) { .start = PM_NODE_START(key), .length = U32(end - PM_NODE_START(key)) }),
2666 key,
2667 value,
2668 NTOK2LOC(parser, operator)
2669 );
2670}
2671
2675static pm_assoc_splat_node_t *
2676pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token_t *operator) {
2677 assert(operator->type == PM_TOKEN_USTAR_STAR);
2678
2679 return pm_assoc_splat_node_new(
2680 parser->arena,
2681 ++parser->node_id,
2682 0,
2683 (value == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, value),
2684 value,
2685 TOK2LOC(parser, operator)
2686 );
2687}
2688
2692static pm_back_reference_read_node_t *
2693pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
2694 assert(name->type == PM_TOKEN_BACK_REFERENCE);
2695
2696 return pm_back_reference_read_node_new(
2697 parser->arena,
2698 ++parser->node_id,
2699 0,
2700 PM_LOCATION_INIT_TOKEN(parser, name),
2701 pm_parser_constant_id_token(parser, name)
2702 );
2703}
2704
2708static pm_begin_node_t *
2709pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_statements_node_t *statements) {
2710 uint32_t start = begin_keyword == NULL ? 0 : PM_TOKEN_START(parser, begin_keyword);
2711 uint32_t end = statements == NULL ? (begin_keyword == NULL ? 0 : PM_TOKEN_END(parser, begin_keyword)) : PM_NODE_END(statements);
2712
2713 return pm_begin_node_new(
2714 parser->arena,
2715 ++parser->node_id,
2716 0,
2717 ((pm_location_t) { .start = start, .length = U32(end - start) }),
2718 NTOK2LOC(parser, begin_keyword),
2719 statements,
2720 NULL,
2721 NULL,
2722 NULL,
2723 ((pm_location_t) { 0 })
2724 );
2725}
2726
2730static void
2731pm_begin_node_rescue_clause_set(pm_begin_node_t *node, pm_rescue_node_t *rescue_clause) {
2732 if (node->begin_keyword_loc.length == 0) {
2733 PM_NODE_START_SET_NODE(node, rescue_clause);
2734 }
2735 PM_NODE_LENGTH_SET_NODE(node, rescue_clause);
2736 node->rescue_clause = rescue_clause;
2737}
2738
2742static void
2743pm_begin_node_else_clause_set(pm_begin_node_t *node, pm_else_node_t *else_clause) {
2744 if ((node->begin_keyword_loc.length == 0) && PM_NODE_START(node) == 0) {
2745 PM_NODE_START_SET_NODE(node, else_clause);
2746 }
2747 PM_NODE_LENGTH_SET_NODE(node, else_clause);
2748 node->else_clause = else_clause;
2749}
2750
2754static void
2755pm_begin_node_ensure_clause_set(pm_begin_node_t *node, pm_ensure_node_t *ensure_clause) {
2756 if ((node->begin_keyword_loc.length == 0) && PM_NODE_START(node) == 0) {
2757 PM_NODE_START_SET_NODE(node, ensure_clause);
2758 }
2759 PM_NODE_LENGTH_SET_NODE(node, ensure_clause);
2760 node->ensure_clause = ensure_clause;
2761}
2762
2766static void
2767pm_begin_node_end_keyword_set(const pm_parser_t *parser, pm_begin_node_t *node, const pm_token_t *end_keyword) {
2768 assert(end_keyword->type == PM_TOKEN_KEYWORD_END || end_keyword->type == 0);
2769 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
2770 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
2771}
2772
2776static pm_block_argument_node_t *
2777pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
2778 assert(operator->type == PM_TOKEN_UAMPERSAND);
2779
2780 return pm_block_argument_node_new(
2781 parser->arena,
2782 ++parser->node_id,
2783 0,
2784 (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression),
2785 expression,
2786 TOK2LOC(parser, operator)
2787 );
2788}
2789
2793static pm_block_node_t *
2794pm_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) {
2795 return pm_block_node_new(
2796 parser->arena,
2797 ++parser->node_id,
2798 0,
2799 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
2800 *locals,
2801 parameters,
2802 body,
2803 TOK2LOC(parser, opening),
2804 TOK2LOC(parser, closing)
2805 );
2806}
2807
2811static pm_block_parameter_node_t *
2812pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator) {
2813 assert(operator->type == PM_TOKEN_UAMPERSAND || operator->type == PM_TOKEN_AMPERSAND);
2814
2815 return pm_block_parameter_node_new(
2816 parser->arena,
2817 ++parser->node_id,
2818 0,
2819 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
2820 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
2821 NTOK2LOC(parser, name),
2822 TOK2LOC(parser, operator)
2823 );
2824}
2825
2829static pm_block_parameters_node_t *
2830pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *parameters, const pm_token_t *opening) {
2831 uint32_t start;
2832 if (opening != NULL) {
2833 start = PM_TOKEN_START(parser, opening);
2834 } else if (parameters != NULL) {
2835 start = PM_NODE_START(parameters);
2836 } else {
2837 start = 0;
2838 }
2839
2840 uint32_t end;
2841 if (parameters != NULL) {
2842 end = PM_NODE_END(parameters);
2843 } else if (opening != NULL) {
2844 end = PM_TOKEN_END(parser, opening);
2845 } else {
2846 end = 0;
2847 }
2848
2849 return pm_block_parameters_node_new(
2850 parser->arena,
2851 ++parser->node_id,
2852 0,
2853 ((pm_location_t) { .start = start, .length = U32(end - start) }),
2854 parameters,
2855 ((pm_node_list_t) { 0 }),
2856 NTOK2LOC(parser, opening),
2857 ((pm_location_t) { 0 })
2858 );
2859}
2860
2864static void
2865pm_block_parameters_node_closing_set(const pm_parser_t *parser, pm_block_parameters_node_t *node, const pm_token_t *closing) {
2866 assert(closing->type == PM_TOKEN_PIPE || closing->type == PM_TOKEN_PARENTHESIS_RIGHT || closing->type == 0);
2867 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
2868 node->closing_loc = TOK2LOC(parser, closing);
2869}
2870
2874static pm_block_local_variable_node_t *
2875pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) {
2876 return pm_block_local_variable_node_new(
2877 parser->arena,
2878 ++parser->node_id,
2879 0,
2880 PM_LOCATION_INIT_TOKEN(parser, name),
2881 pm_parser_constant_id_token(parser, name)
2882 );
2883}
2884
2888static void
2889pm_block_parameters_node_append_local(pm_arena_t *arena, pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) {
2890 pm_node_list_append(arena, &node->locals, UP(local));
2891
2892 if (PM_NODE_LENGTH(node) == 0) {
2893 PM_NODE_START_SET_NODE(node, local);
2894 }
2895
2896 PM_NODE_LENGTH_SET_NODE(node, local);
2897}
2898
2902static pm_break_node_t *
2903pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
2904 assert(keyword->type == PM_TOKEN_KEYWORD_BREAK);
2905
2906 return pm_break_node_new(
2907 parser->arena,
2908 ++parser->node_id,
2909 0,
2910 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
2911 arguments,
2912 TOK2LOC(parser, keyword)
2913 );
2914}
2915
2916// There are certain flags that we want to use internally but don't want to
2917// expose because they are not relevant beyond parsing. Therefore we'll define
2918// them here and not define them in config.yml/a header file.
2919static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2);
2920
2921static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1);
2922static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2);
2923static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3);
2924
2930static pm_call_node_t *
2931pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) {
2932 return pm_call_node_new(
2933 parser->arena,
2934 ++parser->node_id,
2935 flags,
2936 PM_LOCATION_INIT_UNSET,
2937 NULL,
2938 ((pm_location_t) { 0 }),
2939 0,
2940 ((pm_location_t) { 0 }),
2941 ((pm_location_t) { 0 }),
2942 NULL,
2943 ((pm_location_t) { 0 }),
2944 ((pm_location_t) { 0 }),
2945 NULL
2946 );
2947}
2948
2953static PRISM_INLINE pm_node_flags_t
2954pm_call_node_ignore_visibility_flag(const pm_node_t *receiver) {
2955 return PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY : 0;
2956}
2957
2962static pm_call_node_t *
2963pm_call_node_aref_create(pm_parser_t *parser, pm_node_t *receiver, pm_arguments_t *arguments) {
2964 pm_assert_value_expression(parser, receiver);
2965
2966 pm_node_flags_t flags = pm_call_node_ignore_visibility_flag(receiver);
2967 if (arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_ARGUMENT_NODE)) {
2968 flags |= PM_CALL_NODE_FLAGS_INDEX;
2969 }
2970
2971 pm_call_node_t *node = pm_call_node_create(parser, flags);
2972
2973 PM_NODE_START_SET_NODE(node, receiver);
2974
2975 const pm_location_t *end = pm_arguments_end(arguments);
2976 assert(end != NULL && "unreachable");
2977 PM_NODE_LENGTH_SET_LOCATION(node, end);
2978
2979 node->receiver = receiver;
2980 node->message_loc.start = arguments->opening_loc.start;
2981 node->message_loc.length = (arguments->closing_loc.start + arguments->closing_loc.length) - arguments->opening_loc.start;
2982
2983 node->opening_loc = arguments->opening_loc;
2984 node->arguments = arguments->arguments;
2985 node->closing_loc = arguments->closing_loc;
2986 node->block = arguments->block;
2987
2988 node->name = pm_parser_constant_id_constant(parser, "[]", 2);
2989 return node;
2990}
2991
2995static pm_call_node_t *
2996pm_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) {
2997 pm_assert_value_expression(parser, receiver);
2998 pm_assert_value_expression(parser, argument);
2999
3000 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver) | flags);
3001
3002 PM_NODE_START_SET_NODE(node, PM_NODE_START(receiver) < PM_NODE_START(argument) ? receiver : argument);
3003 PM_NODE_LENGTH_SET_NODE(node, PM_NODE_END(receiver) > PM_NODE_END(argument) ? receiver : argument);
3004
3005 node->receiver = receiver;
3006 node->message_loc = TOK2LOC(parser, operator);
3007
3008 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
3009 pm_arguments_node_arguments_append(parser->arena, arguments, argument);
3010 node->arguments = arguments;
3011
3012 node->name = pm_parser_constant_id_token(parser, operator);
3013 return node;
3014}
3015
3016static const uint8_t * parse_operator_symbol_name(const pm_token_t *);
3017
3021static pm_call_node_t *
3022pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_token_t *message, pm_arguments_t *arguments) {
3023 pm_assert_value_expression(parser, receiver);
3024
3025 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3026
3027 PM_NODE_START_SET_NODE(node, receiver);
3028 const pm_location_t *end = pm_arguments_end(arguments);
3029 if (end == NULL) {
3030 PM_NODE_LENGTH_SET_TOKEN(parser, node, message);
3031 } else {
3032 PM_NODE_LENGTH_SET_LOCATION(node, end);
3033 }
3034
3035 node->receiver = receiver;
3036 node->call_operator_loc = TOK2LOC(parser, operator);
3037 node->message_loc = TOK2LOC(parser, message);
3038 node->opening_loc = arguments->opening_loc;
3039 node->arguments = arguments->arguments;
3040 node->closing_loc = arguments->closing_loc;
3041 node->block = arguments->block;
3042
3043 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
3044 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
3045 }
3046
3051 node->name = pm_parser_constant_id_raw(parser, message->start, parse_operator_symbol_name(message));
3052 return node;
3053}
3054
3058static pm_call_node_t *
3059pm_call_node_call_synthesized_create(pm_parser_t *parser, pm_node_t *receiver, const char *message, pm_arguments_node_t *arguments) {
3060 pm_call_node_t *node = pm_call_node_create(parser, 0);
3061 node->base.location = (pm_location_t) { .start = 0, .length = U32(parser->end - parser->start) };
3062
3063 node->receiver = receiver;
3064 node->arguments = arguments;
3065
3066 node->name = pm_parser_constant_id_constant(parser, message, strlen(message));
3067 return node;
3068}
3069
3074static pm_call_node_t *
3075pm_call_node_fcall_create(pm_parser_t *parser, pm_token_t *message, pm_arguments_t *arguments) {
3076 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3077
3078 PM_NODE_START_SET_TOKEN(parser, node, message);
3079 const pm_location_t *end = pm_arguments_end(arguments);
3080 assert(end != NULL && "unreachable");
3081 PM_NODE_LENGTH_SET_LOCATION(node, end);
3082
3083 node->message_loc = TOK2LOC(parser, message);
3084 node->opening_loc = arguments->opening_loc;
3085 node->arguments = arguments->arguments;
3086 node->closing_loc = arguments->closing_loc;
3087 node->block = arguments->block;
3088
3089 node->name = pm_parser_constant_id_token(parser, message);
3090 return node;
3091}
3092
3097static pm_call_node_t *
3098pm_call_node_fcall_synthesized_create(pm_parser_t *parser, pm_arguments_node_t *arguments, pm_constant_id_t name) {
3099 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3100
3101 node->base.location = (pm_location_t) { 0 };
3102 node->arguments = arguments;
3103
3104 node->name = name;
3105 return node;
3106}
3107
3111static pm_call_node_t *
3112pm_call_node_not_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *message, pm_arguments_t *arguments) {
3113 pm_assert_value_expression(parser, receiver);
3114 if (receiver != NULL) pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
3115
3116 pm_call_node_t *node = pm_call_node_create(parser, receiver == NULL ? 0 : pm_call_node_ignore_visibility_flag(receiver));
3117
3118 PM_NODE_START_SET_TOKEN(parser, node, message);
3119 if (arguments->closing_loc.length > 0) {
3120 PM_NODE_LENGTH_SET_LOCATION(node, &arguments->closing_loc);
3121 } else {
3122 assert(receiver != NULL);
3123 PM_NODE_LENGTH_SET_NODE(node, receiver);
3124 }
3125
3126 node->receiver = receiver;
3127 node->message_loc = TOK2LOC(parser, message);
3128 node->opening_loc = arguments->opening_loc;
3129 node->arguments = arguments->arguments;
3130 node->closing_loc = arguments->closing_loc;
3131
3132 node->name = pm_parser_constant_id_constant(parser, "!", 1);
3133 return node;
3134}
3135
3139static pm_call_node_t *
3140pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_arguments_t *arguments) {
3141 pm_assert_value_expression(parser, receiver);
3142
3143 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3144
3145 PM_NODE_START_SET_NODE(node, receiver);
3146 const pm_location_t *end = pm_arguments_end(arguments);
3147 assert(end != NULL && "unreachable");
3148 PM_NODE_LENGTH_SET_LOCATION(node, end);
3149
3150 node->receiver = receiver;
3151 node->call_operator_loc = TOK2LOC(parser, operator);
3152 node->opening_loc = arguments->opening_loc;
3153 node->arguments = arguments->arguments;
3154 node->closing_loc = arguments->closing_loc;
3155 node->block = arguments->block;
3156
3157 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
3158 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
3159 }
3160
3161 node->name = pm_parser_constant_id_constant(parser, "call", 4);
3162 return node;
3163}
3164
3168static pm_call_node_t *
3169pm_call_node_unary_create(pm_parser_t *parser, pm_token_t *operator, pm_node_t *receiver, const char *name) {
3170 pm_assert_value_expression(parser, receiver);
3171
3172 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3173
3174 PM_NODE_START_SET_TOKEN(parser, node, operator);
3175 PM_NODE_LENGTH_SET_NODE(node, receiver);
3176
3177 node->receiver = receiver;
3178 node->message_loc = TOK2LOC(parser, operator);
3179
3180 node->name = pm_parser_constant_id_constant(parser, name, strlen(name));
3181 return node;
3182}
3183
3188static pm_call_node_t *
3189pm_call_node_variable_call_create(pm_parser_t *parser, pm_token_t *message) {
3190 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3191
3192 node->base.location = TOK2LOC(parser, message);
3193 node->message_loc = TOK2LOC(parser, message);
3194
3195 node->name = pm_parser_constant_id_token(parser, message);
3196 return node;
3197}
3198
3203static PRISM_INLINE bool
3204pm_call_node_writable_p(const pm_parser_t *parser, const pm_call_node_t *node) {
3205 return (
3206 (node->message_loc.length > 0) &&
3207 (parser->start[node->message_loc.start + node->message_loc.length - 1] != '!') &&
3208 (parser->start[node->message_loc.start + node->message_loc.length - 1] != '?') &&
3209 char_is_identifier_start(parser, parser->start + node->message_loc.start, (ptrdiff_t) node->message_loc.length) &&
3210 (node->opening_loc.length == 0) &&
3211 (node->arguments == NULL) &&
3212 (node->block == NULL)
3213 );
3214}
3215
3219static void
3220pm_call_write_read_name_init(pm_parser_t *parser, pm_constant_id_t *read_name, pm_constant_id_t *write_name) {
3221 pm_constant_t *write_constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *write_name);
3222
3223 if (write_constant->length > 0) {
3224 size_t length = write_constant->length - 1;
3225
3226 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
3227 memcpy(memory, write_constant->start, length);
3228
3229 *read_name = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, memory, length);
3230 } else {
3231 // We can get here if the message was missing because of a syntax error.
3232 *read_name = pm_parser_constant_id_constant(parser, "", 0);
3233 }
3234}
3235
3239static pm_call_and_write_node_t *
3240pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3241 assert(target->block == NULL);
3242 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3243
3244 pm_call_and_write_node_t *node = pm_call_and_write_node_new(
3245 parser->arena,
3246 ++parser->node_id,
3247 FL(target),
3248 PM_LOCATION_INIT_NODES(target, value),
3249 target->receiver,
3250 target->call_operator_loc,
3251 target->message_loc,
3252 0,
3253 target->name,
3254 TOK2LOC(parser, operator),
3255 value
3256 );
3257
3258 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3259
3260 // The target is no longer necessary because we've reused its children.
3261 // It is arena-allocated so no explicit free is needed.
3262
3263 return node;
3264}
3265
3270static void
3271pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) {
3272 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
3273 if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) {
3274 pm_node_t *node;
3275 PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) {
3276 if (PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)) {
3277 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_INDEX_KEYWORDS);
3278 break;
3279 }
3280 }
3281 }
3282
3283 if (block != NULL) {
3284 pm_parser_err_node(parser, block, PM_ERR_UNEXPECTED_INDEX_BLOCK);
3285 }
3286 }
3287}
3288
3292static pm_index_and_write_node_t *
3293pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3294 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3295
3296 pm_index_arguments_check(parser, target->arguments, target->block);
3297
3298 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3299
3300 pm_index_and_write_node_t *node = pm_index_and_write_node_new(
3301 parser->arena,
3302 ++parser->node_id,
3303 FL(target),
3304 PM_LOCATION_INIT_NODES(target, value),
3305 target->receiver,
3306 target->call_operator_loc,
3307 target->opening_loc,
3308 target->arguments,
3309 target->closing_loc,
3310 (pm_block_argument_node_t *) target->block,
3311 TOK2LOC(parser, operator),
3312 value
3313 );
3314
3315 // The target is no longer necessary because we've reused its children.
3316 // It is arena-allocated so no explicit free is needed.
3317
3318 return node;
3319}
3320
3324static pm_call_operator_write_node_t *
3325pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3326 assert(target->block == NULL);
3327
3328 pm_call_operator_write_node_t *node = pm_call_operator_write_node_new(
3329 parser->arena,
3330 ++parser->node_id,
3331 FL(target),
3332 PM_LOCATION_INIT_NODES(target, value),
3333 target->receiver,
3334 target->call_operator_loc,
3335 target->message_loc,
3336 0,
3337 target->name,
3338 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
3339 TOK2LOC(parser, operator),
3340 value
3341 );
3342
3343 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3344
3345 // The target is no longer necessary because we've reused its children.
3346 // It is arena-allocated so no explicit free is needed.
3347
3348 return node;
3349}
3350
3354static pm_index_operator_write_node_t *
3355pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3356 pm_index_arguments_check(parser, target->arguments, target->block);
3357
3358 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3359
3360 pm_index_operator_write_node_t *node = pm_index_operator_write_node_new(
3361 parser->arena,
3362 ++parser->node_id,
3363 FL(target),
3364 PM_LOCATION_INIT_NODES(target, value),
3365 target->receiver,
3366 target->call_operator_loc,
3367 target->opening_loc,
3368 target->arguments,
3369 target->closing_loc,
3370 (pm_block_argument_node_t *) target->block,
3371 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
3372 TOK2LOC(parser, operator),
3373 value
3374 );
3375
3376 // The target is no longer necessary because we've reused its children.
3377 // It is arena-allocated so no explicit free is needed.
3378
3379 return node;
3380}
3381
3385static pm_call_or_write_node_t *
3386pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3387 assert(target->block == NULL);
3388 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3389
3390 pm_call_or_write_node_t *node = pm_call_or_write_node_new(
3391 parser->arena,
3392 ++parser->node_id,
3393 FL(target),
3394 PM_LOCATION_INIT_NODES(target, value),
3395 target->receiver,
3396 target->call_operator_loc,
3397 target->message_loc,
3398 0,
3399 target->name,
3400 TOK2LOC(parser, operator),
3401 value
3402 );
3403
3404 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3405
3406 // The target is no longer necessary because we've reused its children.
3407 // It is arena-allocated so no explicit free is needed.
3408
3409 return node;
3410}
3411
3415static pm_index_or_write_node_t *
3416pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3417 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3418
3419 pm_index_arguments_check(parser, target->arguments, target->block);
3420
3421 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3422
3423 pm_index_or_write_node_t *node = pm_index_or_write_node_new(
3424 parser->arena,
3425 ++parser->node_id,
3426 FL(target),
3427 PM_LOCATION_INIT_NODES(target, value),
3428 target->receiver,
3429 target->call_operator_loc,
3430 target->opening_loc,
3431 target->arguments,
3432 target->closing_loc,
3433 (pm_block_argument_node_t *) target->block,
3434 TOK2LOC(parser, operator),
3435 value
3436 );
3437
3438 // The target is no longer necessary because we've reused its children.
3439 // It is arena-allocated so no explicit free is needed.
3440
3441 return node;
3442}
3443
3448static pm_call_target_node_t *
3449pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3450 pm_call_target_node_t *node = pm_call_target_node_new(
3451 parser->arena,
3452 ++parser->node_id,
3453 FL(target),
3454 PM_LOCATION_INIT_NODE(target),
3455 target->receiver,
3456 target->call_operator_loc,
3457 target->name,
3458 target->message_loc
3459 );
3460
3461 /* It is possible to get here where we have parsed an invalid syntax tree
3462 * where the call operator was not present. In that case we will have a
3463 * problem because it is a required location. In this case we need to fill
3464 * it in with a fake location so that the syntax tree remains valid. */
3465 if (node->call_operator_loc.length == 0) {
3466 node->call_operator_loc = target->base.location;
3467 }
3468
3469 // The target is no longer necessary because we've reused its children.
3470 // It is arena-allocated so no explicit free is needed.
3471
3472 return node;
3473}
3474
3479static pm_index_target_node_t *
3480pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3481 pm_index_arguments_check(parser, target->arguments, target->block);
3482 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3483
3484 pm_index_target_node_t *node = pm_index_target_node_new(
3485 parser->arena,
3486 ++parser->node_id,
3487 FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE,
3488 PM_LOCATION_INIT_NODE(target),
3489 target->receiver,
3490 target->opening_loc,
3491 target->arguments,
3492 target->closing_loc,
3493 (pm_block_argument_node_t *) target->block
3494 );
3495
3496 // The target is no longer necessary because we've reused its children.
3497 // It is arena-allocated so no explicit free is needed.
3498
3499 return node;
3500}
3501
3505static pm_capture_pattern_node_t *
3506pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_local_variable_target_node_t *target, const pm_token_t *operator) {
3507 return pm_capture_pattern_node_new(
3508 parser->arena,
3509 ++parser->node_id,
3510 0,
3511 PM_LOCATION_INIT_NODES(value, target),
3512 value,
3513 target,
3514 TOK2LOC(parser, operator)
3515 );
3516}
3517
3521static pm_case_node_t *
3522pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) {
3523 return pm_case_node_new(
3524 parser->arena,
3525 ++parser->node_id,
3526 0,
3527 PM_LOCATION_INIT_TOKENS(parser, case_keyword, end_keyword == NULL ? case_keyword : end_keyword),
3528 predicate,
3529 ((pm_node_list_t) { 0 }),
3530 NULL,
3531 TOK2LOC(parser, case_keyword),
3532 NTOK2LOC(parser, end_keyword)
3533 );
3534}
3535
3539static void
3540pm_case_node_condition_append(pm_arena_t *arena, pm_case_node_t *node, pm_node_t *condition) {
3541 assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
3542
3543 pm_node_list_append(arena, &node->conditions, condition);
3544 PM_NODE_LENGTH_SET_NODE(node, condition);
3545}
3546
3550static void
3551pm_case_node_else_clause_set(pm_case_node_t *node, pm_else_node_t *else_clause) {
3552 node->else_clause = else_clause;
3553 PM_NODE_LENGTH_SET_NODE(node, else_clause);
3554}
3555
3559static void
3560pm_case_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_node_t *node, const pm_token_t *end_keyword) {
3561 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
3562 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
3563}
3564
3568static pm_case_match_node_t *
3569pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate) {
3570 return pm_case_match_node_new(
3571 parser->arena,
3572 ++parser->node_id,
3573 0,
3574 PM_LOCATION_INIT_TOKEN(parser, case_keyword),
3575 predicate,
3576 ((pm_node_list_t) { 0 }),
3577 NULL,
3578 TOK2LOC(parser, case_keyword),
3579 ((pm_location_t) { 0 })
3580 );
3581}
3582
3586static void
3587pm_case_match_node_condition_append(pm_arena_t *arena, pm_case_match_node_t *node, pm_node_t *condition) {
3588 assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
3589
3590 pm_node_list_append(arena, &node->conditions, condition);
3591 PM_NODE_LENGTH_SET_NODE(node, condition);
3592}
3593
3597static void
3598pm_case_match_node_else_clause_set(pm_case_match_node_t *node, pm_else_node_t *else_clause) {
3599 node->else_clause = else_clause;
3600 PM_NODE_LENGTH_SET_NODE(node, else_clause);
3601}
3602
3606static void
3607pm_case_match_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_match_node_t *node, const pm_token_t *end_keyword) {
3608 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
3609 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
3610}
3611
3615static pm_class_node_t *
3616pm_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) {
3617 return pm_class_node_new(
3618 parser->arena,
3619 ++parser->node_id,
3620 0,
3621 PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword),
3622 *locals,
3623 TOK2LOC(parser, class_keyword),
3624 constant_path,
3625 NTOK2LOC(parser, inheritance_operator),
3626 superclass,
3627 body,
3628 TOK2LOC(parser, end_keyword),
3629 pm_parser_constant_id_token(parser, name)
3630 );
3631}
3632
3636static pm_class_variable_and_write_node_t *
3637pm_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) {
3638 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3639
3640 return pm_class_variable_and_write_node_new(
3641 parser->arena,
3642 ++parser->node_id,
3643 0,
3644 PM_LOCATION_INIT_NODES(target, value),
3645 target->name,
3646 target->base.location,
3647 TOK2LOC(parser, operator),
3648 value
3649 );
3650}
3651
3655static pm_class_variable_operator_write_node_t *
3656pm_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) {
3657 return pm_class_variable_operator_write_node_new(
3658 parser->arena,
3659 ++parser->node_id,
3660 0,
3661 PM_LOCATION_INIT_NODES(target, value),
3662 target->name,
3663 target->base.location,
3664 TOK2LOC(parser, operator),
3665 value,
3666 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3667 );
3668}
3669
3673static pm_class_variable_or_write_node_t *
3674pm_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) {
3675 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3676
3677 return pm_class_variable_or_write_node_new(
3678 parser->arena,
3679 ++parser->node_id,
3680 0,
3681 PM_LOCATION_INIT_NODES(target, value),
3682 target->name,
3683 target->base.location,
3684 TOK2LOC(parser, operator),
3685 value
3686 );
3687}
3688
3692static pm_class_variable_read_node_t *
3693pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
3694 assert(token->type == PM_TOKEN_CLASS_VARIABLE);
3695
3696 return pm_class_variable_read_node_new(
3697 parser->arena,
3698 ++parser->node_id,
3699 0,
3700 PM_LOCATION_INIT_TOKEN(parser, token),
3701 pm_parser_constant_id_token(parser, token)
3702 );
3703}
3704
3711static PRISM_INLINE pm_node_flags_t
3712pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) {
3713 if (PM_NODE_TYPE_P(node, PM_ARRAY_NODE) && ((const pm_array_node_t *) node)->opening_loc.length == 0) {
3714 return flags;
3715 }
3716 return 0;
3717}
3718
3722static pm_class_variable_write_node_t *
3723pm_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) {
3724 return pm_class_variable_write_node_new(
3725 parser->arena,
3726 ++parser->node_id,
3727 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3728 PM_LOCATION_INIT_NODES(read_node, value),
3729 read_node->name,
3730 read_node->base.location,
3731 value,
3732 TOK2LOC(parser, operator)
3733 );
3734}
3735
3739static pm_constant_path_and_write_node_t *
3740pm_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) {
3741 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3742
3743 return pm_constant_path_and_write_node_new(
3744 parser->arena,
3745 ++parser->node_id,
3746 0,
3747 PM_LOCATION_INIT_NODES(target, value),
3748 target,
3749 TOK2LOC(parser, operator),
3750 value
3751 );
3752}
3753
3757static pm_constant_path_operator_write_node_t *
3758pm_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) {
3759 return pm_constant_path_operator_write_node_new(
3760 parser->arena,
3761 ++parser->node_id,
3762 0,
3763 PM_LOCATION_INIT_NODES(target, value),
3764 target,
3765 TOK2LOC(parser, operator),
3766 value,
3767 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3768 );
3769}
3770
3774static pm_constant_path_or_write_node_t *
3775pm_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) {
3776 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3777
3778 return pm_constant_path_or_write_node_new(
3779 parser->arena,
3780 ++parser->node_id,
3781 0,
3782 PM_LOCATION_INIT_NODES(target, value),
3783 target,
3784 TOK2LOC(parser, operator),
3785 value
3786 );
3787}
3788
3792static pm_constant_path_node_t *
3793pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_token_t *delimiter, const pm_token_t *name_token) {
3794 pm_assert_value_expression(parser, parent);
3795
3796 pm_constant_id_t name = PM_CONSTANT_ID_UNSET;
3797 if (name_token->type == PM_TOKEN_CONSTANT) {
3798 name = pm_parser_constant_id_token(parser, name_token);
3799 }
3800
3801 return pm_constant_path_node_new(
3802 parser->arena,
3803 ++parser->node_id,
3804 0,
3805 (parent == NULL) ? PM_LOCATION_INIT_TOKENS(parser, delimiter, name_token) : PM_LOCATION_INIT_NODE_TOKEN(parser, parent, name_token),
3806 parent,
3807 name,
3808 TOK2LOC(parser, delimiter),
3809 TOK2LOC(parser, name_token)
3810 );
3811}
3812
3816static pm_constant_path_write_node_t *
3817pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3818 return pm_constant_path_write_node_new(
3819 parser->arena,
3820 ++parser->node_id,
3821 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3822 PM_LOCATION_INIT_NODES(target, value),
3823 target,
3824 TOK2LOC(parser, operator),
3825 value
3826 );
3827}
3828
3832static pm_constant_and_write_node_t *
3833pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3834 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3835
3836 return pm_constant_and_write_node_new(
3837 parser->arena,
3838 ++parser->node_id,
3839 0,
3840 PM_LOCATION_INIT_NODES(target, value),
3841 target->name,
3842 target->base.location,
3843 TOK2LOC(parser, operator),
3844 value
3845 );
3846}
3847
3851static pm_constant_operator_write_node_t *
3852pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3853 return pm_constant_operator_write_node_new(
3854 parser->arena,
3855 ++parser->node_id,
3856 0,
3857 PM_LOCATION_INIT_NODES(target, value),
3858 target->name,
3859 target->base.location,
3860 TOK2LOC(parser, operator),
3861 value,
3862 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3863 );
3864}
3865
3869static pm_constant_or_write_node_t *
3870pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3871 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3872
3873 return pm_constant_or_write_node_new(
3874 parser->arena,
3875 ++parser->node_id,
3876 0,
3877 PM_LOCATION_INIT_NODES(target, value),
3878 target->name,
3879 target->base.location,
3880 TOK2LOC(parser, operator),
3881 value
3882 );
3883}
3884
3888static pm_constant_read_node_t *
3889pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
3890 assert(name->type == PM_TOKEN_CONSTANT || name->type == 0);
3891
3892 return pm_constant_read_node_new(
3893 parser->arena,
3894 ++parser->node_id,
3895 0,
3896 PM_LOCATION_INIT_TOKEN(parser, name),
3897 pm_parser_constant_id_token(parser, name)
3898 );
3899}
3900
3904static pm_constant_write_node_t *
3905pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3906 return pm_constant_write_node_new(
3907 parser->arena,
3908 ++parser->node_id,
3909 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3910 PM_LOCATION_INIT_NODES(target, value),
3911 target->name,
3912 target->base.location,
3913 value,
3914 TOK2LOC(parser, operator)
3915 );
3916}
3917
3921static void
3922pm_def_node_receiver_check(pm_parser_t *parser, const pm_node_t *node) {
3923 switch (PM_NODE_TYPE(node)) {
3924 case PM_BEGIN_NODE: {
3925 const pm_begin_node_t *cast = (pm_begin_node_t *) node;
3926 if (cast->statements != NULL) pm_def_node_receiver_check(parser, UP(cast->statements));
3927 break;
3928 }
3929 case PM_PARENTHESES_NODE: {
3930 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
3931 if (cast->body != NULL) pm_def_node_receiver_check(parser, cast->body);
3932 break;
3933 }
3934 case PM_STATEMENTS_NODE: {
3935 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3936 pm_def_node_receiver_check(parser, cast->body.nodes[cast->body.size - 1]);
3937 break;
3938 }
3939 case PM_ARRAY_NODE:
3940 case PM_FLOAT_NODE:
3941 case PM_IMAGINARY_NODE:
3942 case PM_INTEGER_NODE:
3943 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3944 case PM_INTERPOLATED_STRING_NODE:
3945 case PM_INTERPOLATED_SYMBOL_NODE:
3946 case PM_INTERPOLATED_X_STRING_NODE:
3947 case PM_RATIONAL_NODE:
3948 case PM_REGULAR_EXPRESSION_NODE:
3949 case PM_SOURCE_ENCODING_NODE:
3950 case PM_SOURCE_FILE_NODE:
3951 case PM_SOURCE_LINE_NODE:
3952 case PM_STRING_NODE:
3953 case PM_SYMBOL_NODE:
3954 case PM_X_STRING_NODE:
3955 pm_parser_err_node(parser, node, PM_ERR_SINGLETON_FOR_LITERALS);
3956 break;
3957 default:
3958 break;
3959 }
3960}
3961
3965static pm_def_node_t *
3966pm_def_node_create(
3967 pm_parser_t *parser,
3968 pm_constant_id_t name,
3969 const pm_token_t *name_loc,
3970 pm_node_t *receiver,
3971 pm_parameters_node_t *parameters,
3972 pm_node_t *body,
3973 pm_constant_id_list_t *locals,
3974 const pm_token_t *def_keyword,
3975 const pm_token_t *operator,
3976 const pm_token_t *lparen,
3977 const pm_token_t *rparen,
3978 const pm_token_t *equal,
3979 const pm_token_t *end_keyword
3980) {
3981 if (receiver != NULL) {
3982 pm_def_node_receiver_check(parser, receiver);
3983 }
3984
3985 return pm_def_node_new(
3986 parser->arena,
3987 ++parser->node_id,
3988 0,
3989 (end_keyword == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, def_keyword, body) : PM_LOCATION_INIT_TOKENS(parser, def_keyword, end_keyword),
3990 name,
3991 TOK2LOC(parser, name_loc),
3992 receiver,
3993 parameters,
3994 body,
3995 *locals,
3996 TOK2LOC(parser, def_keyword),
3997 NTOK2LOC(parser, operator),
3998 NTOK2LOC(parser, lparen),
3999 NTOK2LOC(parser, rparen),
4000 NTOK2LOC(parser, equal),
4001 NTOK2LOC(parser, end_keyword)
4002 );
4003}
4004
4008static pm_defined_node_t *
4009pm_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) {
4010 return pm_defined_node_new(
4011 parser->arena,
4012 ++parser->node_id,
4013 0,
4014 (rparen == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, value) : PM_LOCATION_INIT_TOKENS(parser, keyword, rparen),
4015 NTOK2LOC(parser, lparen),
4016 value,
4017 NTOK2LOC(parser, rparen),
4018 TOK2LOC(parser, keyword)
4019 );
4020}
4021
4025static pm_else_node_t *
4026pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
4027 return pm_else_node_new(
4028 parser->arena,
4029 ++parser->node_id,
4030 0,
4031 ((end_keyword == NULL) && (statements != NULL)) ? PM_LOCATION_INIT_TOKEN_NODE(parser, else_keyword, statements) : PM_LOCATION_INIT_TOKENS(parser, else_keyword, end_keyword),
4032 TOK2LOC(parser, else_keyword),
4033 statements,
4034 NTOK2LOC(parser, end_keyword)
4035 );
4036}
4037
4041static pm_embedded_statements_node_t *
4042pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
4043 return pm_embedded_statements_node_new(
4044 parser->arena,
4045 ++parser->node_id,
4046 0,
4047 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
4048 TOK2LOC(parser, opening),
4049 statements,
4050 TOK2LOC(parser, closing)
4051 );
4052}
4053
4057static pm_embedded_variable_node_t *
4058pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
4059 return pm_embedded_variable_node_new(
4060 parser->arena,
4061 ++parser->node_id,
4062 0,
4063 PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable),
4064 TOK2LOC(parser, operator),
4065 variable
4066 );
4067}
4068
4072static pm_ensure_node_t *
4073pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
4074 return pm_ensure_node_new(
4075 parser->arena,
4076 ++parser->node_id,
4077 0,
4078 PM_LOCATION_INIT_TOKENS(parser, ensure_keyword, end_keyword),
4079 TOK2LOC(parser, ensure_keyword),
4080 statements,
4081 TOK2LOC(parser, end_keyword)
4082 );
4083}
4084
4088static pm_false_node_t *
4089pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) {
4090 assert(token->type == PM_TOKEN_KEYWORD_FALSE);
4091
4092 return pm_false_node_new(
4093 parser->arena,
4094 ++parser->node_id,
4095 PM_NODE_FLAG_STATIC_LITERAL,
4096 PM_LOCATION_INIT_TOKEN(parser, token)
4097 );
4098}
4099
4104static pm_find_pattern_node_t *
4105pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) {
4106 assert(nodes->size >= 2);
4107 pm_node_t *left = nodes->nodes[0];
4108 pm_node_t *right = nodes->nodes[nodes->size - 1];
4109
4110 assert(PM_NODE_TYPE_P(left, PM_SPLAT_NODE));
4111 assert(PM_NODE_TYPE_P(right, PM_SPLAT_NODE));
4112
4113 pm_find_pattern_node_t *node = pm_find_pattern_node_new(
4114 parser->arena,
4115 ++parser->node_id,
4116 0,
4117 PM_LOCATION_INIT_NODES(left, right),
4118 NULL,
4119 (pm_splat_node_t *) left,
4120 ((pm_node_list_t) { 0 }),
4121 (pm_splat_node_t *) right,
4122 ((pm_location_t) { 0 }),
4123 ((pm_location_t) { 0 })
4124 );
4125
4126 // For now we're going to just copy over each pointer manually. This could be
4127 // much more efficient, as we could instead resize the node list to only point
4128 // to 1...-1.
4129 for (size_t index = 1; index < nodes->size - 1; index++) {
4130 pm_node_list_append(parser->arena, &node->requireds, nodes->nodes[index]);
4131 }
4132
4133 return node;
4134}
4135
4140static double
4141pm_double_parse(pm_parser_t *parser, const pm_token_t *token) {
4142 ptrdiff_t diff = token->end - token->start;
4143 if (diff <= 0) return 0.0;
4144
4145 // First, get a buffer of the content.
4146 size_t length = (size_t) diff;
4147 const size_t buffer_size = sizeof(char) * (length + 1);
4148 char *buffer = xmalloc(buffer_size);
4149 memcpy((void *) buffer, token->start, length);
4150
4151 // Next, determine if we need to replace the decimal point because of
4152 // locale-specific options, and then normalize them if we have to.
4153 char decimal_point = *localeconv()->decimal_point;
4154 if (decimal_point != '.') {
4155 for (size_t index = 0; index < length; index++) {
4156 if (buffer[index] == '.') buffer[index] = decimal_point;
4157 }
4158 }
4159
4160 /*
4161 * Next, handle underscores by removing them from the buffer in a single
4162 * pass to avoid quadratic-time copies.
4163 */
4164 {
4165 size_t write = 0;
4166 for (size_t read = 0; read < length; read++) {
4167 if (buffer[read] != '_') buffer[write++] = buffer[read];
4168 }
4169 length = write;
4170 }
4171
4172 // Null-terminate the buffer so that strtod cannot read off the end.
4173 buffer[length] = '\0';
4174
4175 // Now, call strtod to parse the value. Note that CRuby has their own
4176 // version of strtod which avoids locales. We're okay using the locale-aware
4177 // version because we've already validated through the parser that the token
4178 // is in a valid format.
4179 errno = 0;
4180 char *eptr;
4181 double value = strtod(buffer, &eptr);
4182
4183 // This should never happen, because we've already checked that the token
4184 // is in a valid format. However it's good to be safe.
4185 if ((eptr != buffer + length) || (errno != 0 && errno != ERANGE)) {
4186 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, PM_ERR_FLOAT_PARSE);
4187 xfree_sized(buffer, buffer_size);
4188 return 0.0;
4189 }
4190
4191 // If errno is set, then it should only be ERANGE. At this point we need to
4192 // check if it's infinity (it should be).
4193 if (errno == ERANGE && PRISM_ISINF(value)) {
4194 int warn_width;
4195 const char *ellipsis;
4196
4197 if (length > 20) {
4198 warn_width = 20;
4199 ellipsis = "...";
4200 } else {
4201 warn_width = (int) length;
4202 ellipsis = "";
4203 }
4204
4205 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);
4206 value = (value < 0.0) ? -HUGE_VAL : HUGE_VAL;
4207 }
4208
4209 // Finally we can free the buffer and return the value.
4210 xfree_sized(buffer, buffer_size);
4211 return value;
4212}
4213
4217static pm_float_node_t *
4218pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) {
4219 assert(token->type == PM_TOKEN_FLOAT);
4220
4221 return pm_float_node_new(
4222 parser->arena,
4223 ++parser->node_id,
4224 PM_NODE_FLAG_STATIC_LITERAL,
4225 PM_LOCATION_INIT_TOKEN(parser, token),
4226 pm_double_parse(parser, token)
4227 );
4228}
4229
4233static pm_imaginary_node_t *
4234pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
4235 assert(token->type == PM_TOKEN_FLOAT_IMAGINARY);
4236
4237 return pm_imaginary_node_new(
4238 parser->arena,
4239 ++parser->node_id,
4240 PM_NODE_FLAG_STATIC_LITERAL,
4241 PM_LOCATION_INIT_TOKEN(parser, token),
4242 UP(pm_float_node_create(parser, &((pm_token_t) {
4243 .type = PM_TOKEN_FLOAT,
4244 .start = token->start,
4245 .end = token->end - 1
4246 })))
4247 );
4248}
4249
4253static pm_rational_node_t *
4254pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
4255 assert(token->type == PM_TOKEN_FLOAT_RATIONAL);
4256
4257 pm_rational_node_t *node = pm_rational_node_new(
4258 parser->arena,
4259 ++parser->node_id,
4260 PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL,
4261 PM_LOCATION_INIT_TOKEN(parser, token),
4262 ((pm_integer_t) { 0 }),
4263 ((pm_integer_t) { 0 })
4264 );
4265
4266 const uint8_t *start = token->start;
4267 const uint8_t *end = token->end - 1; // r
4268
4269 while (start < end && *start == '0') start++; // 0.1 -> .1
4270 while (end > start && end[-1] == '0') end--; // 1.0 -> 1.
4271
4272 size_t length = (size_t) (end - start);
4273 if (length == 1) {
4274 node->denominator.value = 1;
4275 return node;
4276 }
4277
4278 const uint8_t *point = memchr(start, '.', length);
4279 assert(point && "should have a decimal point");
4280
4281 uint8_t *digits = xmalloc(length);
4282 if (digits == NULL) {
4283 fputs("[pm_float_node_rational_create] Failed to allocate memory", stderr);
4284 abort();
4285 }
4286
4287 memcpy(digits, start, (unsigned long) (point - start));
4288 memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1));
4289 pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1);
4290
4291 size_t fract_length = 0;
4292 for (const uint8_t *fract = point; fract < end; ++fract) {
4293 if (*fract != '_') ++fract_length;
4294 }
4295 digits[0] = '1';
4296 if (fract_length > 1) memset(digits + 1, '0', fract_length - 1);
4297 pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + fract_length);
4298 xfree_sized(digits, length);
4299
4300 pm_integers_reduce(&node->numerator, &node->denominator);
4301 pm_integer_arena_move(parser->arena, &node->numerator);
4302 pm_integer_arena_move(parser->arena, &node->denominator);
4303 return node;
4304}
4305
4310static pm_imaginary_node_t *
4311pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
4312 assert(token->type == PM_TOKEN_FLOAT_RATIONAL_IMAGINARY);
4313
4314 return pm_imaginary_node_new(
4315 parser->arena,
4316 ++parser->node_id,
4317 PM_NODE_FLAG_STATIC_LITERAL,
4318 PM_LOCATION_INIT_TOKEN(parser, token),
4319 UP(pm_float_node_rational_create(parser, &((pm_token_t) {
4320 .type = PM_TOKEN_FLOAT_RATIONAL,
4321 .start = token->start,
4322 .end = token->end - 1
4323 })))
4324 );
4325}
4326
4330static pm_for_node_t *
4331pm_for_node_create(
4332 pm_parser_t *parser,
4333 pm_node_t *index,
4334 pm_node_t *collection,
4335 pm_statements_node_t *statements,
4336 const pm_token_t *for_keyword,
4337 const pm_token_t *in_keyword,
4338 const pm_token_t *do_keyword,
4339 const pm_token_t *end_keyword
4340) {
4341 return pm_for_node_new(
4342 parser->arena,
4343 ++parser->node_id,
4344 0,
4345 PM_LOCATION_INIT_TOKENS(parser, for_keyword, end_keyword),
4346 index,
4347 collection,
4348 statements,
4349 TOK2LOC(parser, for_keyword),
4350 TOK2LOC(parser, in_keyword),
4351 NTOK2LOC(parser, do_keyword),
4352 TOK2LOC(parser, end_keyword)
4353 );
4354}
4355
4359static pm_forwarding_arguments_node_t *
4360pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token) {
4361 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4362
4363 return pm_forwarding_arguments_node_new(
4364 parser->arena,
4365 ++parser->node_id,
4366 0,
4367 PM_LOCATION_INIT_TOKEN(parser, token)
4368 );
4369}
4370
4374static pm_forwarding_parameter_node_t *
4375pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
4376 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4377
4378 return pm_forwarding_parameter_node_new(
4379 parser->arena,
4380 ++parser->node_id,
4381 0,
4382 PM_LOCATION_INIT_TOKEN(parser, token)
4383 );
4384}
4385
4389static pm_forwarding_super_node_t *
4390pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm_arguments_t *arguments) {
4391 assert(arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_NODE));
4392 assert(token->type == PM_TOKEN_KEYWORD_SUPER);
4393
4394 pm_block_node_t *block = NULL;
4395 if (arguments->block != NULL) {
4396 block = (pm_block_node_t *) arguments->block;
4397 }
4398
4399 return pm_forwarding_super_node_new(
4400 parser->arena,
4401 ++parser->node_id,
4402 0,
4403 (block == NULL) ? PM_LOCATION_INIT_TOKEN(parser, token) : PM_LOCATION_INIT_TOKEN_NODE(parser, token, block),
4404 PM_LOCATION_INIT_TOKEN(parser, token),
4405 block
4406 );
4407}
4408
4413static pm_hash_pattern_node_t *
4414pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
4415 return pm_hash_pattern_node_new(
4416 parser->arena,
4417 ++parser->node_id,
4418 0,
4419 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
4420 NULL,
4421 ((pm_node_list_t) { 0 }),
4422 NULL,
4423 TOK2LOC(parser, opening),
4424 TOK2LOC(parser, closing)
4425 );
4426}
4427
4431static pm_hash_pattern_node_t *
4432pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *elements, pm_node_t *rest) {
4433 uint32_t start;
4434 uint32_t end;
4435
4436 if (elements->size > 0) {
4437 if (rest) {
4438 start = MIN(PM_NODE_START(rest), PM_NODE_START(elements->nodes[0]));
4439 end = MAX(PM_NODE_END(rest), PM_NODE_END(elements->nodes[elements->size - 1]));
4440 } else {
4441 start = PM_NODE_START(elements->nodes[0]);
4442 end = PM_NODE_END(elements->nodes[elements->size - 1]);
4443 }
4444 } else {
4445 assert(rest != NULL);
4446 start = PM_NODE_START(rest);
4447 end = PM_NODE_END(rest);
4448 }
4449
4450 pm_hash_pattern_node_t *node = pm_hash_pattern_node_new(
4451 parser->arena,
4452 ++parser->node_id,
4453 0,
4454 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4455 NULL,
4456 ((pm_node_list_t) { 0 }),
4457 rest,
4458 ((pm_location_t) { 0 }),
4459 ((pm_location_t) { 0 })
4460 );
4461
4462 pm_node_list_concat(parser->arena, &node->elements, elements);
4463 return node;
4464}
4465
4469static pm_constant_id_t
4470pm_global_variable_write_name(pm_parser_t *parser, const pm_node_t *target) {
4471 switch (PM_NODE_TYPE(target)) {
4472 case PM_GLOBAL_VARIABLE_READ_NODE:
4473 return ((pm_global_variable_read_node_t *) target)->name;
4474 case PM_BACK_REFERENCE_READ_NODE:
4475 return ((pm_back_reference_read_node_t *) target)->name;
4476 case PM_NUMBERED_REFERENCE_READ_NODE:
4477 // This will only ever happen in the event of a syntax error, but we
4478 // still need to provide something for the node.
4479 return pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target));
4480 default:
4481 assert(false && "unreachable");
4482 return (pm_constant_id_t) -1;
4483 }
4484}
4485
4489static pm_global_variable_and_write_node_t *
4490pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4491 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4492
4493 return pm_global_variable_and_write_node_new(
4494 parser->arena,
4495 ++parser->node_id,
4496 0,
4497 PM_LOCATION_INIT_NODES(target, value),
4498 pm_global_variable_write_name(parser, target),
4499 target->location,
4500 TOK2LOC(parser, operator),
4501 value
4502 );
4503}
4504
4508static pm_global_variable_operator_write_node_t *
4509pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4510 return pm_global_variable_operator_write_node_new(
4511 parser->arena,
4512 ++parser->node_id,
4513 0,
4514 PM_LOCATION_INIT_NODES(target, value),
4515 pm_global_variable_write_name(parser, target),
4516 target->location,
4517 TOK2LOC(parser, operator),
4518 value,
4519 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
4520 );
4521}
4522
4526static pm_global_variable_or_write_node_t *
4527pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4528 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4529
4530 return pm_global_variable_or_write_node_new(
4531 parser->arena,
4532 ++parser->node_id,
4533 0,
4534 PM_LOCATION_INIT_NODES(target, value),
4535 pm_global_variable_write_name(parser, target),
4536 target->location,
4537 TOK2LOC(parser, operator),
4538 value
4539 );
4540}
4541
4545static pm_global_variable_read_node_t *
4546pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
4547 return pm_global_variable_read_node_new(
4548 parser->arena,
4549 ++parser->node_id,
4550 0,
4551 PM_LOCATION_INIT_TOKEN(parser, name),
4552 pm_parser_constant_id_token(parser, name)
4553 );
4554}
4555
4559static pm_global_variable_read_node_t *
4560pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name) {
4561 return pm_global_variable_read_node_new(
4562 parser->arena,
4563 ++parser->node_id,
4564 0,
4565 PM_LOCATION_INIT_UNSET,
4566 name
4567 );
4568}
4569
4573static pm_global_variable_write_node_t *
4574pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4575 return pm_global_variable_write_node_new(
4576 parser->arena,
4577 ++parser->node_id,
4578 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
4579 PM_LOCATION_INIT_NODES(target, value),
4580 pm_global_variable_write_name(parser, target),
4581 target->location,
4582 value,
4583 TOK2LOC(parser, operator)
4584 );
4585}
4586
4590static pm_global_variable_write_node_t *
4591pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name, pm_node_t *value) {
4592 return pm_global_variable_write_node_new(
4593 parser->arena,
4594 ++parser->node_id,
4595 0,
4596 PM_LOCATION_INIT_UNSET,
4597 name,
4598 ((pm_location_t) { 0 }),
4599 value,
4600 ((pm_location_t) { 0 })
4601 );
4602}
4603
4607static pm_hash_node_t *
4608pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) {
4609 assert(opening != NULL);
4610
4611 return pm_hash_node_new(
4612 parser->arena,
4613 ++parser->node_id,
4614 PM_NODE_FLAG_STATIC_LITERAL,
4615 PM_LOCATION_INIT_TOKEN(parser, opening),
4616 TOK2LOC(parser, opening),
4617 ((pm_node_list_t) { 0 }),
4618 ((pm_location_t) { 0 })
4619 );
4620}
4621
4625static PRISM_INLINE void
4626pm_hash_node_elements_append(pm_arena_t *arena, pm_hash_node_t *hash, pm_node_t *element) {
4627 pm_node_list_append(arena, &hash->elements, element);
4628
4629 bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE);
4630 if (static_literal) {
4631 pm_assoc_node_t *assoc = (pm_assoc_node_t *) element;
4632 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);
4633 static_literal = static_literal && PM_NODE_FLAG_P(assoc->key, PM_NODE_FLAG_STATIC_LITERAL);
4634 static_literal = static_literal && PM_NODE_FLAG_P(assoc, PM_NODE_FLAG_STATIC_LITERAL);
4635 }
4636
4637 if (!static_literal) {
4638 pm_node_flag_unset(UP(hash), PM_NODE_FLAG_STATIC_LITERAL);
4639 }
4640}
4641
4642static PRISM_INLINE void
4643pm_hash_node_closing_loc_set(const pm_parser_t *parser, pm_hash_node_t *hash, pm_token_t *token) {
4644 PM_NODE_LENGTH_SET_TOKEN(parser, hash, token);
4645 hash->closing_loc = TOK2LOC(parser, token);
4646}
4647
4651static pm_if_node_t *
4652pm_if_node_create(pm_parser_t *parser,
4653 const pm_token_t *if_keyword,
4654 pm_node_t *predicate,
4655 const pm_token_t *then_keyword,
4656 pm_statements_node_t *statements,
4657 pm_node_t *subsequent,
4658 const pm_token_t *end_keyword
4659) {
4660 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4661
4662 uint32_t start = PM_TOKEN_START(parser, if_keyword);
4663 uint32_t end;
4664
4665 if (end_keyword != NULL) {
4666 end = PM_TOKEN_END(parser, end_keyword);
4667 } else if (subsequent != NULL) {
4668 end = PM_NODE_END(subsequent);
4669 } else if (pm_statements_node_body_length(statements) != 0) {
4670 end = PM_NODE_END(statements);
4671 } else {
4672 end = PM_NODE_END(predicate);
4673 }
4674
4675 return pm_if_node_new(
4676 parser->arena,
4677 ++parser->node_id,
4678 PM_NODE_FLAG_NEWLINE,
4679 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4680 TOK2LOC(parser, if_keyword),
4681 predicate,
4682 NTOK2LOC(parser, then_keyword),
4683 statements,
4684 subsequent,
4685 NTOK2LOC(parser, end_keyword)
4686 );
4687}
4688
4692static pm_if_node_t *
4693pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *if_keyword, pm_node_t *predicate) {
4694 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4695
4696 pm_statements_node_t *statements = pm_statements_node_create(parser);
4697 pm_statements_node_body_append(parser, statements, statement, true);
4698
4699 return pm_if_node_new(
4700 parser->arena,
4701 ++parser->node_id,
4702 PM_NODE_FLAG_NEWLINE,
4703 PM_LOCATION_INIT_NODES(statement, predicate),
4704 TOK2LOC(parser, if_keyword),
4705 predicate,
4706 ((pm_location_t) { 0 }),
4707 statements,
4708 NULL,
4709 ((pm_location_t) { 0 })
4710 );
4711}
4712
4716static pm_if_node_t *
4717pm_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) {
4718 pm_assert_value_expression(parser, predicate);
4719 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4720
4721 pm_statements_node_t *if_statements = pm_statements_node_create(parser);
4722 pm_statements_node_body_append(parser, if_statements, true_expression, true);
4723
4724 pm_statements_node_t *else_statements = pm_statements_node_create(parser);
4725 pm_statements_node_body_append(parser, else_statements, false_expression, true);
4726
4727 pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, NULL);
4728 return pm_if_node_new(
4729 parser->arena,
4730 ++parser->node_id,
4731 PM_NODE_FLAG_NEWLINE,
4732 PM_LOCATION_INIT_NODES(predicate, false_expression),
4733 ((pm_location_t) { 0 }),
4734 predicate,
4735 TOK2LOC(parser, qmark),
4736 if_statements,
4737 UP(else_node),
4738 ((pm_location_t) { 0 })
4739 );
4740}
4741
4742static PRISM_INLINE void
4743pm_if_node_end_keyword_loc_set(const pm_parser_t *parser, pm_if_node_t *node, const pm_token_t *keyword) {
4744 PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword);
4745 node->end_keyword_loc = TOK2LOC(parser, keyword);
4746}
4747
4748static PRISM_INLINE void
4749pm_else_node_end_keyword_loc_set(const pm_parser_t *parser, pm_else_node_t *node, const pm_token_t *keyword) {
4750 PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword);
4751 node->end_keyword_loc = TOK2LOC(parser, keyword);
4752}
4753
4757static pm_implicit_node_t *
4758pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) {
4759 return pm_implicit_node_new(
4760 parser->arena,
4761 ++parser->node_id,
4762 0,
4763 PM_LOCATION_INIT_NODE(value),
4764 value
4765 );
4766}
4767
4771static pm_implicit_rest_node_t *
4772pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) {
4773 assert(token->type == PM_TOKEN_COMMA);
4774
4775 return pm_implicit_rest_node_new(
4776 parser->arena,
4777 ++parser->node_id,
4778 0,
4779 PM_LOCATION_INIT_TOKEN(parser, token)
4780 );
4781}
4782
4786static pm_integer_node_t *
4787pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4788 assert(token->type == PM_TOKEN_INTEGER);
4789
4790 pm_integer_node_t *node = pm_integer_node_new(
4791 parser->arena,
4792 ++parser->node_id,
4793 base | PM_NODE_FLAG_STATIC_LITERAL,
4794 PM_LOCATION_INIT_TOKEN(parser, token),
4795 ((pm_integer_t) { 0 })
4796 );
4797
4798 if (parser->integer.lexed) {
4799 // The value was already computed during lexing.
4800 node->value.value = parser->integer.value;
4801 parser->integer.lexed = false;
4802 } else {
4803 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4804 switch (base) {
4805 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4806 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4807 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4808 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4809 default: assert(false && "unreachable"); break;
4810 }
4811
4812 pm_integer_parse(&node->value, integer_base, token->start, token->end);
4813 pm_integer_arena_move(parser->arena, &node->value);
4814 }
4815
4816 return node;
4817}
4818
4823static pm_imaginary_node_t *
4824pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4825 assert(token->type == PM_TOKEN_INTEGER_IMAGINARY);
4826
4827 return pm_imaginary_node_new(
4828 parser->arena,
4829 ++parser->node_id,
4830 PM_NODE_FLAG_STATIC_LITERAL,
4831 PM_LOCATION_INIT_TOKEN(parser, token),
4832 UP(pm_integer_node_create(parser, base, &((pm_token_t) {
4833 .type = PM_TOKEN_INTEGER,
4834 .start = token->start,
4835 .end = token->end - 1
4836 })))
4837 );
4838}
4839
4844static pm_rational_node_t *
4845pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4846 assert(token->type == PM_TOKEN_INTEGER_RATIONAL);
4847
4848 pm_rational_node_t *node = pm_rational_node_new(
4849 parser->arena,
4850 ++parser->node_id,
4851 base | PM_NODE_FLAG_STATIC_LITERAL,
4852 PM_LOCATION_INIT_TOKEN(parser, token),
4853 ((pm_integer_t) { 0 }),
4854 ((pm_integer_t) { .value = 1 })
4855 );
4856
4857 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4858 switch (base) {
4859 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4860 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4861 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4862 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4863 default: assert(false && "unreachable"); break;
4864 }
4865
4866 pm_integer_parse(&node->numerator, integer_base, token->start, token->end - 1);
4867 pm_integer_arena_move(parser->arena, &node->numerator);
4868
4869 return node;
4870}
4871
4876static pm_imaginary_node_t *
4877pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4878 assert(token->type == PM_TOKEN_INTEGER_RATIONAL_IMAGINARY);
4879
4880 return pm_imaginary_node_new(
4881 parser->arena,
4882 ++parser->node_id,
4883 PM_NODE_FLAG_STATIC_LITERAL,
4884 PM_LOCATION_INIT_TOKEN(parser, token),
4885 UP(pm_integer_node_rational_create(parser, base, &((pm_token_t) {
4886 .type = PM_TOKEN_INTEGER_RATIONAL,
4887 .start = token->start,
4888 .end = token->end - 1
4889 })))
4890 );
4891}
4892
4896static pm_in_node_t *
4897pm_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) {
4898 uint32_t start = PM_TOKEN_START(parser, in_keyword);
4899 uint32_t end;
4900
4901 if (statements != NULL) {
4902 end = PM_NODE_END(statements);
4903 } else if (then_keyword != NULL) {
4904 end = PM_TOKEN_END(parser, then_keyword);
4905 } else {
4906 end = PM_NODE_END(pattern);
4907 }
4908
4909 return pm_in_node_new(
4910 parser->arena,
4911 ++parser->node_id,
4912 0,
4913 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4914 pattern,
4915 statements,
4916 TOK2LOC(parser, in_keyword),
4917 NTOK2LOC(parser, then_keyword)
4918 );
4919}
4920
4924static pm_instance_variable_and_write_node_t *
4925pm_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) {
4926 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4927
4928 return pm_instance_variable_and_write_node_new(
4929 parser->arena,
4930 ++parser->node_id,
4931 0,
4932 PM_LOCATION_INIT_NODES(target, value),
4933 target->name,
4934 target->base.location,
4935 TOK2LOC(parser, operator),
4936 value
4937 );
4938}
4939
4943static pm_instance_variable_operator_write_node_t *
4944pm_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) {
4945 return pm_instance_variable_operator_write_node_new(
4946 parser->arena,
4947 ++parser->node_id,
4948 0,
4949 PM_LOCATION_INIT_NODES(target, value),
4950 target->name,
4951 target->base.location,
4952 TOK2LOC(parser, operator),
4953 value,
4954 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
4955 );
4956}
4957
4961static pm_instance_variable_or_write_node_t *
4962pm_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) {
4963 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4964
4965 return pm_instance_variable_or_write_node_new(
4966 parser->arena,
4967 ++parser->node_id,
4968 0,
4969 PM_LOCATION_INIT_NODES(target, value),
4970 target->name,
4971 target->base.location,
4972 TOK2LOC(parser, operator),
4973 value
4974 );
4975}
4976
4980static pm_instance_variable_read_node_t *
4981pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
4982 assert(token->type == PM_TOKEN_INSTANCE_VARIABLE);
4983
4984 return pm_instance_variable_read_node_new(
4985 parser->arena,
4986 ++parser->node_id,
4987 0,
4988 PM_LOCATION_INIT_TOKEN(parser, token),
4989 pm_parser_constant_id_token(parser, token)
4990 );
4991}
4992
4997static pm_instance_variable_write_node_t *
4998pm_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) {
4999 return pm_instance_variable_write_node_new(
5000 parser->arena,
5001 ++parser->node_id,
5002 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5003 PM_LOCATION_INIT_NODES(read_node, value),
5004 read_node->name,
5005 read_node->base.location,
5006 value,
5007 TOK2LOC(parser, operator)
5008 );
5009}
5010
5016static void
5017pm_interpolated_node_append(pm_arena_t *arena, pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) {
5018 switch (PM_NODE_TYPE(part)) {
5019 case PM_STRING_NODE:
5020 pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
5021 break;
5022 case PM_EMBEDDED_STATEMENTS_NODE: {
5023 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
5024 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
5025
5026 if (embedded == NULL) {
5027 // If there are no statements or more than one statement, then
5028 // we lose the static literal flag.
5029 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
5030 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
5031 // If the embedded statement is a string, then we can keep the
5032 // static literal flag and mark the string as frozen.
5033 pm_node_flag_set(embedded, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
5034 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
5035 // If the embedded statement is an interpolated string and it's
5036 // a static literal, then we can keep the static literal flag.
5037 } else {
5038 // Otherwise we lose the static literal flag.
5039 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
5040 }
5041
5042 break;
5043 }
5044 case PM_EMBEDDED_VARIABLE_NODE:
5045 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
5046 break;
5047 default:
5048 assert(false && "unexpected node type");
5049 break;
5050 }
5051
5052 pm_node_list_append(arena, parts, part);
5053}
5054
5058static pm_interpolated_regular_expression_node_t *
5059pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening) {
5060 return pm_interpolated_regular_expression_node_new(
5061 parser->arena,
5062 ++parser->node_id,
5063 PM_NODE_FLAG_STATIC_LITERAL,
5064 PM_LOCATION_INIT_TOKEN(parser, opening),
5065 TOK2LOC(parser, opening),
5066 ((pm_node_list_t) { 0 }),
5067 TOK2LOC(parser, opening)
5068 );
5069}
5070
5071static PRISM_INLINE void
5072pm_interpolated_regular_expression_node_append(pm_arena_t *arena, pm_interpolated_regular_expression_node_t *node, pm_node_t *part) {
5073 if (PM_NODE_START(node) > PM_NODE_START(part)) {
5074 PM_NODE_START_SET_NODE(node, part);
5075 }
5076 if (PM_NODE_END(node) < PM_NODE_END(part)) {
5077 PM_NODE_LENGTH_SET_NODE(node, part);
5078 }
5079
5080 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5081}
5082
5083static PRISM_INLINE void
5084pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) {
5085 node->closing_loc = TOK2LOC(parser, closing);
5086 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5087 pm_node_flag_set(UP(node), pm_regular_expression_flags_create(parser, closing));
5088}
5089
5113static PRISM_INLINE void
5114pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_node_t *node, pm_node_t *part) {
5115 pm_arena_t *arena = parser->arena;
5116#define CLEAR_FLAGS(node) \
5117 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))
5118
5119#define MUTABLE_FLAGS(node) \
5120 node->base.flags = (pm_node_flags_t) ((FL(node) | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN);
5121
5122 if (node->parts.size == 0 && node->opening_loc.length == 0) {
5123 PM_NODE_START_SET_NODE(node, part);
5124 }
5125
5126 if (PM_NODE_END(part) > PM_NODE_END(node)) {
5127 PM_NODE_LENGTH_SET_NODE(node, part);
5128 }
5129
5130 switch (PM_NODE_TYPE(part)) {
5131 case PM_STRING_NODE:
5132 // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags,
5133 // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for
5134 // as long as this interpolation only consists of other string literals.
5135 if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
5136 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
5137 }
5138 part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5139 break;
5140 case PM_INTERPOLATED_STRING_NODE:
5141 if (PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
5142 // If the string that we're concatenating is a static literal,
5143 // then we can keep the static literal flag for this string.
5144 } else {
5145 // Otherwise, we lose the static literal flag here and we should
5146 // also clear the mutability flags.
5147 CLEAR_FLAGS(node);
5148 }
5149 break;
5150 case PM_EMBEDDED_STATEMENTS_NODE: {
5151 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
5152 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
5153
5154 if (embedded == NULL) {
5155 // If we're embedding multiple statements or no statements, then
5156 // the string is not longer a static literal.
5157 CLEAR_FLAGS(node);
5158 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
5159 // If the embedded statement is a string, then we can make that
5160 // string as frozen and static literal, and not touch the static
5161 // literal status of this string.
5162 embedded->flags = (pm_node_flags_t) ((embedded->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5163
5164 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
5165 MUTABLE_FLAGS(node);
5166 }
5167 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
5168 // If the embedded statement is an interpolated string, but that
5169 // string is marked as static literal, then we can keep our
5170 // static literal status for this string.
5171 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
5172 MUTABLE_FLAGS(node);
5173 }
5174 } else {
5175 // In all other cases, we lose the static literal flag here and
5176 // become mutable.
5177 CLEAR_FLAGS(node);
5178 }
5179
5180 break;
5181 }
5182 case PM_EMBEDDED_VARIABLE_NODE:
5183 // Embedded variables clear static literal, which means we also
5184 // should clear the mutability flags.
5185 CLEAR_FLAGS(node);
5186 break;
5187 case PM_X_STRING_NODE:
5188 case PM_INTERPOLATED_X_STRING_NODE:
5189 case PM_SYMBOL_NODE:
5190 case PM_INTERPOLATED_SYMBOL_NODE:
5191 // These will only happen in error cases. But we want to handle it
5192 // here so that we don't fail the assertion.
5193 CLEAR_FLAGS(node);
5194 pm_node_list_append(arena, &node->parts, UP(pm_error_recovery_node_create_unexpected(parser, part)));
5195 return;
5196 case PM_ERROR_RECOVERY_NODE:
5197 CLEAR_FLAGS(node);
5198 break;
5199 default:
5200 assert(false && "unexpected node type");
5201 break;
5202 }
5203
5204 pm_node_list_append(arena, &node->parts, part);
5205
5206#undef CLEAR_FLAGS
5207#undef MUTABLE_FLAGS
5208}
5209
5213static pm_interpolated_string_node_t *
5214pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
5215 pm_node_flags_t flags = PM_NODE_FLAG_STATIC_LITERAL;
5216
5217 switch (parser->frozen_string_literal) {
5218 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
5219 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE;
5220 break;
5221 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
5222 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN;
5223 break;
5224 }
5225
5226 uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening);
5227 uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing);
5228
5229 pm_interpolated_string_node_t *node = pm_interpolated_string_node_new(
5230 parser->arena,
5231 ++parser->node_id,
5232 flags,
5233 ((pm_location_t) { .start = start, .length = U32(end - start) }),
5234 NTOK2LOC(parser, opening),
5235 ((pm_node_list_t) { 0 }),
5236 NTOK2LOC(parser, closing)
5237 );
5238
5239 if (parts != NULL) {
5240 pm_node_t *part;
5241 PM_NODE_LIST_FOREACH(parts, index, part) {
5242 pm_interpolated_string_node_append(parser, node, part);
5243 }
5244 }
5245
5246 return node;
5247}
5248
5252static void
5253pm_interpolated_string_node_closing_set(const pm_parser_t *parser, pm_interpolated_string_node_t *node, const pm_token_t *closing) {
5254 node->closing_loc = TOK2LOC(parser, closing);
5255 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5256}
5257
5258static void
5259pm_interpolated_symbol_node_append(pm_arena_t *arena, pm_interpolated_symbol_node_t *node, pm_node_t *part) {
5260 if (node->parts.size == 0 && node->opening_loc.length == 0) {
5261 PM_NODE_START_SET_NODE(node, part);
5262 }
5263
5264 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5265
5266 if (PM_NODE_END(part) > PM_NODE_END(node)) {
5267 PM_NODE_LENGTH_SET_NODE(node, part);
5268 }
5269}
5270
5271static void
5272pm_interpolated_symbol_node_closing_loc_set(const pm_parser_t *parser, pm_interpolated_symbol_node_t *node, const pm_token_t *closing) {
5273 node->closing_loc = TOK2LOC(parser, closing);
5274 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5275}
5276
5280static pm_interpolated_symbol_node_t *
5281pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
5282 uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening);
5283 uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing);
5284
5285 pm_interpolated_symbol_node_t *node = pm_interpolated_symbol_node_new(
5286 parser->arena,
5287 ++parser->node_id,
5288 PM_NODE_FLAG_STATIC_LITERAL,
5289 ((pm_location_t) { .start = start, .length = U32(end - start) }),
5290 NTOK2LOC(parser, opening),
5291 ((pm_node_list_t) { 0 }),
5292 NTOK2LOC(parser, closing)
5293 );
5294
5295 if (parts != NULL) {
5296 pm_node_t *part;
5297 PM_NODE_LIST_FOREACH(parts, index, part) {
5298 pm_interpolated_symbol_node_append(parser->arena, node, part);
5299 }
5300 }
5301
5302 return node;
5303}
5304
5308static pm_interpolated_x_string_node_t *
5309pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
5310 return pm_interpolated_x_string_node_new(
5311 parser->arena,
5312 ++parser->node_id,
5313 0,
5314 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
5315 TOK2LOC(parser, opening),
5316 ((pm_node_list_t) { 0 }),
5317 TOK2LOC(parser, closing)
5318 );
5319}
5320
5321static PRISM_INLINE void
5322pm_interpolated_xstring_node_append(pm_arena_t *arena, pm_interpolated_x_string_node_t *node, pm_node_t *part) {
5323 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5324 PM_NODE_LENGTH_SET_NODE(node, part);
5325}
5326
5327static PRISM_INLINE void
5328pm_interpolated_xstring_node_closing_set(const pm_parser_t *parser, pm_interpolated_x_string_node_t *node, const pm_token_t *closing) {
5329 node->closing_loc = TOK2LOC(parser, closing);
5330 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5331}
5332
5336static pm_it_local_variable_read_node_t *
5337pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
5338 return pm_it_local_variable_read_node_new(
5339 parser->arena,
5340 ++parser->node_id,
5341 0,
5342 PM_LOCATION_INIT_TOKEN(parser, name)
5343 );
5344}
5345
5349static pm_it_parameters_node_t *
5350pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
5351 return pm_it_parameters_node_new(
5352 parser->arena,
5353 ++parser->node_id,
5354 0,
5355 PM_LOCATION_INIT_TOKENS(parser, opening, closing)
5356 );
5357}
5358
5362static pm_keyword_hash_node_t *
5363pm_keyword_hash_node_create(pm_parser_t *parser) {
5364 return pm_keyword_hash_node_new(
5365 parser->arena,
5366 ++parser->node_id,
5367 PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS,
5368 PM_LOCATION_INIT_UNSET,
5369 ((pm_node_list_t) { 0 })
5370 );
5371}
5372
5376static void
5377pm_keyword_hash_node_elements_append(pm_arena_t *arena, pm_keyword_hash_node_t *hash, pm_node_t *element) {
5378 // If the element being added is not an AssocNode or does not have a symbol
5379 // key, then we want to turn the SYMBOL_KEYS flag off.
5380 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_TYPE_P(((pm_assoc_node_t *) element)->key, PM_SYMBOL_NODE)) {
5381 pm_node_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS);
5382 }
5383
5384 pm_node_list_append(arena, &hash->elements, element);
5385 if (PM_NODE_LENGTH(hash) == 0) {
5386 PM_NODE_START_SET_NODE(hash, element);
5387 }
5388 PM_NODE_LENGTH_SET_NODE(hash, element);
5389}
5390
5394static pm_required_keyword_parameter_node_t *
5395pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name) {
5396 return pm_required_keyword_parameter_node_new(
5397 parser->arena,
5398 ++parser->node_id,
5399 0,
5400 PM_LOCATION_INIT_TOKEN(parser, name),
5401 pm_parser_constant_id_raw(parser, name->start, name->end - 1),
5402 TOK2LOC(parser, name)
5403 );
5404}
5405
5409static pm_optional_keyword_parameter_node_t *
5410pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, pm_node_t *value) {
5411 return pm_optional_keyword_parameter_node_new(
5412 parser->arena,
5413 ++parser->node_id,
5414 0,
5415 PM_LOCATION_INIT_TOKEN_NODE(parser, name, value),
5416 pm_parser_constant_id_raw(parser, name->start, name->end - 1),
5417 TOK2LOC(parser, name),
5418 value
5419 );
5420}
5421
5425static pm_keyword_rest_parameter_node_t *
5426pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
5427 return pm_keyword_rest_parameter_node_new(
5428 parser->arena,
5429 ++parser->node_id,
5430 0,
5431 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
5432 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
5433 NTOK2LOC(parser, name),
5434 TOK2LOC(parser, operator)
5435 );
5436}
5437
5441static pm_lambda_node_t *
5442pm_lambda_node_create(
5443 pm_parser_t *parser,
5444 pm_constant_id_list_t *locals,
5445 const pm_token_t *operator,
5446 const pm_token_t *opening,
5447 const pm_token_t *closing,
5448 pm_node_t *parameters,
5449 pm_node_t *body
5450) {
5451 return pm_lambda_node_new(
5452 parser->arena,
5453 ++parser->node_id,
5454 0,
5455 PM_LOCATION_INIT_TOKENS(parser, operator, closing),
5456 *locals,
5457 TOK2LOC(parser, operator),
5458 TOK2LOC(parser, opening),
5459 TOK2LOC(parser, closing),
5460 parameters,
5461 body
5462 );
5463}
5464
5468static pm_local_variable_and_write_node_t *
5469pm_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) {
5470 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));
5471 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
5472
5473 return pm_local_variable_and_write_node_new(
5474 parser->arena,
5475 ++parser->node_id,
5476 0,
5477 PM_LOCATION_INIT_NODES(target, value),
5478 target->location,
5479 TOK2LOC(parser, operator),
5480 value,
5481 name,
5482 depth
5483 );
5484}
5485
5489static pm_local_variable_operator_write_node_t *
5490pm_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) {
5491 return pm_local_variable_operator_write_node_new(
5492 parser->arena,
5493 ++parser->node_id,
5494 0,
5495 PM_LOCATION_INIT_NODES(target, value),
5496 target->location,
5497 TOK2LOC(parser, operator),
5498 value,
5499 name,
5500 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
5501 depth
5502 );
5503}
5504
5508static pm_local_variable_or_write_node_t *
5509pm_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) {
5510 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));
5511 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
5512
5513 return pm_local_variable_or_write_node_new(
5514 parser->arena,
5515 ++parser->node_id,
5516 0,
5517 PM_LOCATION_INIT_NODES(target, value),
5518 target->location,
5519 TOK2LOC(parser, operator),
5520 value,
5521 name,
5522 depth
5523 );
5524}
5525
5529static pm_local_variable_read_node_t *
5530pm_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) {
5531 if (!missing) pm_locals_read(&pm_parser_scope_find(parser, depth)->locals, name_id);
5532
5533 return pm_local_variable_read_node_new(
5534 parser->arena,
5535 ++parser->node_id,
5536 0,
5537 PM_LOCATION_INIT_TOKEN(parser, name),
5538 name_id,
5539 depth
5540 );
5541}
5542
5546static pm_local_variable_read_node_t *
5547pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5548 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5549 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, false);
5550}
5551
5556static pm_local_variable_read_node_t *
5557pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5558 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5559 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, true);
5560}
5561
5565static pm_local_variable_write_node_t *
5566pm_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) {
5567 return pm_local_variable_write_node_new(
5568 parser->arena,
5569 ++parser->node_id,
5570 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5571 ((pm_location_t) { .start = name_loc->start, .length = PM_NODE_END(value) - name_loc->start }),
5572 name,
5573 depth,
5574 *name_loc,
5575 value,
5576 TOK2LOC(parser, operator)
5577 );
5578}
5579
5583static PRISM_INLINE bool
5584pm_token_is_it(const uint8_t *start, const uint8_t *end) {
5585 return (end - start == 2) && (start[0] == 'i') && (start[1] == 't');
5586}
5587
5592static PRISM_INLINE bool
5593pm_token_is_numbered_parameter(const pm_parser_t *parser, uint32_t start, uint32_t length) {
5594 return (
5595 (length == 2) &&
5596 (parser->start[start] == '_') &&
5597 (parser->start[start + 1] != '0') &&
5598 pm_char_is_decimal_digit(parser->start[start + 1])
5599 );
5600}
5601
5606static PRISM_INLINE void
5607pm_refute_numbered_parameter(pm_parser_t *parser, uint32_t start, uint32_t length) {
5608 if (pm_token_is_numbered_parameter(parser, start, length)) {
5609 PM_PARSER_ERR_FORMAT(parser, start, length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + start);
5610 }
5611}
5612
5617static pm_local_variable_target_node_t *
5618pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *location, pm_constant_id_t name, uint32_t depth) {
5619 pm_refute_numbered_parameter(parser, location->start, location->length);
5620
5621 return pm_local_variable_target_node_new(
5622 parser->arena,
5623 ++parser->node_id,
5624 0,
5625 ((pm_location_t) { .start = location->start, .length = location->length }),
5626 name,
5627 depth
5628 );
5629}
5630
5634static pm_match_predicate_node_t *
5635pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5636 pm_assert_value_expression(parser, value);
5637
5638 return pm_match_predicate_node_new(
5639 parser->arena,
5640 ++parser->node_id,
5641 0,
5642 PM_LOCATION_INIT_NODES(value, pattern),
5643 value,
5644 pattern,
5645 TOK2LOC(parser, operator)
5646 );
5647}
5648
5652static pm_match_required_node_t *
5653pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5654 pm_assert_value_expression(parser, value);
5655
5656 return pm_match_required_node_new(
5657 parser->arena,
5658 ++parser->node_id,
5659 0,
5660 PM_LOCATION_INIT_NODES(value, pattern),
5661 value,
5662 pattern,
5663 TOK2LOC(parser, operator)
5664 );
5665}
5666
5670static pm_match_write_node_t *
5671pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) {
5672 return pm_match_write_node_new(
5673 parser->arena,
5674 ++parser->node_id,
5675 0,
5676 PM_LOCATION_INIT_NODE(call),
5677 call,
5678 ((pm_node_list_t) { 0 })
5679 );
5680}
5681
5685static pm_module_node_t *
5686pm_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) {
5687 pm_constant_id_list_t module_locals = { .ids = NULL, .size = 0, .capacity = 0 };
5688 if (locals != NULL) module_locals = *locals;
5689
5690 return pm_module_node_new(
5691 parser->arena,
5692 ++parser->node_id,
5693 0,
5694 PM_LOCATION_INIT_TOKENS(parser, module_keyword, end_keyword),
5695 module_locals,
5696 TOK2LOC(parser, module_keyword),
5697 constant_path,
5698 body,
5699 TOK2LOC(parser, end_keyword),
5700 pm_parser_constant_id_token(parser, name)
5701 );
5702}
5703
5707static pm_multi_target_node_t *
5708pm_multi_target_node_create(pm_parser_t *parser) {
5709 return pm_multi_target_node_new(
5710 parser->arena,
5711 ++parser->node_id,
5712 0,
5713 PM_LOCATION_INIT_UNSET,
5714 ((pm_node_list_t) { 0 }),
5715 NULL,
5716 ((pm_node_list_t) { 0 }),
5717 ((pm_location_t) { 0 }),
5718 ((pm_location_t) { 0 })
5719 );
5720}
5721
5725static void
5726pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t *node, pm_node_t *target) {
5727 if (PM_NODE_TYPE_P(target, PM_SPLAT_NODE)) {
5728 if (node->rest == NULL) {
5729 node->rest = target;
5730 } else {
5731 pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
5732 pm_node_list_append(parser->arena, &node->rights, target);
5733 }
5734 } else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
5735 if (node->rest == NULL) {
5736 node->rest = target;
5737 } else {
5738 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
5739 pm_node_list_append(parser->arena, &node->rights, target);
5740 }
5741 } else if (node->rest == NULL) {
5742 pm_node_list_append(parser->arena, &node->lefts, target);
5743 } else {
5744 pm_node_list_append(parser->arena, &node->rights, target);
5745 }
5746
5747 if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_START(node) > PM_NODE_START(target))) {
5748 PM_NODE_START_SET_NODE(node, target);
5749 }
5750
5751 if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_END(node) < PM_NODE_END(target))) {
5752 PM_NODE_LENGTH_SET_NODE(node, target);
5753 }
5754}
5755
5759static void
5760pm_multi_target_node_opening_set(const pm_parser_t *parser, pm_multi_target_node_t *node, const pm_token_t *lparen) {
5761 PM_NODE_START_SET_TOKEN(parser, node, lparen);
5762 PM_NODE_LENGTH_SET_TOKEN(parser, node, lparen);
5763 node->lparen_loc = TOK2LOC(parser, lparen);
5764}
5765
5769static void
5770pm_multi_target_node_closing_set(const pm_parser_t *parser, pm_multi_target_node_t *node, const pm_token_t *rparen) {
5771 PM_NODE_LENGTH_SET_TOKEN(parser, node, rparen);
5772 node->rparen_loc = TOK2LOC(parser, rparen);
5773}
5774
5778static pm_multi_write_node_t *
5779pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, const pm_token_t *operator, pm_node_t *value) {
5780 /* The target is no longer necessary because we have reused its children. It
5781 * is arena-allocated so no explicit free is needed. */
5782 return pm_multi_write_node_new(
5783 parser->arena,
5784 ++parser->node_id,
5785 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5786 PM_LOCATION_INIT_NODES(target, value),
5787 target->lefts,
5788 target->rest,
5789 target->rights,
5790 target->lparen_loc,
5791 target->rparen_loc,
5792 TOK2LOC(parser, operator),
5793 value
5794 );
5795}
5796
5800static pm_next_node_t *
5801pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
5802 assert(keyword->type == PM_TOKEN_KEYWORD_NEXT);
5803
5804 return pm_next_node_new(
5805 parser->arena,
5806 ++parser->node_id,
5807 0,
5808 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
5809 arguments,
5810 TOK2LOC(parser, keyword)
5811 );
5812}
5813
5817static pm_nil_node_t *
5818pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) {
5819 assert(token->type == PM_TOKEN_KEYWORD_NIL);
5820
5821 return pm_nil_node_new(
5822 parser->arena,
5823 ++parser->node_id,
5824 PM_NODE_FLAG_STATIC_LITERAL,
5825 PM_LOCATION_INIT_TOKEN(parser, token)
5826 );
5827}
5828
5832static pm_no_block_parameter_node_t *
5833pm_no_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
5834 assert(operator->type == PM_TOKEN_AMPERSAND || operator->type == PM_TOKEN_UAMPERSAND);
5835 assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
5836
5837 return pm_no_block_parameter_node_new(
5838 parser->arena,
5839 ++parser->node_id,
5840 0,
5841 PM_LOCATION_INIT_TOKENS(parser, operator, keyword),
5842 TOK2LOC(parser, operator),
5843 TOK2LOC(parser, keyword)
5844 );
5845}
5846
5850static pm_no_keywords_parameter_node_t *
5851pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
5852 assert(operator->type == PM_TOKEN_USTAR_STAR || operator->type == PM_TOKEN_STAR_STAR);
5853 assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
5854
5855 return pm_no_keywords_parameter_node_new(
5856 parser->arena,
5857 ++parser->node_id,
5858 0,
5859 PM_LOCATION_INIT_TOKENS(parser, operator, keyword),
5860 TOK2LOC(parser, operator),
5861 TOK2LOC(parser, keyword)
5862 );
5863}
5864
5868static pm_numbered_parameters_node_t *
5869pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing, uint8_t maximum) {
5870 return pm_numbered_parameters_node_new(
5871 parser->arena,
5872 ++parser->node_id,
5873 0,
5874 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
5875 maximum
5876 );
5877}
5878
5883#define NTH_REF_MAX ((uint32_t) (INT_MAX >> 1))
5884
5891static uint32_t
5892pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *token) {
5893 const uint8_t *start = token->start + 1;
5894 const uint8_t *end = token->end;
5895
5896 ptrdiff_t diff = end - start;
5897 assert(diff > 0);
5898#if PTRDIFF_MAX > SIZE_MAX
5899 assert(diff < (ptrdiff_t) SIZE_MAX);
5900#endif
5901 size_t length = (size_t) diff;
5902
5903 char *digits = xcalloc(length + 1, sizeof(char));
5904 memcpy(digits, start, length);
5905 digits[length] = '\0';
5906
5907 char *endptr;
5908 errno = 0;
5909 unsigned long value = strtoul(digits, &endptr, 10);
5910
5911 if ((digits == endptr) || (*endptr != '\0')) {
5912 pm_parser_err(parser, U32(start - parser->start), U32(length), PM_ERR_INVALID_NUMBER_DECIMAL);
5913 value = 0;
5914 }
5915
5916 xfree_sized(digits, sizeof(char) * (length + 1));
5917
5918 if ((errno == ERANGE) || (value > NTH_REF_MAX)) {
5919 PM_PARSER_WARN_FORMAT(parser, U32(start - parser->start), U32(length), PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start);
5920 value = 0;
5921 }
5922
5923 return (uint32_t) value;
5924}
5925
5926#undef NTH_REF_MAX
5927
5931static pm_numbered_reference_read_node_t *
5932pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
5933 assert(name->type == PM_TOKEN_NUMBERED_REFERENCE);
5934
5935 return pm_numbered_reference_read_node_new(
5936 parser->arena,
5937 ++parser->node_id,
5938 0,
5939 PM_LOCATION_INIT_TOKEN(parser, name),
5940 pm_numbered_reference_read_node_number(parser, name)
5941 );
5942}
5943
5947static pm_optional_parameter_node_t *
5948pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator, pm_node_t *value) {
5949 return pm_optional_parameter_node_new(
5950 parser->arena,
5951 ++parser->node_id,
5952 0,
5953 PM_LOCATION_INIT_TOKEN_NODE(parser, name, value),
5954 pm_parser_constant_id_token(parser, name),
5955 TOK2LOC(parser, name),
5956 TOK2LOC(parser, operator),
5957 value
5958 );
5959}
5960
5964static pm_or_node_t *
5965pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
5966 pm_assert_value_expression(parser, left);
5967
5968 return pm_or_node_new(
5969 parser->arena,
5970 ++parser->node_id,
5971 0,
5972 PM_LOCATION_INIT_NODES(left, right),
5973 left,
5974 right,
5975 TOK2LOC(parser, operator)
5976 );
5977}
5978
5982static pm_parameters_node_t *
5983pm_parameters_node_create(pm_parser_t *parser) {
5984 return pm_parameters_node_new(
5985 parser->arena,
5986 ++parser->node_id,
5987 0,
5988 PM_LOCATION_INIT_UNSET,
5989 ((pm_node_list_t) { 0 }),
5990 ((pm_node_list_t) { 0 }),
5991 NULL,
5992 ((pm_node_list_t) { 0 }),
5993 ((pm_node_list_t) { 0 }),
5994 NULL,
5995 NULL
5996 );
5997}
5998
6002static void
6003pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param) {
6004 if ((params->base.location.length == 0) || PM_NODE_START(params) > PM_NODE_START(param)) {
6005 PM_NODE_START_SET_NODE(params, param);
6006 }
6007
6008 if ((params->base.location.length == 0) || (PM_NODE_END(params) < PM_NODE_END(param))) {
6009 PM_NODE_LENGTH_SET_NODE(params, param);
6010 }
6011}
6012
6016static void
6017pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6018 pm_parameters_node_location_set(params, param);
6019 pm_node_list_append(arena, &params->requireds, param);
6020}
6021
6025static void
6026pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_optional_parameter_node_t *param) {
6027 pm_parameters_node_location_set(params, UP(param));
6028 pm_node_list_append(arena, &params->optionals, UP(param));
6029}
6030
6034static void
6035pm_parameters_node_posts_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6036 pm_parameters_node_location_set(params, param);
6037 pm_node_list_append(arena, &params->posts, param);
6038}
6039
6043static void
6044pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
6045 pm_parameters_node_location_set(params, param);
6046 params->rest = param;
6047}
6048
6052static void
6053pm_parameters_node_keywords_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6054 pm_parameters_node_location_set(params, param);
6055 pm_node_list_append(arena, &params->keywords, param);
6056}
6057
6061static void
6062pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
6063 assert(params->keyword_rest == NULL);
6064 pm_parameters_node_location_set(params, param);
6065 params->keyword_rest = param;
6066}
6067
6071static void
6072pm_parameters_node_block_set(pm_parameters_node_t *params, pm_node_t *param) {
6073 assert(params->block == NULL);
6074 pm_parameters_node_location_set(params, param);
6075 params->block = param;
6076}
6077
6081static pm_program_node_t *
6082pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) {
6083 return pm_program_node_new(
6084 parser->arena,
6085 ++parser->node_id,
6086 0,
6087 PM_LOCATION_INIT_NODE(statements),
6088 *locals,
6089 statements
6090 );
6091}
6092
6096static pm_parentheses_node_t *
6097pm_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) {
6098 return pm_parentheses_node_new(
6099 parser->arena,
6100 ++parser->node_id,
6101 flags,
6102 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6103 body,
6104 TOK2LOC(parser, opening),
6105 TOK2LOC(parser, closing)
6106 );
6107}
6108
6112static pm_pinned_expression_node_t *
6113pm_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) {
6114 return pm_pinned_expression_node_new(
6115 parser->arena,
6116 ++parser->node_id,
6117 0,
6118 PM_LOCATION_INIT_TOKENS(parser, operator, rparen),
6119 expression,
6120 TOK2LOC(parser, operator),
6121 TOK2LOC(parser, lparen),
6122 TOK2LOC(parser, rparen)
6123 );
6124}
6125
6129static pm_pinned_variable_node_t *
6130pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
6131 return pm_pinned_variable_node_new(
6132 parser->arena,
6133 ++parser->node_id,
6134 0,
6135 PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable),
6136 variable,
6137 TOK2LOC(parser, operator)
6138 );
6139}
6140
6144static pm_post_execution_node_t *
6145pm_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) {
6146 return pm_post_execution_node_new(
6147 parser->arena,
6148 ++parser->node_id,
6149 0,
6150 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
6151 statements,
6152 TOK2LOC(parser, keyword),
6153 TOK2LOC(parser, opening),
6154 TOK2LOC(parser, closing)
6155 );
6156}
6157
6161static pm_pre_execution_node_t *
6162pm_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) {
6163 return pm_pre_execution_node_new(
6164 parser->arena,
6165 ++parser->node_id,
6166 0,
6167 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
6168 statements,
6169 TOK2LOC(parser, keyword),
6170 TOK2LOC(parser, opening),
6171 TOK2LOC(parser, closing)
6172 );
6173}
6174
6178static pm_range_node_t *
6179pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
6180 pm_assert_value_expression(parser, left);
6181 pm_assert_value_expression(parser, right);
6182 pm_node_flags_t flags = 0;
6183
6184 // Indicate that this node is an exclusive range if the operator is `...`.
6185 if (operator->type == PM_TOKEN_DOT_DOT_DOT || operator->type == PM_TOKEN_UDOT_DOT_DOT) {
6186 flags |= PM_RANGE_FLAGS_EXCLUDE_END;
6187 }
6188
6189 // Indicate that this node is a static literal (i.e., can be compiled with
6190 // a putobject in CRuby) if the left and right are implicit nil, explicit
6191 // nil, or integers.
6192 if (
6193 (left == NULL || PM_NODE_TYPE_P(left, PM_NIL_NODE) || PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) &&
6194 (right == NULL || PM_NODE_TYPE_P(right, PM_NIL_NODE) || PM_NODE_TYPE_P(right, PM_INTEGER_NODE))
6195 ) {
6196 flags |= PM_NODE_FLAG_STATIC_LITERAL;
6197 }
6198
6199 uint32_t start = left == NULL ? PM_TOKEN_START(parser, operator) : PM_NODE_START(left);
6200 uint32_t end = right == NULL ? PM_TOKEN_END(parser, operator) : PM_NODE_END(right);
6201
6202 return pm_range_node_new(
6203 parser->arena,
6204 ++parser->node_id,
6205 flags,
6206 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6207 left,
6208 right,
6209 TOK2LOC(parser, operator)
6210 );
6211}
6212
6216static pm_redo_node_t *
6217pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) {
6218 assert(token->type == PM_TOKEN_KEYWORD_REDO);
6219
6220 return pm_redo_node_new(
6221 parser->arena,
6222 ++parser->node_id,
6223 0,
6224 PM_LOCATION_INIT_TOKEN(parser, token)
6225 );
6226}
6227
6232static pm_regular_expression_node_t *
6233pm_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) {
6234 return pm_regular_expression_node_new(
6235 parser->arena,
6236 ++parser->node_id,
6237 pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL,
6238 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6239 TOK2LOC(parser, opening),
6240 TOK2LOC(parser, content),
6241 TOK2LOC(parser, closing),
6242 *unescaped
6243 );
6244}
6245
6249static PRISM_INLINE pm_regular_expression_node_t *
6250pm_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6251 return pm_regular_expression_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6252}
6253
6257static pm_required_parameter_node_t *
6258pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
6259 return pm_required_parameter_node_new(
6260 parser->arena,
6261 ++parser->node_id,
6262 0,
6263 PM_LOCATION_INIT_TOKEN(parser, token),
6264 pm_parser_constant_id_token(parser, token)
6265 );
6266}
6267
6271static pm_rescue_modifier_node_t *
6272pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *keyword, pm_node_t *rescue_expression) {
6273 return pm_rescue_modifier_node_new(
6274 parser->arena,
6275 ++parser->node_id,
6276 0,
6277 PM_LOCATION_INIT_NODES(expression, rescue_expression),
6278 expression,
6279 TOK2LOC(parser, keyword),
6280 rescue_expression
6281 );
6282}
6283
6287static pm_rescue_node_t *
6288pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
6289 return pm_rescue_node_new(
6290 parser->arena,
6291 ++parser->node_id,
6292 0,
6293 PM_LOCATION_INIT_TOKEN(parser, keyword),
6294 TOK2LOC(parser, keyword),
6295 ((pm_node_list_t) { 0 }),
6296 ((pm_location_t) { 0 }),
6297 NULL,
6298 ((pm_location_t) { 0 }),
6299 NULL,
6300 NULL
6301 );
6302}
6303
6304static PRISM_INLINE void
6305pm_rescue_node_operator_set(const pm_parser_t *parser, pm_rescue_node_t *node, const pm_token_t *operator) {
6306 node->operator_loc = TOK2LOC(parser, operator);
6307}
6308
6312static void
6313pm_rescue_node_reference_set(pm_rescue_node_t *node, pm_node_t *reference) {
6314 node->reference = reference;
6315 PM_NODE_LENGTH_SET_NODE(node, reference);
6316}
6317
6321static void
6322pm_rescue_node_statements_set(pm_rescue_node_t *node, pm_statements_node_t *statements) {
6323 node->statements = statements;
6324 if (pm_statements_node_body_length(statements) > 0) {
6325 PM_NODE_LENGTH_SET_NODE(node, statements);
6326 }
6327}
6328
6332static void
6333pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subsequent) {
6334 node->subsequent = subsequent;
6335 PM_NODE_LENGTH_SET_NODE(node, subsequent);
6336}
6337
6341static void
6342pm_rescue_node_exceptions_append(pm_arena_t *arena, pm_rescue_node_t *node, pm_node_t *exception) {
6343 pm_node_list_append(arena, &node->exceptions, exception);
6344 PM_NODE_LENGTH_SET_NODE(node, exception);
6345}
6346
6350static pm_rest_parameter_node_t *
6351pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
6352 return pm_rest_parameter_node_new(
6353 parser->arena,
6354 ++parser->node_id,
6355 0,
6356 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
6357 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
6358 NTOK2LOC(parser, name),
6359 TOK2LOC(parser, operator)
6360 );
6361}
6362
6366static pm_retry_node_t *
6367pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) {
6368 assert(token->type == PM_TOKEN_KEYWORD_RETRY);
6369
6370 return pm_retry_node_new(
6371 parser->arena,
6372 ++parser->node_id,
6373 0,
6374 PM_LOCATION_INIT_TOKEN(parser, token)
6375 );
6376}
6377
6381static pm_return_node_t *
6382pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
6383 return pm_return_node_new(
6384 parser->arena,
6385 ++parser->node_id,
6386 0,
6387 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
6388 TOK2LOC(parser, keyword),
6389 arguments
6390 );
6391}
6392
6396static pm_self_node_t *
6397pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) {
6398 assert(token->type == PM_TOKEN_KEYWORD_SELF);
6399
6400 return pm_self_node_new(
6401 parser->arena,
6402 ++parser->node_id,
6403 0,
6404 PM_LOCATION_INIT_TOKEN(parser, token)
6405 );
6406}
6407
6411static pm_shareable_constant_node_t *
6412pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shareable_constant_value_t value) {
6413 return pm_shareable_constant_node_new(
6414 parser->arena,
6415 ++parser->node_id,
6416 (pm_node_flags_t) value,
6417 PM_LOCATION_INIT_NODE(write),
6418 write
6419 );
6420}
6421
6425static pm_singleton_class_node_t *
6426pm_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) {
6427 return pm_singleton_class_node_new(
6428 parser->arena,
6429 ++parser->node_id,
6430 0,
6431 PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword),
6432 *locals,
6433 TOK2LOC(parser, class_keyword),
6434 TOK2LOC(parser, operator),
6435 expression,
6436 body,
6437 TOK2LOC(parser, end_keyword)
6438 );
6439}
6440
6444static pm_source_encoding_node_t *
6445pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) {
6446 assert(token->type == PM_TOKEN_KEYWORD___ENCODING__);
6447
6448 return pm_source_encoding_node_new(
6449 parser->arena,
6450 ++parser->node_id,
6451 PM_NODE_FLAG_STATIC_LITERAL,
6452 PM_LOCATION_INIT_TOKEN(parser, token)
6453 );
6454}
6455
6459static pm_source_file_node_t*
6460pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) {
6461 assert(file_keyword->type == PM_TOKEN_KEYWORD___FILE__);
6462
6463 pm_node_flags_t flags = 0;
6464
6465 switch (parser->frozen_string_literal) {
6466 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6467 flags |= PM_STRING_FLAGS_MUTABLE;
6468 break;
6469 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6470 flags |= PM_STRING_FLAGS_FROZEN;
6471 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
6472 flags |= PM_NODE_FLAG_STATIC_LITERAL;
6473 }
6474 break;
6475 }
6476
6477 return pm_source_file_node_new(
6478 parser->arena,
6479 ++parser->node_id,
6480 flags,
6481 PM_LOCATION_INIT_TOKEN(parser, file_keyword),
6482 parser->filepath
6483 );
6484}
6485
6489static pm_source_line_node_t *
6490pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) {
6491 assert(token->type == PM_TOKEN_KEYWORD___LINE__);
6492
6493 return pm_source_line_node_new(
6494 parser->arena,
6495 ++parser->node_id,
6496 PM_NODE_FLAG_STATIC_LITERAL,
6497 PM_LOCATION_INIT_TOKEN(parser, token)
6498 );
6499}
6500
6504static pm_splat_node_t *
6505pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
6506 return pm_splat_node_new(
6507 parser->arena,
6508 ++parser->node_id,
6509 0,
6510 (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression),
6511 TOK2LOC(parser, operator),
6512 expression
6513 );
6514}
6515
6519static pm_statements_node_t *
6520pm_statements_node_create(pm_parser_t *parser) {
6521 return pm_statements_node_new(
6522 parser->arena,
6523 ++parser->node_id,
6524 0,
6525 PM_LOCATION_INIT_UNSET,
6526 ((pm_node_list_t) { 0 })
6527 );
6528}
6529
6533static size_t
6534pm_statements_node_body_length(pm_statements_node_t *node) {
6535 return node && node->body.size;
6536}
6537
6542static PRISM_INLINE void
6543pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement) {
6544 if (pm_statements_node_body_length(node) == 0 || PM_NODE_START(statement) < PM_NODE_START(node)) {
6545 PM_NODE_START_SET_NODE(node, statement);
6546 }
6547
6548 if (PM_NODE_END(statement) > PM_NODE_END(node)) {
6549 PM_NODE_LENGTH_SET_NODE(node, statement);
6550 }
6551}
6552
6556static void
6557pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline) {
6558 pm_statements_node_body_update(node, statement);
6559
6560 if (node->body.size > 0) {
6561 const pm_node_t *previous = node->body.nodes[node->body.size - 1];
6562
6563 switch (PM_NODE_TYPE(previous)) {
6564 case PM_BREAK_NODE:
6565 case PM_NEXT_NODE:
6566 case PM_REDO_NODE:
6567 case PM_RETRY_NODE:
6568 case PM_RETURN_NODE:
6569 pm_parser_warn_node(parser, statement, PM_WARN_UNREACHABLE_STATEMENT);
6570 break;
6571 default:
6572 break;
6573 }
6574 }
6575
6576 pm_node_list_append(parser->arena, &node->body, statement);
6577 if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6578}
6579
6583static void
6584pm_statements_node_body_prepend(pm_arena_t *arena, pm_statements_node_t *node, pm_node_t *statement) {
6585 pm_statements_node_body_update(node, statement);
6586 pm_node_list_prepend(arena, &node->body, statement);
6587 pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6588}
6589
6593static PRISM_INLINE pm_string_node_t *
6594pm_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) {
6595 pm_node_flags_t flags = 0;
6596
6597 switch (parser->frozen_string_literal) {
6598 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6599 flags = PM_STRING_FLAGS_MUTABLE;
6600 break;
6601 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6602 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6603 break;
6604 }
6605
6606 uint32_t start = PM_TOKEN_START(parser, opening == NULL ? content : opening);
6607 uint32_t end = PM_TOKEN_END(parser, closing == NULL ? content : closing);
6608
6609 return pm_string_node_new(
6610 parser->arena,
6611 ++parser->node_id,
6612 flags,
6613 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6614 NTOK2LOC(parser, opening),
6615 TOK2LOC(parser, content),
6616 NTOK2LOC(parser, closing),
6617 *string
6618 );
6619}
6620
6624static pm_string_node_t *
6625pm_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6626 return pm_string_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6627}
6628
6633static pm_string_node_t *
6634pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6635 pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, &parser->current_string);
6636 parser->current_string = PM_STRING_EMPTY;
6637 return node;
6638}
6639
6643static pm_super_node_t *
6644pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_t *arguments) {
6645 assert(keyword->type == PM_TOKEN_KEYWORD_SUPER);
6646
6647 const pm_location_t *end = pm_arguments_end(arguments);
6648 assert(end != NULL && "unreachable");
6649
6650 return pm_super_node_new(
6651 parser->arena,
6652 ++parser->node_id,
6653 0,
6654 ((pm_location_t) { .start = PM_TOKEN_START(parser, keyword), .length = PM_LOCATION_END(end) - PM_TOKEN_START(parser, keyword) }),
6655 TOK2LOC(parser, keyword),
6656 arguments->opening_loc,
6657 arguments->arguments,
6658 arguments->closing_loc,
6659 arguments->block
6660 );
6661}
6662
6667static bool
6668pm_ascii_only_p(const pm_string_t *contents) {
6669 const size_t length = pm_string_length(contents);
6670 const uint8_t *source = pm_string_source(contents);
6671
6672 for (size_t index = 0; index < length; index++) {
6673 if (source[index] & 0x80) return false;
6674 }
6675
6676 return true;
6677}
6678
6682static void
6683parse_symbol_encoding_validate_utf8(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6684 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6685 size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor);
6686
6687 if (width == 0) {
6688 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6689 break;
6690 }
6691
6692 cursor += width;
6693 }
6694}
6695
6700static void
6701parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6702 const pm_encoding_t *encoding = parser->encoding;
6703
6704 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6705 size_t width = encoding->char_width(cursor, end - cursor);
6706
6707 if (width == 0) {
6708 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6709 break;
6710 }
6711
6712 cursor += width;
6713 }
6714}
6715
6725static PRISM_INLINE pm_node_flags_t
6726parse_symbol_encoding(pm_parser_t *parser, const pm_encoding_t *explicit_encoding, const pm_token_t *location, const pm_string_t *contents, bool validate) {
6727 if (explicit_encoding != NULL) {
6728 // A Symbol may optionally have its encoding explicitly set. This will
6729 // happen if an escape sequence results in a non-ASCII code point.
6730 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
6731 if (validate) parse_symbol_encoding_validate_utf8(parser, location, contents);
6732 return PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
6733 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
6734 return PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING;
6735 } else if (validate) {
6736 parse_symbol_encoding_validate_other(parser, location, contents);
6737 }
6738 } else if (pm_ascii_only_p(contents)) {
6739 // Ruby stipulates that all source files must use an ASCII-compatible
6740 // encoding. Thus, all symbols appearing in source are eligible for
6741 // "downgrading" to US-ASCII.
6742 return PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
6743 } else if (validate) {
6744 parse_symbol_encoding_validate_other(parser, location, contents);
6745 }
6746
6747 return 0;
6748}
6749
6754static pm_symbol_node_t *
6755pm_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) {
6756 uint32_t start = opening == NULL ? PM_TOKEN_START(parser, value) : PM_TOKEN_START(parser, opening);
6757 uint32_t end = closing == NULL ? PM_TOKEN_END(parser, value) : PM_TOKEN_END(parser, closing);
6758
6759 return pm_symbol_node_new(
6760 parser->arena,
6761 ++parser->node_id,
6762 PM_NODE_FLAG_STATIC_LITERAL | flags,
6763 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6764 NTOK2LOC(parser, opening),
6765 NTOK2LOC(parser, value),
6766 NTOK2LOC(parser, closing),
6767 *unescaped
6768 );
6769}
6770
6774static PRISM_INLINE pm_symbol_node_t *
6775pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6776 return pm_symbol_node_create_unescaped(parser, opening, value, closing, &PM_STRING_EMPTY, 0);
6777}
6778
6782static pm_symbol_node_t *
6783pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6784 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));
6785 parser->current_string = PM_STRING_EMPTY;
6786 return node;
6787}
6788
6792static pm_symbol_node_t *
6793pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) {
6794 assert(token->type == PM_TOKEN_LABEL);
6795
6796 pm_token_t closing = { .type = PM_TOKEN_LABEL_END, .start = token->end - 1, .end = token->end };
6797 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end - 1 };
6798 pm_symbol_node_t *node = pm_symbol_node_create(parser, NULL, &label, &closing);
6799
6800 assert((label.end - label.start) >= 0);
6801 pm_string_shared_init(&node->unescaped, label.start, label.end);
6802 pm_node_flag_set(UP(node), parse_symbol_encoding(parser, parser->explicit_encoding, &label, &node->unescaped, false));
6803
6804 return node;
6805}
6806
6810static pm_symbol_node_t *
6811pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
6812 pm_symbol_node_t *node = pm_symbol_node_new(
6813 parser->arena,
6814 ++parser->node_id,
6815 PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING,
6816 PM_LOCATION_INIT_UNSET,
6817 ((pm_location_t) { 0 }),
6818 ((pm_location_t) { 0 }),
6819 ((pm_location_t) { 0 }),
6820 ((pm_string_t) { 0 })
6821 );
6822
6823 pm_string_constant_init(&node->unescaped, content, strlen(content));
6824 return node;
6825}
6826
6830static bool
6831pm_symbol_node_label_p(const pm_parser_t *parser, const pm_node_t *node) {
6832 const pm_location_t *location = NULL;
6833
6834 switch (PM_NODE_TYPE(node)) {
6835 case PM_SYMBOL_NODE: {
6836 const pm_symbol_node_t *cast = (pm_symbol_node_t *) node;
6837 if (cast->closing_loc.length > 0) {
6838 location = &cast->closing_loc;
6839 }
6840 break;
6841 }
6842 case PM_INTERPOLATED_SYMBOL_NODE: {
6843 const pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node;
6844 if (cast->closing_loc.length > 0) {
6845 location = &cast->closing_loc;
6846 }
6847 break;
6848 }
6849 default:
6850 return false;
6851 }
6852
6853 return (location != NULL) && (parser->start[PM_LOCATION_END(location) - 1] == ':');
6854}
6855
6859static pm_symbol_node_t *
6860pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t *opening, const pm_token_t *closing) {
6861 pm_symbol_node_t *new_node = pm_symbol_node_new(
6862 parser->arena,
6863 ++parser->node_id,
6864 PM_NODE_FLAG_STATIC_LITERAL,
6865 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6866 TOK2LOC(parser, opening),
6867 node->content_loc,
6868 TOK2LOC(parser, closing),
6869 node->unescaped
6870 );
6871
6872 pm_token_t content = {
6873 .type = PM_TOKEN_IDENTIFIER,
6874 .start = parser->start + node->content_loc.start,
6875 .end = parser->start + node->content_loc.start + node->content_loc.length
6876 };
6877
6878 pm_node_flag_set(UP(new_node), parse_symbol_encoding(parser, parser->explicit_encoding, &content, &node->unescaped, true));
6879
6880 /* The old node is arena-allocated so no explicit free is needed. */
6881 return new_node;
6882}
6883
6887static pm_string_node_t *
6888pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) {
6889 pm_node_flags_t flags = 0;
6890
6891 switch (parser->frozen_string_literal) {
6892 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6893 flags = PM_STRING_FLAGS_MUTABLE;
6894 break;
6895 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6896 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6897 break;
6898 }
6899
6900 pm_string_node_t *new_node = pm_string_node_new(
6901 parser->arena,
6902 ++parser->node_id,
6903 flags,
6904 PM_LOCATION_INIT_NODE(node),
6905 node->opening_loc,
6906 node->content_loc,
6907 node->closing_loc,
6908 node->unescaped
6909 );
6910
6911 /* The old node is arena-allocated so no explicit free is needed. */
6912 return new_node;
6913}
6914
6918static pm_true_node_t *
6919pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) {
6920 assert(token->type == PM_TOKEN_KEYWORD_TRUE);
6921
6922 return pm_true_node_new(
6923 parser->arena,
6924 ++parser->node_id,
6925 PM_NODE_FLAG_STATIC_LITERAL,
6926 PM_LOCATION_INIT_TOKEN(parser, token)
6927 );
6928}
6929
6933static pm_true_node_t *
6934pm_true_node_synthesized_create(pm_parser_t *parser) {
6935 return pm_true_node_new(
6936 parser->arena,
6937 ++parser->node_id,
6938 PM_NODE_FLAG_STATIC_LITERAL,
6939 PM_LOCATION_INIT_UNSET
6940 );
6941}
6942
6946static pm_undef_node_t *
6947pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
6948 assert(token->type == PM_TOKEN_KEYWORD_UNDEF);
6949
6950 return pm_undef_node_new(
6951 parser->arena,
6952 ++parser->node_id,
6953 0,
6954 PM_LOCATION_INIT_TOKEN(parser, token),
6955 ((pm_node_list_t) { 0 }),
6956 TOK2LOC(parser, token)
6957 );
6958}
6959
6963static void
6964pm_undef_node_append(pm_arena_t *arena, pm_undef_node_t *node, pm_node_t *name) {
6965 PM_NODE_LENGTH_SET_NODE(node, name);
6966 pm_node_list_append(arena, &node->names, name);
6967}
6968
6972static pm_unless_node_t *
6973pm_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) {
6974 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6975 pm_node_t *end = statements == NULL ? predicate : UP(statements);
6976
6977 return pm_unless_node_new(
6978 parser->arena,
6979 ++parser->node_id,
6980 PM_NODE_FLAG_NEWLINE,
6981 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, end),
6982 TOK2LOC(parser, keyword),
6983 predicate,
6984 NTOK2LOC(parser, then_keyword),
6985 statements,
6986 NULL,
6987 ((pm_location_t) { 0 })
6988 );
6989}
6990
6994static pm_unless_node_t *
6995pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) {
6996 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6997
6998 pm_statements_node_t *statements = pm_statements_node_create(parser);
6999 pm_statements_node_body_append(parser, statements, statement, true);
7000
7001 return pm_unless_node_new(
7002 parser->arena,
7003 ++parser->node_id,
7004 PM_NODE_FLAG_NEWLINE,
7005 PM_LOCATION_INIT_NODES(statement, predicate),
7006 TOK2LOC(parser, unless_keyword),
7007 predicate,
7008 ((pm_location_t) { 0 }),
7009 statements,
7010 NULL,
7011 ((pm_location_t) { 0 })
7012 );
7013}
7014
7015static PRISM_INLINE void
7016pm_unless_node_end_keyword_loc_set(const pm_parser_t *parser, pm_unless_node_t *node, const pm_token_t *end_keyword) {
7017 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
7018 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
7019}
7020
7026static void
7027pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statements) {
7028 assert(parser->current_block_exits != NULL);
7029
7030 // All of the block exits that we want to remove should be within the
7031 // statements, and since we are modifying the statements, we shouldn't have
7032 // to check the end location.
7033 uint32_t start = statements->base.location.start;
7034
7035 for (size_t index = parser->current_block_exits->size; index > 0; index--) {
7036 pm_node_t *block_exit = parser->current_block_exits->nodes[index - 1];
7037 if (block_exit->location.start < start) break;
7038
7039 // Implicitly remove from the list by lowering the size.
7040 parser->current_block_exits->size--;
7041 }
7042}
7043
7047static pm_until_node_t *
7048pm_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) {
7049 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7050
7051 return pm_until_node_new(
7052 parser->arena,
7053 ++parser->node_id,
7054 flags,
7055 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7056 TOK2LOC(parser, keyword),
7057 NTOK2LOC(parser, do_keyword),
7058 TOK2LOC(parser, closing),
7059 predicate,
7060 statements
7061 );
7062}
7063
7067static pm_until_node_t *
7068pm_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) {
7069 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7070 pm_loop_modifier_block_exits(parser, statements);
7071
7072 return pm_until_node_new(
7073 parser->arena,
7074 ++parser->node_id,
7075 flags,
7076 PM_LOCATION_INIT_NODES(statements, predicate),
7077 TOK2LOC(parser, keyword),
7078 ((pm_location_t) { 0 }),
7079 ((pm_location_t) { 0 }),
7080 predicate,
7081 statements
7082 );
7083}
7084
7088static pm_when_node_t *
7089pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
7090 return pm_when_node_new(
7091 parser->arena,
7092 ++parser->node_id,
7093 0,
7094 PM_LOCATION_INIT_TOKEN(parser, keyword),
7095 TOK2LOC(parser, keyword),
7096 ((pm_node_list_t) { 0 }),
7097 ((pm_location_t) { 0 }),
7098 NULL
7099 );
7100}
7101
7105static void
7106pm_when_node_conditions_append(pm_arena_t *arena, pm_when_node_t *node, pm_node_t *condition) {
7107 PM_NODE_LENGTH_SET_NODE(node, condition);
7108 pm_node_list_append(arena, &node->conditions, condition);
7109}
7110
7114static PRISM_INLINE void
7115pm_when_node_then_keyword_loc_set(const pm_parser_t *parser, pm_when_node_t *node, const pm_token_t *then_keyword) {
7116 PM_NODE_LENGTH_SET_TOKEN(parser, node, then_keyword);
7117 node->then_keyword_loc = TOK2LOC(parser, then_keyword);
7118}
7119
7123static void
7124pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statements) {
7125 if (PM_NODE_END(statements) > PM_NODE_END(node)) {
7126 PM_NODE_LENGTH_SET_NODE(node, statements);
7127 }
7128
7129 node->statements = statements;
7130}
7131
7135static pm_while_node_t *
7136pm_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) {
7137 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7138
7139 return pm_while_node_new(
7140 parser->arena,
7141 ++parser->node_id,
7142 flags,
7143 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7144 TOK2LOC(parser, keyword),
7145 NTOK2LOC(parser, do_keyword),
7146 TOK2LOC(parser, closing),
7147 predicate,
7148 statements
7149 );
7150}
7151
7155static pm_while_node_t *
7156pm_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) {
7157 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7158 pm_loop_modifier_block_exits(parser, statements);
7159
7160 return pm_while_node_new(
7161 parser->arena,
7162 ++parser->node_id,
7163 flags,
7164 PM_LOCATION_INIT_NODES(statements, predicate),
7165 TOK2LOC(parser, keyword),
7166 ((pm_location_t) { 0 }),
7167 ((pm_location_t) { 0 }),
7168 predicate,
7169 statements
7170 );
7171}
7172
7176static pm_while_node_t *
7177pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_statements_node_t *statements) {
7178 return pm_while_node_new(
7179 parser->arena,
7180 ++parser->node_id,
7181 0,
7182 PM_LOCATION_INIT_UNSET,
7183 ((pm_location_t) { 0 }),
7184 ((pm_location_t) { 0 }),
7185 ((pm_location_t) { 0 }),
7186 predicate,
7187 statements
7188 );
7189}
7190
7195static pm_x_string_node_t *
7196pm_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) {
7197 return pm_x_string_node_new(
7198 parser->arena,
7199 ++parser->node_id,
7200 PM_STRING_FLAGS_FROZEN,
7201 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
7202 TOK2LOC(parser, opening),
7203 TOK2LOC(parser, content),
7204 TOK2LOC(parser, closing),
7205 *unescaped
7206 );
7207}
7208
7212static PRISM_INLINE pm_x_string_node_t *
7213pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
7214 return pm_xstring_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
7215}
7216
7220static pm_yield_node_t *
7221pm_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) {
7222 uint32_t start = PM_TOKEN_START(parser, keyword);
7223 uint32_t end;
7224
7225 if (rparen_loc->length > 0) {
7226 end = PM_LOCATION_END(rparen_loc);
7227 } else if (arguments != NULL) {
7228 end = PM_NODE_END(arguments);
7229 } else if (lparen_loc->length > 0) {
7230 end = PM_LOCATION_END(lparen_loc);
7231 } else {
7232 end = PM_TOKEN_END(parser, keyword);
7233 }
7234
7235 return pm_yield_node_new(
7236 parser->arena,
7237 ++parser->node_id,
7238 0,
7239 ((pm_location_t) { .start = start, .length = U32(end - start) }),
7240 TOK2LOC(parser, keyword),
7241 *lparen_loc,
7242 arguments,
7243 *rparen_loc
7244 );
7245}
7246
7251static int
7252pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant_id) {
7253 pm_scope_t *scope = parser->current_scope;
7254 int depth = 0;
7255
7256 while (scope != NULL) {
7257 if (pm_locals_find(&scope->locals, constant_id) != UINT32_MAX) return depth;
7258 if (scope->closed) break;
7259
7260 scope = scope->previous;
7261 depth++;
7262 }
7263
7264 return -1;
7265}
7266
7272static PRISM_INLINE int
7273pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
7274 return pm_parser_local_depth_constant_id(parser, pm_parser_constant_id_token(parser, token));
7275}
7276
7280static PRISM_INLINE void
7281pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7282 pm_locals_write(&parser->metadata_arena, &parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
7283}
7284
7288static pm_constant_id_t
7289pm_parser_local_add_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7290 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
7291 if (constant_id != 0) pm_parser_local_add(parser, constant_id, start, end, reads);
7292 return constant_id;
7293}
7294
7298static PRISM_INLINE pm_constant_id_t
7299pm_parser_local_add_location(pm_parser_t *parser, pm_location_t *location, uint32_t reads) {
7300 return pm_parser_local_add_raw(parser, parser->start + location->start, parser->start + location->start + location->length, reads);
7301}
7302
7306static PRISM_INLINE pm_constant_id_t
7307pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token, uint32_t reads) {
7308 return pm_parser_local_add_raw(parser, token->start, token->end, reads);
7309}
7310
7314static pm_constant_id_t
7315pm_parser_local_add_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
7316 pm_constant_id_t constant_id = pm_parser_constant_id_owned(parser, start, length);
7317 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7318 return constant_id;
7319}
7320
7324static pm_constant_id_t
7325pm_parser_local_add_constant(pm_parser_t *parser, const char *start, size_t length) {
7326 pm_constant_id_t constant_id = pm_parser_constant_id_constant(parser, start, length);
7327 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7328 return constant_id;
7329}
7330
7338static bool
7339pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) {
7340 // We want to check whether the parameter name is a numbered parameter or
7341 // not.
7342 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, name), PM_TOKEN_LENGTH(name));
7343
7344 // Otherwise we'll fetch the constant id for the parameter name and check
7345 // whether it's already in the current scope.
7346 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name);
7347
7348 if (pm_locals_find(&parser->current_scope->locals, constant_id) != UINT32_MAX) {
7349 // Add an error if the parameter doesn't start with _ and has been seen before
7350 if ((name->start < name->end) && (*name->start != '_')) {
7351 pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_DUPLICATED);
7352 }
7353 return true;
7354 }
7355 return false;
7356}
7357
7361static void
7362pm_parser_scope_pop(pm_parser_t *parser) {
7363 pm_scope_t *scope = parser->current_scope;
7364 parser->current_scope = scope->previous;
7365 scope->previous = parser->free_scopes;
7366 parser->free_scopes = scope;
7367}
7368
7369/******************************************************************************/
7370/* Stack helpers */
7371/******************************************************************************/
7372
7376static PRISM_INLINE void
7377pm_state_stack_push(pm_state_stack_t *stack, bool value) {
7378 *stack = (*stack << 1) | (value & 1);
7379}
7380
7384static PRISM_INLINE void
7385pm_state_stack_pop(pm_state_stack_t *stack) {
7386 *stack >>= 1;
7387}
7388
7392static PRISM_INLINE bool
7393pm_state_stack_p(const pm_state_stack_t *stack) {
7394 return *stack & 1;
7395}
7396
7397static PRISM_INLINE void
7398pm_accepts_block_stack_push(pm_parser_t *parser, bool value) {
7399 // Use the negation of the value to prevent stack overflow.
7400 pm_state_stack_push(&parser->accepts_block_stack, !value);
7401}
7402
7403static PRISM_INLINE void
7404pm_accepts_block_stack_pop(pm_parser_t *parser) {
7405 pm_state_stack_pop(&parser->accepts_block_stack);
7406}
7407
7408static PRISM_INLINE bool
7409pm_accepts_block_stack_p(pm_parser_t *parser) {
7410 return !pm_state_stack_p(&parser->accepts_block_stack);
7411}
7412
7413static PRISM_INLINE void
7414pm_do_loop_stack_push(pm_parser_t *parser, bool value) {
7415 pm_state_stack_push(&parser->do_loop_stack, value);
7416}
7417
7418static PRISM_INLINE void
7419pm_do_loop_stack_pop(pm_parser_t *parser) {
7420 pm_state_stack_pop(&parser->do_loop_stack);
7421}
7422
7423static PRISM_INLINE bool
7424pm_do_loop_stack_p(pm_parser_t *parser) {
7425 return pm_state_stack_p(&parser->do_loop_stack);
7426}
7427
7457static PRISM_INLINE void
7458pm_enclosure_frame_push(pm_parser_t *parser) {
7459 pm_do_loop_stack_push(parser, false);
7460 pm_accepts_block_stack_push(parser, true);
7461}
7462
7463static PRISM_INLINE void
7464pm_enclosure_frame_pop(pm_parser_t *parser) {
7465 pm_do_loop_stack_pop(parser);
7466 pm_accepts_block_stack_pop(parser);
7467}
7468
7469/******************************************************************************/
7470/* Lexer check helpers */
7471/******************************************************************************/
7472
7477static PRISM_INLINE uint8_t
7478peek_at(const pm_parser_t *parser, const uint8_t *cursor) {
7479 if (cursor < parser->end) {
7480 return *cursor;
7481 } else {
7482 return '\0';
7483 }
7484}
7485
7491static PRISM_INLINE uint8_t
7492peek_offset(pm_parser_t *parser, ptrdiff_t offset) {
7493 return peek_at(parser, parser->current.end + offset);
7494}
7495
7500static PRISM_INLINE uint8_t
7501peek(const pm_parser_t *parser) {
7502 return peek_at(parser, parser->current.end);
7503}
7504
7509static PRISM_INLINE bool
7510match(pm_parser_t *parser, uint8_t value) {
7511 if (peek(parser) == value) {
7512 parser->current.end++;
7513 return true;
7514 }
7515 return false;
7516}
7517
7522static PRISM_INLINE size_t
7523match_eol_at(pm_parser_t *parser, const uint8_t *cursor) {
7524 if (peek_at(parser, cursor) == '\n') {
7525 return 1;
7526 }
7527 if (peek_at(parser, cursor) == '\r' && peek_at(parser, cursor + 1) == '\n') {
7528 return 2;
7529 }
7530 return 0;
7531}
7532
7538static PRISM_INLINE size_t
7539match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) {
7540 return match_eol_at(parser, parser->current.end + offset);
7541}
7542
7548static PRISM_INLINE size_t
7549match_eol(pm_parser_t *parser) {
7550 return match_eol_at(parser, parser->current.end);
7551}
7552
7556static PRISM_INLINE const uint8_t *
7557next_newline(const uint8_t *cursor, ptrdiff_t length) {
7558 assert(length >= 0);
7559
7560 // Note that it's okay for us to use memchr here to look for \n because none
7561 // of the encodings that we support have \n as a component of a multi-byte
7562 // character.
7563 return memchr(cursor, '\n', (size_t) length);
7564}
7565
7569static PRISM_INLINE bool
7570ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) {
7571 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));
7572}
7573
7578static bool
7579parser_lex_magic_comment_encoding_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
7580 const pm_encoding_t *encoding = pm_encoding_find(start, end);
7581
7582 if (encoding != NULL) {
7583 if (parser->encoding != encoding) {
7584 parser->encoding = encoding;
7585 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
7586 }
7587
7588 parser->encoding_changed = (encoding != PM_ENCODING_UTF_8_ENTRY);
7589 return true;
7590 }
7591
7592 return false;
7593}
7594
7599static void
7600parser_lex_magic_comment_encoding(pm_parser_t *parser) {
7601 const uint8_t *cursor = parser->current.start + 1;
7602 const uint8_t *end = parser->current.end;
7603
7604 bool separator = false;
7605 while (true) {
7606 if (end - cursor <= 6) return;
7607 switch (cursor[6]) {
7608 case 'C': case 'c': cursor += 6; continue;
7609 case 'O': case 'o': cursor += 5; continue;
7610 case 'D': case 'd': cursor += 4; continue;
7611 case 'I': case 'i': cursor += 3; continue;
7612 case 'N': case 'n': cursor += 2; continue;
7613 case 'G': case 'g': cursor += 1; continue;
7614 case '=': case ':':
7615 separator = true;
7616 cursor += 6;
7617 break;
7618 default:
7619 cursor += 6;
7620 if (pm_char_is_whitespace(*cursor)) break;
7621 continue;
7622 }
7623 if (pm_strncasecmp(cursor - 6, (const uint8_t *) "coding", 6) == 0) break;
7624 separator = false;
7625 }
7626
7627 while (true) {
7628 do {
7629 if (++cursor >= end) return;
7630 } while (pm_char_is_whitespace(*cursor));
7631
7632 if (separator) break;
7633 if (*cursor != '=' && *cursor != ':') return;
7634
7635 separator = true;
7636 cursor++;
7637 }
7638
7639 const uint8_t *value_start = cursor;
7640 while ((*cursor == '-' || *cursor == '_' || parser->encoding->alnum_char(cursor, 1)) && ++cursor < end);
7641
7642 if (!parser_lex_magic_comment_encoding_value(parser, value_start, cursor)) {
7643 // If we were unable to parse the encoding value, then we've got an
7644 // issue because we didn't understand the encoding that the user was
7645 // trying to use. In this case we'll keep using the default encoding but
7646 // add an error to the parser to indicate an unsuccessful parse.
7647 pm_parser_err(parser, U32(value_start - parser->start), U32(cursor - value_start), PM_ERR_INVALID_ENCODING_MAGIC_COMMENT);
7648 }
7649}
7650
7651typedef enum {
7652 PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE,
7653 PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE,
7654 PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID
7655} pm_magic_comment_boolean_value_t;
7656
7661static pm_magic_comment_boolean_value_t
7662parser_lex_magic_comment_boolean_value(const uint8_t *value_start, uint32_t value_length) {
7663 if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "true", 4) == 0) {
7664 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE;
7665 } else if (value_length == 5 && pm_strncasecmp(value_start, (const uint8_t *) "false", 5) == 0) {
7666 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE;
7667 } else {
7668 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID;
7669 }
7670}
7671
7672static PRISM_INLINE bool
7673pm_char_is_magic_comment_key_delimiter(const uint8_t b) {
7674 return b == '\'' || b == '"' || b == ':' || b == ';';
7675}
7676
7682static PRISM_INLINE const uint8_t *
7683parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor, const uint8_t *end) {
7684 // Scan for '*' as the middle character, since it is rarer than '-' in
7685 // typical comments and avoids repeated memchr calls for '-' that hit
7686 // dashes in words like "foo-bar".
7687 while ((cursor + 3 <= end) && (cursor = pm_memchr(cursor + 1, '*', (size_t) (end - cursor - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7688 if (cursor[-1] == '-' && cursor + 1 < end && cursor[1] == '-') {
7689 return cursor - 1;
7690 }
7691 }
7692 return NULL;
7693}
7694
7705static PRISM_INLINE bool
7706parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
7707 bool result = true;
7708
7709 const uint8_t *start = parser->current.start + 1;
7710 const uint8_t *end = parser->current.end;
7711 if (end - start <= 7) return false;
7712
7713 const uint8_t *cursor;
7714 bool indicator = false;
7715
7716 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7717 start = cursor + 3;
7718
7719 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7720 end = cursor;
7721 indicator = true;
7722 } else {
7723 // If we have a start marker but not an end marker, then we cannot
7724 // have a magic comment.
7725 return false;
7726 }
7727 } else {
7728 // Non-emacs magic comments must contain a colon for `key: value`.
7729 // Reject early if there is no colon to avoid scanning the entire
7730 // comment character-by-character.
7731 if (pm_memchr(start, ':', (size_t) (end - start), parser->encoding_changed, parser->encoding) == NULL) {
7732 return false;
7733 }
7734
7735 // Advance start past leading whitespace so the main loop begins
7736 // directly at the key, avoiding a redundant whitespace scan.
7737 start += pm_strspn_whitespace(start, end - start);
7738 }
7739
7740 cursor = start;
7741 while (cursor < end) {
7742 if (indicator) {
7743 while (cursor < end && (pm_char_is_magic_comment_key_delimiter(*cursor) || pm_char_is_whitespace(*cursor))) cursor++;
7744 }
7745
7746 const uint8_t *key_start = cursor;
7747 while (cursor < end && (!pm_char_is_magic_comment_key_delimiter(*cursor) && !pm_char_is_whitespace(*cursor))) cursor++;
7748
7749 const uint8_t *key_end = cursor;
7750 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7751 if (cursor == end) break;
7752
7753 if (*cursor == ':') {
7754 cursor++;
7755 } else {
7756 if (!indicator) return false;
7757 continue;
7758 }
7759
7760 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7761 if (cursor == end) break;
7762
7763 const uint8_t *value_start;
7764 const uint8_t *value_end;
7765
7766 if (*cursor == '"') {
7767 value_start = ++cursor;
7768 for (; cursor < end && *cursor != '"'; cursor++) {
7769 if (*cursor == '\\' && (cursor + 1 < end)) cursor++;
7770 }
7771 value_end = cursor;
7772 if (cursor < end && *cursor == '"') cursor++;
7773 } else {
7774 value_start = cursor;
7775 while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++;
7776 value_end = cursor;
7777 }
7778
7779 if (indicator) {
7780 while (cursor < end && (*cursor == ';' || pm_char_is_whitespace(*cursor))) cursor++;
7781 } else {
7782 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7783 if (cursor != end) return false;
7784 }
7785
7786 // Here, we need to do some processing on the key to swap out dashes for
7787 // underscores. We only need to do this if there _is_ a dash in the key.
7788 pm_string_t key;
7789 const size_t key_length = (size_t) (key_end - key_start);
7790 const uint8_t *dash = pm_memchr(key_start, '-', key_length, parser->encoding_changed, parser->encoding);
7791
7792 if (dash == NULL) {
7793 pm_string_shared_init(&key, key_start, key_end);
7794 } else {
7795 uint8_t *buffer = xmalloc(key_length);
7796 if (buffer == NULL) break;
7797
7798 memcpy(buffer, key_start, key_length);
7799 buffer[dash - key_start] = '_';
7800
7801 while ((dash = pm_memchr(dash + 1, '-', (size_t) (key_end - dash - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7802 buffer[dash - key_start] = '_';
7803 }
7804
7805 pm_string_owned_init(&key, buffer, key_length);
7806 }
7807
7808 // Finally, we can start checking the key against the list of known
7809 // magic comment keys, and potentially change state based on that.
7810 const uint8_t *key_source = pm_string_source(&key);
7811 uint32_t value_length = (uint32_t) (value_end - value_start);
7812
7813 // We only want to attempt to compare against encoding comments if it's
7814 // the first line in the file (or the second in the case of a shebang).
7815 if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7816 if (
7817 (key_length == 8 && pm_strncasecmp(key_source, (const uint8_t *) "encoding", 8) == 0) ||
7818 (key_length == 6 && pm_strncasecmp(key_source, (const uint8_t *) "coding", 6) == 0)
7819 ) {
7820 result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7821 }
7822 }
7823
7824 if (key_length == 11) {
7825 if (pm_strncasecmp(key_source, (const uint8_t *) "warn_indent", 11) == 0) {
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 (const char *) key_source,
7834 (int) value_length,
7835 (const char *) value_start
7836 );
7837 break;
7838 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7839 parser->warn_mismatched_indentation = false;
7840 break;
7841 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7842 parser->warn_mismatched_indentation = true;
7843 break;
7844 }
7845 }
7846 } else if (key_length == 21) {
7847 if (pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) {
7848 // We only want to handle frozen string literal comments if it's
7849 // before any semantic tokens have been seen.
7850 if (semantic_token_seen) {
7851 pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
7852 } else {
7853 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7854 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7855 PM_PARSER_WARN_TOKEN_FORMAT(
7856 parser,
7857 &parser->current,
7858 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7859 (int) key_length,
7860 (const char *) key_source,
7861 (int) value_length,
7862 (const char *) value_start
7863 );
7864 break;
7865 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7866 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
7867 break;
7868 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7869 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
7870 break;
7871 }
7872 }
7873 }
7874 } else if (key_length == 24) {
7875 if (pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) {
7876 const uint8_t *cursor = parser->current.start;
7877 while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
7878
7879 if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
7880 pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
7881 } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
7882 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
7883 } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
7884 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
7885 } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
7886 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
7887 } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
7888 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
7889 } else {
7890 PM_PARSER_WARN_TOKEN_FORMAT(
7891 parser,
7892 &parser->current,
7893 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7894 (int) key_length,
7895 (const char *) key_source,
7896 (int) value_length,
7897 (const char *) value_start
7898 );
7899 }
7900 }
7901 }
7902
7903 // When we're done, we want to free the string in case we had to
7904 // allocate memory for it.
7905 pm_string_cleanup(&key);
7906
7907 // Allocate a new magic comment node to append to the parser's list.
7908 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));
7909 magic_comment->node.next = NULL;
7910 magic_comment->key = (pm_location_t) { .start = U32(key_start - parser->start), .length = U32(key_length) };
7911 magic_comment->value = (pm_location_t) { .start = U32(value_start - parser->start), .length = value_length };
7912 pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment);
7913 }
7914
7915 return result;
7916}
7917
7918/******************************************************************************/
7919/* Context manipulations */
7920/******************************************************************************/
7921
7922static const uint32_t context_terminators[] = {
7923 [PM_CONTEXT_NONE] = 0,
7924 [PM_CONTEXT_BEGIN] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7925 [PM_CONTEXT_BEGIN_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7926 [PM_CONTEXT_BEGIN_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7927 [PM_CONTEXT_BEGIN_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7928 [PM_CONTEXT_BLOCK_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7929 [PM_CONTEXT_BLOCK_KEYWORDS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7930 [PM_CONTEXT_BLOCK_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7931 [PM_CONTEXT_BLOCK_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7932 [PM_CONTEXT_BLOCK_PARAMETERS] = (1U << PM_TOKEN_PIPE),
7933 [PM_CONTEXT_BLOCK_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7934 [PM_CONTEXT_CASE_WHEN] = (1U << PM_TOKEN_KEYWORD_WHEN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7935 [PM_CONTEXT_CASE_IN] = (1U << PM_TOKEN_KEYWORD_IN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7936 [PM_CONTEXT_CLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7937 [PM_CONTEXT_CLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7938 [PM_CONTEXT_CLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7939 [PM_CONTEXT_CLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7940 [PM_CONTEXT_DEF] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7941 [PM_CONTEXT_DEF_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7942 [PM_CONTEXT_DEF_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7943 [PM_CONTEXT_DEF_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7944 [PM_CONTEXT_DEF_PARAMS] = (1U << PM_TOKEN_EOF),
7945 [PM_CONTEXT_DEFINED] = (1U << PM_TOKEN_EOF),
7946 [PM_CONTEXT_DEFAULT_PARAMS] = (1U << PM_TOKEN_COMMA) | (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7947 [PM_CONTEXT_ELSE] = (1U << PM_TOKEN_KEYWORD_END),
7948 [PM_CONTEXT_ELSIF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7949 [PM_CONTEXT_EMBEXPR] = (1U << PM_TOKEN_EMBEXPR_END),
7950 [PM_CONTEXT_FOR] = (1U << PM_TOKEN_KEYWORD_END),
7951 [PM_CONTEXT_FOR_INDEX] = (1U << PM_TOKEN_KEYWORD_IN),
7952 [PM_CONTEXT_IF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7953 [PM_CONTEXT_LAMBDA_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7954 [PM_CONTEXT_LAMBDA_DO_END] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7955 [PM_CONTEXT_LAMBDA_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7956 [PM_CONTEXT_LAMBDA_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7957 [PM_CONTEXT_LAMBDA_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7958 [PM_CONTEXT_LOOP_PREDICATE] = (1U << PM_TOKEN_KEYWORD_DO) | (1U << PM_TOKEN_KEYWORD_THEN),
7959 [PM_CONTEXT_MAIN] = (1U << PM_TOKEN_EOF),
7960 [PM_CONTEXT_MODULE] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7961 [PM_CONTEXT_MODULE_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7962 [PM_CONTEXT_MODULE_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7963 [PM_CONTEXT_MODULE_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7964 [PM_CONTEXT_MULTI_TARGET] = (1U << PM_TOKEN_EOF),
7965 [PM_CONTEXT_PARENS] = (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7966 [PM_CONTEXT_POSTEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7967 [PM_CONTEXT_PREDICATE] = (1U << PM_TOKEN_KEYWORD_THEN) | (1U << PM_TOKEN_NEWLINE) | (1U << PM_TOKEN_SEMICOLON),
7968 [PM_CONTEXT_PREEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7969 [PM_CONTEXT_RESCUE_MODIFIER] = (1U << PM_TOKEN_EOF),
7970 [PM_CONTEXT_SCLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7971 [PM_CONTEXT_SCLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7972 [PM_CONTEXT_SCLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7973 [PM_CONTEXT_SCLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7974 [PM_CONTEXT_TERNARY] = (1U << PM_TOKEN_EOF),
7975 [PM_CONTEXT_UNLESS] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7976 [PM_CONTEXT_UNTIL] = (1U << PM_TOKEN_KEYWORD_END),
7977 [PM_CONTEXT_WHILE] = (1U << PM_TOKEN_KEYWORD_END),
7978};
7979
7980static PRISM_INLINE bool
7981context_terminator(pm_context_t context, pm_token_t *token) {
7982 return token->type < 32 && (context_terminators[context] & (1U << token->type));
7983}
7984
7989static pm_context_t
7990context_recoverable(const pm_parser_t *parser, pm_token_t *token) {
7991 pm_context_node_t *context_node = parser->current_context;
7992
7993 while (context_node != NULL) {
7994 if (context_terminator(context_node->context, token)) return context_node->context;
7995 context_node = context_node->prev;
7996 }
7997
7998 return PM_CONTEXT_NONE;
7999}
8000
8001PM_STATIC_ASSERT(__LINE__, PM_CONTEXT_MAXIMUM <= 64, "Expected every context to fit in the context mask.");
8002
8003static bool
8004context_push(pm_parser_t *parser, pm_context_t context) {
8005 pm_context_node_t *context_node = parser->free_contexts;
8006 if (context_node != NULL) {
8007 parser->free_contexts = context_node->prev;
8008 } else {
8009 context_node = (pm_context_node_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_context_node_t), PRISM_ALIGNOF(pm_context_node_t));
8010 }
8011
8012 pm_context_node_t *previous = parser->current_context;
8013 *context_node = (pm_context_node_t) {
8014 .context = context,
8015 .prev = previous,
8016 .mask = (previous == NULL ? 0 : previous->mask) | ((uint64_t) 1 << context)
8017 };
8018
8019 parser->current_context = context_node;
8020 return true;
8021}
8022
8023static void
8024context_pop(pm_parser_t *parser) {
8025 pm_context_node_t *context_node = parser->current_context;
8026 parser->current_context = context_node->prev;
8027 context_node->prev = parser->free_contexts;
8028 parser->free_contexts = context_node;
8029}
8030
8031static bool
8032context_p(const pm_parser_t *parser, pm_context_t context) {
8033 return parser->current_context != NULL && (parser->current_context->mask & ((uint64_t) 1 << context)) != 0;
8034}
8035
8036static bool
8037context_def_p(const pm_parser_t *parser) {
8038 pm_context_node_t *context_node = parser->current_context;
8039
8040 while (context_node != NULL) {
8041 switch (context_node->context) {
8042 case PM_CONTEXT_DEF:
8043 case PM_CONTEXT_DEF_PARAMS:
8044 case PM_CONTEXT_DEF_ENSURE:
8045 case PM_CONTEXT_DEF_RESCUE:
8046 case PM_CONTEXT_DEF_ELSE:
8047 return true;
8048 case PM_CONTEXT_CLASS:
8049 case PM_CONTEXT_CLASS_ENSURE:
8050 case PM_CONTEXT_CLASS_RESCUE:
8051 case PM_CONTEXT_CLASS_ELSE:
8052 case PM_CONTEXT_MODULE:
8053 case PM_CONTEXT_MODULE_ENSURE:
8054 case PM_CONTEXT_MODULE_RESCUE:
8055 case PM_CONTEXT_MODULE_ELSE:
8056 case PM_CONTEXT_SCLASS:
8057 case PM_CONTEXT_SCLASS_ENSURE:
8058 case PM_CONTEXT_SCLASS_RESCUE:
8059 case PM_CONTEXT_SCLASS_ELSE:
8060 return false;
8061 default:
8062 context_node = context_node->prev;
8063 }
8064 }
8065
8066 return false;
8067}
8068
8073static const char *
8074context_human(pm_context_t context) {
8075 switch (context) {
8076 case PM_CONTEXT_NONE:
8077 case PM_CONTEXT_MAXIMUM:
8078 assert(false && "unreachable");
8079 return "";
8080 case PM_CONTEXT_BEGIN: return "begin statement";
8081 case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block";
8082 case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block";
8083 case PM_CONTEXT_BLOCK_PARAMETERS: return "'|'..'|' block parameter";
8084 case PM_CONTEXT_CASE_WHEN: return "'when' clause";
8085 case PM_CONTEXT_CASE_IN: return "'in' clause";
8086 case PM_CONTEXT_CLASS: return "class definition";
8087 case PM_CONTEXT_DEF: return "method definition";
8088 case PM_CONTEXT_DEF_PARAMS: return "method parameters";
8089 case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value";
8090 case PM_CONTEXT_DEFINED: return "'defined?' expression";
8091 case PM_CONTEXT_ELSE:
8092 case PM_CONTEXT_BEGIN_ELSE:
8093 case PM_CONTEXT_BLOCK_ELSE:
8094 case PM_CONTEXT_CLASS_ELSE:
8095 case PM_CONTEXT_DEF_ELSE:
8096 case PM_CONTEXT_LAMBDA_ELSE:
8097 case PM_CONTEXT_MODULE_ELSE:
8098 case PM_CONTEXT_SCLASS_ELSE: return "'else' clause";
8099 case PM_CONTEXT_ELSIF: return "'elsif' clause";
8100 case PM_CONTEXT_EMBEXPR: return "embedded expression";
8101 case PM_CONTEXT_BEGIN_ENSURE:
8102 case PM_CONTEXT_BLOCK_ENSURE:
8103 case PM_CONTEXT_CLASS_ENSURE:
8104 case PM_CONTEXT_DEF_ENSURE:
8105 case PM_CONTEXT_LAMBDA_ENSURE:
8106 case PM_CONTEXT_MODULE_ENSURE:
8107 case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause";
8108 case PM_CONTEXT_FOR: return "for loop";
8109 case PM_CONTEXT_FOR_INDEX: return "for loop index";
8110 case PM_CONTEXT_IF: return "if statement";
8111 case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block";
8112 case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block";
8113 case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate";
8114 case PM_CONTEXT_MAIN: return "top level context";
8115 case PM_CONTEXT_MODULE: return "module definition";
8116 case PM_CONTEXT_MULTI_TARGET: return "multiple targets";
8117 case PM_CONTEXT_PARENS: return "parentheses";
8118 case PM_CONTEXT_POSTEXE: return "'END' block";
8119 case PM_CONTEXT_PREDICATE: return "predicate";
8120 case PM_CONTEXT_PREEXE: return "'BEGIN' block";
8121 case PM_CONTEXT_BEGIN_RESCUE:
8122 case PM_CONTEXT_BLOCK_RESCUE:
8123 case PM_CONTEXT_CLASS_RESCUE:
8124 case PM_CONTEXT_DEF_RESCUE:
8125 case PM_CONTEXT_LAMBDA_RESCUE:
8126 case PM_CONTEXT_MODULE_RESCUE:
8127 case PM_CONTEXT_RESCUE_MODIFIER:
8128 case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause";
8129 case PM_CONTEXT_SCLASS: return "singleton class definition";
8130 case PM_CONTEXT_TERNARY: return "ternary expression";
8131 case PM_CONTEXT_UNLESS: return "unless statement";
8132 case PM_CONTEXT_UNTIL: return "until statement";
8133 case PM_CONTEXT_WHILE: return "while statement";
8134 }
8135
8136 assert(false && "unreachable");
8137 return "";
8138}
8139
8140/******************************************************************************/
8141/* Specific token lexers */
8142/******************************************************************************/
8143
8144static PRISM_INLINE void
8145pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *string, size_t length, const uint8_t *invalid) {
8146 if (invalid != NULL) {
8147 pm_diagnostic_id_t diag_id = (invalid == (string + length - 1)) ? PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING : PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER;
8148 pm_parser_err(parser, U32(invalid - parser->start), 1, diag_id);
8149 }
8150}
8151
8152static size_t
8153pm_strspn_binary_number_validate(pm_parser_t *parser, const uint8_t *string) {
8154 const uint8_t *invalid = NULL;
8155 size_t length = pm_strspn_binary_number(string, parser->end - string, &invalid);
8156 pm_strspn_number_validate(parser, string, length, invalid);
8157 return length;
8158}
8159
8160static size_t
8161pm_strspn_octal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8162 const uint8_t *invalid = NULL;
8163 size_t length = pm_strspn_octal_number(string, parser->end - string, &invalid);
8164 pm_strspn_number_validate(parser, string, length, invalid);
8165 return length;
8166}
8167
8168static size_t
8169pm_strspn_decimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8170 const uint8_t *invalid = NULL;
8171 size_t length = pm_strspn_decimal_number(string, parser->end - string, &invalid);
8172 pm_strspn_number_validate(parser, string, length, invalid);
8173 return length;
8174}
8175
8176static size_t
8177pm_strspn_hexadecimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8178 const uint8_t *invalid = NULL;
8179 size_t length = pm_strspn_hexadecimal_number(string, parser->end - string, &invalid);
8180 pm_strspn_number_validate(parser, string, length, invalid);
8181 return length;
8182}
8183
8184static pm_token_type_t
8185lex_optional_float_suffix(pm_parser_t *parser, bool* seen_e) {
8186 pm_token_type_t type = PM_TOKEN_INTEGER;
8187
8188 // Here we're going to attempt to parse the optional decimal portion of a
8189 // float. If it's not there, then it's okay and we'll just continue on.
8190 if (peek(parser) == '.') {
8191 if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8192 parser->current.end += 2;
8193 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8194 type = PM_TOKEN_FLOAT;
8195 } else {
8196 // If we had a . and then something else, then it's not a float
8197 // suffix on a number it's a method call or something else.
8198 return type;
8199 }
8200 }
8201
8202 // Here we're going to attempt to parse the optional exponent portion of a
8203 // float. If it's not there, it's okay and we'll just continue on.
8204 if ((peek(parser) == 'e') || (peek(parser) == 'E')) {
8205 if ((peek_offset(parser, 1) == '+') || (peek_offset(parser, 1) == '-')) {
8206 parser->current.end += 2;
8207
8208 if (pm_char_is_decimal_digit(peek(parser))) {
8209 parser->current.end++;
8210 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8211 } else {
8212 pm_parser_err_current(parser, PM_ERR_INVALID_FLOAT_EXPONENT);
8213 }
8214 } else if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8215 parser->current.end++;
8216 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8217 } else {
8218 return type;
8219 }
8220
8221 *seen_e = true;
8222 type = PM_TOKEN_FLOAT;
8223 }
8224
8225 return type;
8226}
8227
8228static pm_token_type_t
8229lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) {
8230 pm_token_type_t type = PM_TOKEN_INTEGER;
8231 *seen_e = false;
8232
8233 if (peek_offset(parser, -1) == '0') {
8234 switch (*parser->current.end) {
8235 // 0d1111 is a decimal number
8236 case 'd':
8237 case 'D':
8238 parser->current.end++;
8239 if (pm_char_is_decimal_digit(peek(parser))) {
8240 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8241 } else {
8242 match(parser, '_');
8243 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_DECIMAL);
8244 }
8245
8246 break;
8247
8248 // 0b1111 is a binary number
8249 case 'b':
8250 case 'B':
8251 parser->current.end++;
8252 if (pm_char_is_binary_digit(peek(parser))) {
8253 parser->current.end += pm_strspn_binary_number_validate(parser, parser->current.end);
8254 } else {
8255 match(parser, '_');
8256 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY);
8257 }
8258
8259 parser->integer.base = PM_INTEGER_BASE_FLAGS_BINARY;
8260 break;
8261
8262 // 0o1111 is an octal number
8263 case 'o':
8264 case 'O':
8265 parser->current.end++;
8266 if (pm_char_is_octal_digit(peek(parser))) {
8267 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8268 } else {
8269 match(parser, '_');
8270 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL);
8271 }
8272
8273 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8274 break;
8275
8276 // 01111 is an octal number
8277 case '_':
8278 case '0':
8279 case '1':
8280 case '2':
8281 case '3':
8282 case '4':
8283 case '5':
8284 case '6':
8285 case '7':
8286 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8287 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8288 break;
8289
8290 // 0x1111 is a hexadecimal number
8291 case 'x':
8292 case 'X':
8293 parser->current.end++;
8294 if (pm_char_is_hexadecimal_digit(peek(parser))) {
8295 parser->current.end += pm_strspn_hexadecimal_number_validate(parser, parser->current.end);
8296 } else {
8297 match(parser, '_');
8298 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL);
8299 }
8300
8301 parser->integer.base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL;
8302 break;
8303
8304 // 0.xxx is a float
8305 case '.': {
8306 type = lex_optional_float_suffix(parser, seen_e);
8307 break;
8308 }
8309
8310 // 0exxx is a float
8311 case 'e':
8312 case 'E': {
8313 type = lex_optional_float_suffix(parser, seen_e);
8314 break;
8315 }
8316 }
8317 } else {
8318 // If it didn't start with a 0, then we'll lex as far as we can into a
8319 // decimal number. We compute the integer value inline to avoid
8320 // re-scanning the digits later in pm_integer_parse.
8321 {
8322 const uint8_t *cursor = parser->current.end;
8323 const uint8_t *end = parser->end;
8324 uint64_t value = (uint64_t) (cursor[-1] - '0');
8325
8326 bool has_underscore = false;
8327 bool prev_underscore = false;
8328 const uint8_t *invalid = NULL;
8329
8330 while (cursor < end) {
8331 uint8_t c = *cursor;
8332 if (c >= '0' && c <= '9') {
8333 if (value <= UINT32_MAX) value = value * 10 + (uint64_t) (c - '0');
8334 prev_underscore = false;
8335 cursor++;
8336 } else if (c == '_') {
8337 has_underscore = true;
8338 if (prev_underscore && invalid == NULL) invalid = cursor;
8339 prev_underscore = true;
8340 cursor++;
8341 } else {
8342 break;
8343 }
8344 }
8345
8346 if (has_underscore) {
8347 if (prev_underscore && invalid == NULL) invalid = cursor - 1;
8348 pm_strspn_number_validate(parser, parser->current.end, (size_t) (cursor - parser->current.end), invalid);
8349 }
8350
8351 if (value <= UINT32_MAX) {
8352 parser->integer.value = (uint32_t) value;
8353 parser->integer.lexed = true;
8354 }
8355
8356 parser->current.end = cursor;
8357 }
8358
8359 // Afterward, we'll lex as far as we can into an optional float suffix.
8360 // Guard the function call: the vast majority of decimal numbers are
8361 // plain integers, so avoid the call when the next byte cannot start a
8362 // float suffix.
8363 {
8364 uint8_t next = peek(parser);
8365 if (next == '.' || next == 'e' || next == 'E') {
8366 type = lex_optional_float_suffix(parser, seen_e);
8367
8368 // If it turned out to be a float, the cached integer value is
8369 // invalid.
8370 if (type != PM_TOKEN_INTEGER) {
8371 parser->integer.lexed = false;
8372 }
8373 }
8374 }
8375 }
8376
8377 // At this point we have a completed number, but we want to provide the user
8378 // with a good experience if they put an additional .xxx fractional
8379 // component on the end, so we'll check for that here.
8380 if (peek_offset(parser, 0) == '.' && pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8381 const uint8_t *fraction_start = parser->current.end;
8382 const uint8_t *fraction_end = parser->current.end + 2;
8383 fraction_end += pm_strspn_decimal_digit(fraction_end, parser->end - fraction_end);
8384 pm_parser_err(parser, U32(fraction_start - parser->start), U32(fraction_end - fraction_start), PM_ERR_INVALID_NUMBER_FRACTION);
8385 }
8386
8387 return type;
8388}
8389
8390static pm_token_type_t
8391lex_numeric(pm_parser_t *parser) {
8392 pm_token_type_t type = PM_TOKEN_INTEGER;
8393 parser->integer.base = PM_INTEGER_BASE_FLAGS_DECIMAL;
8394 parser->integer.lexed = false;
8395
8396 if (parser->current.end < parser->end) {
8397 bool seen_e = false;
8398 type = lex_numeric_prefix(parser, &seen_e);
8399
8400 const uint8_t *end = parser->current.end;
8401 pm_token_type_t suffix_type = type;
8402
8403 if (type == PM_TOKEN_INTEGER) {
8404 if (match(parser, 'r')) {
8405 suffix_type = PM_TOKEN_INTEGER_RATIONAL;
8406
8407 if (match(parser, 'i')) {
8408 suffix_type = PM_TOKEN_INTEGER_RATIONAL_IMAGINARY;
8409 }
8410 } else if (match(parser, 'i')) {
8411 suffix_type = PM_TOKEN_INTEGER_IMAGINARY;
8412 }
8413 } else {
8414 if (!seen_e && match(parser, 'r')) {
8415 suffix_type = PM_TOKEN_FLOAT_RATIONAL;
8416
8417 if (match(parser, 'i')) {
8418 suffix_type = PM_TOKEN_FLOAT_RATIONAL_IMAGINARY;
8419 }
8420 } else if (match(parser, 'i')) {
8421 suffix_type = PM_TOKEN_FLOAT_IMAGINARY;
8422 }
8423 }
8424
8425 const uint8_t b = peek(parser);
8426 if (b != '\0' && (b >= 0x80 || ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) || b == '_')) {
8427 parser->current.end = end;
8428 } else {
8429 type = suffix_type;
8430 }
8431 }
8432
8433 return type;
8434}
8435
8436static pm_token_type_t
8437lex_global_variable(pm_parser_t *parser) {
8438 if (parser->current.end >= parser->end) {
8439 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8440 return PM_TOKEN_GLOBAL_VARIABLE;
8441 }
8442
8443 // True if multiple characters are allowed after the declaration of the
8444 // global variable. Not true when it starts with "$-".
8445 bool allow_multiple = true;
8446
8447 switch (*parser->current.end) {
8448 case '~': // $~: match-data
8449 case '*': // $*: argv
8450 case '$': // $$: pid
8451 case '?': // $?: last status
8452 case '!': // $!: error string
8453 case '@': // $@: error position
8454 case '/': // $/: input record separator
8455 case '\\': // $\: output record separator
8456 case ';': // $;: field separator
8457 case ',': // $,: output field separator
8458 case '.': // $.: last read line number
8459 case '=': // $=: ignorecase
8460 case ':': // $:: load path
8461 case '<': // $<: reading filename
8462 case '>': // $>: default output handle
8463 case '\"': // $": already loaded files
8464 parser->current.end++;
8465 return PM_TOKEN_GLOBAL_VARIABLE;
8466
8467 case '&': // $&: last match
8468 case '`': // $`: string before last match
8469 case '\'': // $': string after last match
8470 case '+': // $+: string matches last paren.
8471 parser->current.end++;
8472 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_BACK_REFERENCE;
8473
8474 case '0': {
8475 parser->current.end++;
8476 size_t width;
8477
8478 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8479 do {
8480 parser->current.end += width;
8481 } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8482
8483 // $0 isn't allowed to be followed by anything.
8484 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;
8485 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, diag_id);
8486 }
8487
8488 return PM_TOKEN_GLOBAL_VARIABLE;
8489 }
8490
8491 case '1':
8492 case '2':
8493 case '3':
8494 case '4':
8495 case '5':
8496 case '6':
8497 case '7':
8498 case '8':
8499 case '9':
8500 parser->current.end += pm_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end);
8501 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_NUMBERED_REFERENCE;
8502
8503 case '-':
8504 parser->current.end++;
8505 allow_multiple = false;
8507 default: {
8508 size_t width;
8509
8510 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8511 do {
8512 parser->current.end += width;
8513 } while (allow_multiple && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8514 } else if (pm_char_is_whitespace(peek(parser))) {
8515 // If we get here, then we have a $ followed by whitespace,
8516 // which is not allowed.
8517 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8518 } else {
8519 // If we get here, then we have a $ followed by something that
8520 // isn't recognized as a global variable.
8521 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;
8522 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
8523 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);
8524 }
8525
8526 return PM_TOKEN_GLOBAL_VARIABLE;
8527 }
8528 }
8529}
8530
8535typedef struct {
8536 /* The keyword itself. */
8537 const char *name;
8538
8539 /* The keyword's first, middle, and last bytes and its length, packed by
8540 * PM_KEYWORD_PACK. */
8541 uint32_t packed;
8542
8543 /* The state to transition to when the keyword is lexed. */
8544 pm_lex_state_t state;
8545
8546 /* The token type of the keyword. */
8547 pm_token_type_t type;
8548
8549 /* The token type of the keyword in modifier position, or PM_TOKEN_EOF if
8550 * the keyword has no modifier form. */
8551 pm_token_type_t modifier_type;
8552} pm_keyword_t;
8553
8557#define PM_KEYWORD_LENGTH_MAXIMUM 12
8558
8563#define PM_KEYWORD_PACK(first, middle, last, length) \
8564 ((uint32_t) (first) | ((uint32_t) (middle) << 8) | ((uint32_t) (last) << 16) | ((uint32_t) (length) << 24))
8565
8570#define PM_KEYWORD_SLOT_BITS 6
8571
8585#define PM_KEYWORD_SLOT(packed) ((uint32_t) ((uint32_t) (packed) * 0x75f796ddU) >> (32 - PM_KEYWORD_SLOT_BITS))
8586
8591#define PM_KEYWORD(name, first, middle, last, state, type, modifier_type) \
8592 [PM_KEYWORD_SLOT(PM_KEYWORD_PACK(first, middle, last, sizeof(name) - 1))] = { name, PM_KEYWORD_PACK(first, middle, last, sizeof(name) - 1), state, type, modifier_type }
8593
8599static const pm_keyword_t pm_keywords[1 << PM_KEYWORD_SLOT_BITS] = {
8600 PM_KEYWORD("do", 'd', 'o', 'o', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF),
8601 PM_KEYWORD("if", 'i', 'f', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER),
8602 PM_KEYWORD("in", 'i', 'n', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IN, PM_TOKEN_EOF),
8603 PM_KEYWORD("or", 'o', 'r', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_OR, PM_TOKEN_EOF),
8604 PM_KEYWORD("and", 'a', 'n', 'd', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_AND, PM_TOKEN_EOF),
8605 PM_KEYWORD("def", 'd', 'e', 'f', PM_LEX_STATE_FNAME, PM_TOKEN_KEYWORD_DEF, PM_TOKEN_EOF),
8606 PM_KEYWORD("end", 'e', 'n', 'd', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END, PM_TOKEN_EOF),
8607 PM_KEYWORD("END", 'E', 'N', 'D', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END_UPCASE, PM_TOKEN_EOF),
8608 PM_KEYWORD("for", 'f', 'o', 'r', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_FOR, PM_TOKEN_EOF),
8609 PM_KEYWORD("nil", 'n', 'i', 'l', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_NIL, PM_TOKEN_EOF),
8610 PM_KEYWORD("not", 'n', 'o', 't', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_NOT, PM_TOKEN_EOF),
8611 PM_KEYWORD("case", 'c', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_CASE, PM_TOKEN_EOF),
8612 PM_KEYWORD("else", 'e', 's', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_EOF),
8613 PM_KEYWORD("next", 'n', 'x', 't', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_NEXT, PM_TOKEN_EOF),
8614 PM_KEYWORD("redo", 'r', 'd', 'o', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_REDO, PM_TOKEN_EOF),
8615 PM_KEYWORD("self", 's', 'l', 'f', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_SELF, PM_TOKEN_EOF),
8616 PM_KEYWORD("then", 't', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_EOF),
8617 PM_KEYWORD("true", 't', 'u', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_TRUE, PM_TOKEN_EOF),
8618 PM_KEYWORD("when", 'w', 'e', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_EOF),
8619 PM_KEYWORD("alias", 'a', 'i', 's', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_ALIAS, PM_TOKEN_EOF),
8620 PM_KEYWORD("begin", 'b', 'g', 'n', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_BEGIN, PM_TOKEN_EOF),
8621 PM_KEYWORD("BEGIN", 'B', 'G', 'N', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_BEGIN_UPCASE, PM_TOKEN_EOF),
8622 PM_KEYWORD("break", 'b', 'e', 'k', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_BREAK, PM_TOKEN_EOF),
8623 PM_KEYWORD("class", 'c', 'a', 's', PM_LEX_STATE_CLASS, PM_TOKEN_KEYWORD_CLASS, PM_TOKEN_EOF),
8624 PM_KEYWORD("elsif", 'e', 's', 'f', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_EOF),
8625 PM_KEYWORD("false", 'f', 'l', 'e', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_FALSE, PM_TOKEN_EOF),
8626 PM_KEYWORD("retry", 'r', 't', 'y', PM_LEX_STATE_END, PM_TOKEN_KEYWORD_RETRY, PM_TOKEN_EOF),
8627 PM_KEYWORD("super", 's', 'p', 'r', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_SUPER, PM_TOKEN_EOF),
8628 PM_KEYWORD("undef", 'u', 'd', 'f', PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_UNDEF, PM_TOKEN_EOF),
8629 PM_KEYWORD("until", 'u', 't', 'l', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNTIL, PM_TOKEN_KEYWORD_UNTIL_MODIFIER),
8630 PM_KEYWORD("while", 'w', 'i', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHILE, PM_TOKEN_KEYWORD_WHILE_MODIFIER),
8631 PM_KEYWORD("yield", 'y', 'e', 'd', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_YIELD, PM_TOKEN_EOF),
8632 PM_KEYWORD("ensure", 'e', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_EOF),
8633 PM_KEYWORD("module", 'm', 'u', 'e', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_MODULE, PM_TOKEN_EOF),
8634 PM_KEYWORD("rescue", 'r', 'c', 'e', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_RESCUE_MODIFIER),
8635 PM_KEYWORD("return", 'r', 'u', 'n', PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RETURN, PM_TOKEN_EOF),
8636 PM_KEYWORD("unless", 'u', 'e', 's', PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNLESS, PM_TOKEN_KEYWORD_UNLESS_MODIFIER),
8637 PM_KEYWORD("defined?", 'd', 'n', '?', PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF),
8638 PM_KEYWORD("__LINE__", '_', 'N', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___LINE__, PM_TOKEN_EOF),
8639 PM_KEYWORD("__FILE__", '_', 'L', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___FILE__, PM_TOKEN_EOF),
8640 PM_KEYWORD("__ENCODING__", '_', 'D', '_', PM_LEX_STATE_END, PM_TOKEN_KEYWORD___ENCODING__, PM_TOKEN_EOF)
8641};
8642
8643#undef PM_KEYWORD
8644
8649static PRISM_INLINE const pm_keyword_t *
8650pm_keyword_find(const uint8_t *start, const uint8_t *end) {
8651 ptrdiff_t width = end - start;
8652 if (width < 2 || width > PM_KEYWORD_LENGTH_MAXIMUM) return NULL;
8653
8654 size_t uwidth = (size_t) width;
8655 uint32_t packed = PM_KEYWORD_PACK(start[0], start[uwidth >> 1], start[uwidth - 1], uwidth);
8656
8657 const pm_keyword_t *keyword = &pm_keywords[PM_KEYWORD_SLOT(packed)];
8658 return (keyword->packed == packed && memcmp(start + 1, keyword->name + 1, uwidth - 2) == 0) ? keyword : NULL;
8659}
8660
8665bool
8666pm_keyword_p(const uint8_t *start, const uint8_t *end) {
8667 return pm_keyword_find(start, end) != NULL;
8668}
8669
8670static pm_token_type_t
8671lex_identifier(pm_parser_t *parser, bool previous_command_start) {
8672 /* Lex as far as we can into the current identifier. */
8673 const uint8_t *end = parser->end;
8674 const uint8_t *current_start = parser->current.start;
8675 const uint8_t *current_end = parser->current.end;
8676 bool encoding_changed = parser->encoding_changed;
8677
8678 if (encoding_changed) {
8679 size_t width;
8680 while ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) {
8681 current_end += width;
8682 }
8683 } else {
8684 /* Fast path: scan ASCII identifier bytes using wide operations. */
8685 current_end += scan_identifier_ascii(current_end, end);
8686
8687 /* Byte-at-a-time fallback for the tail and any UTF-8 sequences. */
8688 size_t width;
8689 while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) {
8690 current_end += width;
8691 }
8692 }
8693
8694 parser->current.end = current_end;
8695
8696 /* Whether the identifier ends in ! or ?, which makes it a method name
8697 * unless it spells a keyword (defined?). */
8698 bool method_name = false;
8699
8700 if (current_end < end) {
8701 if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
8702 /* Extend the identifier by the ! or ?, so that the keyword lookup
8703 * below sees the whole name. */
8704 current_end++;
8705
8706 if (
8707 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8708 (peek(parser) == ':') && (peek_offset(parser, 1) != ':')
8709 ) {
8710 /* If we are in a position where we can accept a : at the end of
8711 * an identifier, then we will optionally accept it. */
8712 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8713 (void) match(parser, ':');
8714
8715 /* A label is a symbol lexed inline rather than through a lex
8716 * mode, so it clears the encoding here. */
8717 parser->explicit_encoding = NULL;
8718 return PM_TOKEN_LABEL;
8719 }
8720
8721 method_name = true;
8722 } else if (lex_state_p(parser, PM_LEX_STATE_FNAME) && peek_offset(parser, 1) != '~' && peek_offset(parser, 1) != '>' && (peek_offset(parser, 1) != '=' || peek_offset(parser, 2) == '>') && match(parser, '=')) {
8723 /* If we are in a position where we can accept a = at the end of an
8724 * identifier, then we will optionally accept it. */
8725 return PM_TOKEN_IDENTIFIER;
8726 } else if (
8727 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8728 peek(parser) == ':' && peek_offset(parser, 1) != ':'
8729 ) {
8730 /* If we are in a position where we can accept a : at the end of an
8731 * identifier, then we will optionally accept it. */
8732 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8733 (void) match(parser, ':');
8734
8735 /* A label is a symbol lexed inline rather than through a lex
8736 * mode, so it clears the encoding here. */
8737 parser->explicit_encoding = NULL;
8738 return PM_TOKEN_LABEL;
8739 }
8740 }
8741
8742 if (parser->lex_state != PM_LEX_STATE_DOT) {
8743 /* The lex state from before the keyword changes it, which decides
8744 * whether the keyword is in its modifier form and whether `do` is a
8745 * plain name below. */
8746 pm_lex_state_t previous_lex_state = parser->lex_state;
8747 const pm_keyword_t *keyword = pm_keyword_find(current_start, current_end);
8748
8749 if (keyword != NULL) {
8750 if (previous_lex_state & PM_LEX_STATE_FNAME) {
8751 lex_state_set(parser, PM_LEX_STATE_ENDFN);
8752 } else {
8753 lex_state_set(parser, keyword->state);
8754 if (keyword->state == PM_LEX_STATE_BEG) {
8755 parser->command_start = true;
8756 }
8757
8758 if ((keyword->modifier_type != PM_TOKEN_EOF) && !(previous_lex_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
8759 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
8760 return keyword->modifier_type;
8761 }
8762 }
8763
8764 if (keyword->type == PM_TOKEN_KEYWORD_DO) {
8765 /* In FNAME position (a symbol like `:do` or a method name like
8766 * `def do`), `do` is a plain name rather than a block, loop, or
8767 * lambda opener, so none of the discrimination below applies
8768 * and the lambda nesting is left untouched. */
8769 if (previous_lex_state & PM_LEX_STATE_FNAME) {
8770 return PM_TOKEN_KEYWORD_DO;
8771 }
8772 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
8773 /* At the bare nesting level of a lambda literal (no
8774 * delimiter opened since `->`), a `do` opens the lambda
8775 * body. This is a distinct token so that a command in a
8776 * parameter default cannot consume it as its own block
8777 * (`-> a = foo do end` is `->(a = foo) do end`).
8778 *
8779 * Clear the nesting so that no token within the `do`/`end`
8780 * body is considered to be at the beginning of a lambda;
8781 * the parser restores the enclosing value once the lambda
8782 * has been fully parsed. */
8783 parser->lambda_enclosure_nesting = -1;
8784 return PM_TOKEN_KEYWORD_DO_LAMBDA;
8785 }
8786 if (pm_do_loop_stack_p(parser)) {
8787 return PM_TOKEN_KEYWORD_DO_LOOP;
8788 }
8789 if (!pm_accepts_block_stack_p(parser)) {
8790 return PM_TOKEN_KEYWORD_DO_BLOCK;
8791 }
8792 return PM_TOKEN_KEYWORD_DO;
8793 }
8794
8795 return keyword->type;
8796 }
8797 }
8798
8799 if (method_name) {
8800 return PM_TOKEN_METHOD_NAME;
8801 }
8802
8803 if (encoding_changed) {
8804 return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8805 }
8806
8807 /* Identifiers usually start with an ASCII byte, for which the uppercase
8808 * check is a simple range comparison. This avoids the call into the
8809 * encoding module for every identifier. */
8810 if (*current_start < 0x80) {
8811 return (*current_start >= 'A' && *current_start <= 'Z') ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8812 }
8813 return pm_encoding_utf_8_isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8814}
8815
8820static bool
8821current_token_starts_line(pm_parser_t *parser) {
8822 return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n');
8823}
8824
8839static pm_token_type_t
8840lex_interpolation(pm_parser_t *parser, const uint8_t *pound) {
8841 // If there is no content following this #, then we're at the end of
8842 // the string and we can safely return string content.
8843 if (pound + 1 >= parser->end) {
8844 parser->current.end = pound + 1;
8845 return PM_TOKEN_STRING_CONTENT;
8846 }
8847
8848 // Now we'll check against the character that follows the #. If it
8849 // constitutes valid interplation, we'll handle that, otherwise we'll return
8850 // 0.
8851 switch (pound[1]) {
8852 case '@': {
8853 // In this case we may have hit an embedded instance or class variable.
8854 if (pound + 2 >= parser->end) {
8855 parser->current.end = pound + 1;
8856 return PM_TOKEN_STRING_CONTENT;
8857 }
8858
8859 // If we're looking at a @ and there's another @, then we'll skip past the
8860 // second @.
8861 const uint8_t *variable = pound + 2;
8862 if (*variable == '@' && pound + 3 < parser->end) variable++;
8863
8864 if (char_is_identifier_start(parser, variable, parser->end - variable)) {
8865 // At this point we're sure that we've either hit an embedded instance
8866 // or class variable. In this case we'll first need to check if we've
8867 // already consumed content.
8868 if (pound > parser->current.start) {
8869 parser->current.end = pound;
8870 return PM_TOKEN_STRING_CONTENT;
8871 }
8872
8873 // Otherwise we need to return the embedded variable token
8874 // and then switch to the embedded variable lex mode.
8875 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8876 parser->current.end = pound + 1;
8877 return PM_TOKEN_EMBVAR;
8878 }
8879
8880 // If we didn't get a valid interpolation, then this is just regular
8881 // string content. This is like if we get "#@-". In this case the caller
8882 // should keep lexing.
8883 parser->current.end = pound + 1;
8884 return 0;
8885 }
8886 case '$':
8887 // In this case we may have hit an embedded global variable. If there's
8888 // not enough room, then we'll just return string content.
8889 if (pound + 2 >= parser->end) {
8890 parser->current.end = pound + 1;
8891 return PM_TOKEN_STRING_CONTENT;
8892 }
8893
8894 // This is the character that we're going to check to see if it is the
8895 // start of an identifier that would indicate that this is a global
8896 // variable.
8897 const uint8_t *check = pound + 2;
8898
8899 if (pound[2] == '-') {
8900 if (pound + 3 >= parser->end) {
8901 parser->current.end = pound + 2;
8902 return PM_TOKEN_STRING_CONTENT;
8903 }
8904
8905 check++;
8906 }
8907
8908 // If the character that we're going to check is the start of an
8909 // identifier, or we don't have a - and the character is a decimal number
8910 // or a global name punctuation character, then we've hit an embedded
8911 // global variable.
8912 if (
8913 char_is_identifier_start(parser, check, parser->end - check) ||
8914 (pound[2] != '-' && (pm_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2])))
8915 ) {
8916 // In this case we've hit an embedded global variable. First check to
8917 // see if we've already consumed content. If we have, then we need to
8918 // return that content as string content first.
8919 if (pound > parser->current.start) {
8920 parser->current.end = pound;
8921 return PM_TOKEN_STRING_CONTENT;
8922 }
8923
8924 // Otherwise, we need to return the embedded variable token and switch
8925 // to the embedded variable lex mode.
8926 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8927 parser->current.end = pound + 1;
8928 return PM_TOKEN_EMBVAR;
8929 }
8930
8931 // In this case we've hit a #$ that does not indicate a global variable.
8932 // In this case we'll continue lexing past it.
8933 parser->current.end = pound + 1;
8934 return 0;
8935 case '{':
8936 // In this case it's the start of an embedded expression. If we have
8937 // already consumed content, then we need to return that content as string
8938 // content first.
8939 if (pound > parser->current.start) {
8940 parser->current.end = pound;
8941 return PM_TOKEN_STRING_CONTENT;
8942 }
8943
8944 parser->enclosure_nesting++;
8945
8946 // Otherwise we'll skip past the #{ and begin lexing the embedded
8947 // expression.
8948 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBEXPR });
8949 parser->current.end = pound + 2;
8950 parser->command_start = true;
8951 pm_enclosure_frame_push(parser);
8952 return PM_TOKEN_EMBEXPR_BEGIN;
8953 default:
8954 // In this case we've hit a # that doesn't constitute interpolation. We'll
8955 // mark that by returning the not provided token type. This tells the
8956 // consumer to keep lexing forward.
8957 parser->current.end = pound + 1;
8958 return 0;
8959 }
8960}
8961
8962static const uint8_t PM_ESCAPE_FLAG_NONE = 0x0;
8963static const uint8_t PM_ESCAPE_FLAG_CONTROL = 0x1;
8964static const uint8_t PM_ESCAPE_FLAG_META = 0x2;
8965static const uint8_t PM_ESCAPE_FLAG_SINGLE = 0x4;
8966static const uint8_t PM_ESCAPE_FLAG_REGEXP = 0x8;
8967
8971static const bool ascii_printable_chars[] = {
8972 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
8973 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8974 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8975 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8976 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8977 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
8978 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8979 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
8980};
8981
8982static PRISM_INLINE bool
8983char_is_ascii_printable(const uint8_t b) {
8984 return (b < 0x80) && ascii_printable_chars[b];
8985}
8986
8991static PRISM_INLINE uint8_t
8992escape_hexadecimal_digit(const uint8_t value) {
8993 return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9);
8994}
8995
9001static PRISM_INLINE uint32_t
9002escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location, const uint8_t flags) {
9003 uint32_t value = 0;
9004 for (size_t index = 0; index < length; index++) {
9005 if (index != 0) value <<= 4;
9006 value |= escape_hexadecimal_digit(string[index]);
9007 }
9008
9009 // Here we're going to verify that the value is actually a valid Unicode
9010 // codepoint and not a surrogate pair.
9011 if (value >= 0xD800 && value <= 0xDFFF) {
9012 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9013 // In regexp context, defer the error to regexp encoding
9014 // validation where we can produce a regexp-specific message.
9015 } else if (error_location != NULL) {
9016 pm_parser_err(parser, error_location->start, error_location->length, PM_ERR_ESCAPE_INVALID_UNICODE);
9017 } else {
9018 pm_parser_err(parser, U32(string - parser->start), U32(length), PM_ERR_ESCAPE_INVALID_UNICODE);
9019 }
9020 return 0xFFFD;
9021 }
9022
9023 return value;
9024}
9025
9029static PRISM_INLINE uint8_t
9030escape_byte(uint8_t value, const uint8_t flags) {
9031 if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x9f;
9032 if (flags & PM_ESCAPE_FLAG_META) value |= 0x80;
9033 return value;
9034}
9035
9039static PRISM_INLINE void
9040escape_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) {
9041 // \u escape sequences in string-like structures implicitly change the
9042 // encoding to UTF-8 if they are >= 0x80 or if they are used in a character
9043 // literal.
9044 if (value >= 0x80 || flags & PM_ESCAPE_FLAG_SINGLE) {
9045 if (parser->explicit_encoding != NULL && parser->explicit_encoding != PM_ENCODING_UTF_8_ENTRY) {
9046 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9047 // In regexp context, suppress this error — the regexp encoding
9048 // validation will produce a more specific error message.
9049 } else {
9050 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(end - start), PM_ERR_MIXED_ENCODING, parser->explicit_encoding->name);
9051 }
9052 }
9053
9054 parser->explicit_encoding = PM_ENCODING_UTF_8_ENTRY;
9055 }
9056
9057 if (!pm_buffer_append_unicode_codepoint(buffer, value)) {
9058 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9059 // In regexp context, defer the error to the regexp encoding
9060 // validation which produces a regexp-specific message.
9061 } else {
9062 pm_parser_err(parser, U32(start - parser->start), U32(end - start), PM_ERR_ESCAPE_INVALID_UNICODE);
9063 }
9064
9065 pm_buffer_append_byte(buffer, 0xEF);
9066 pm_buffer_append_byte(buffer, 0xBF);
9067 pm_buffer_append_byte(buffer, 0xBD);
9068 }
9069}
9070
9075static PRISM_INLINE void
9076escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t flags, uint8_t byte) {
9077 if (byte >= 0x80) {
9078 if (parser->explicit_encoding != NULL && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
9079 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9080 // In regexp context, suppress this error — the regexp encoding
9081 // validation will produce a more specific error message.
9082 } else {
9083 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_MIXED_ENCODING, parser->encoding->name);
9084 }
9085 }
9086
9087 parser->explicit_encoding = parser->encoding;
9088 }
9089
9090 pm_buffer_append_byte(buffer, byte);
9091}
9092
9108static PRISM_INLINE void
9109escape_write_byte(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags, uint8_t byte) {
9110 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9111 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", byte);
9112 }
9113
9114 escape_write_byte_encoded(parser, buffer, flags, byte);
9115}
9116
9120static PRISM_INLINE void
9121escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9122 size_t width;
9123 if (parser->encoding_changed) {
9124 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9125 } else {
9126 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9127 }
9128
9129 if (width == 1) {
9130 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);
9131 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(*parser->current.end++, flags));
9132 } else if (width > 1) {
9133 // Valid multibyte character. Just ignore escape.
9134 pm_buffer_t *b = (flags & PM_ESCAPE_FLAG_REGEXP) ? regular_expression_buffer : buffer;
9135 pm_buffer_append_bytes(b, parser->current.end, width);
9136 parser->current.end += width;
9137 } else {
9138 // Assume the next character wasn't meant to be part of this escape
9139 // sequence since it is invalid. Add an error and move on.
9140 parser->current.end++;
9141 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9142 }
9143}
9144
9150static void
9151escape_read_warn(pm_parser_t *parser, uint8_t flags, uint8_t flag, const char *type) {
9152#define FLAG(value) ((value & PM_ESCAPE_FLAG_CONTROL) ? "\\C-" : (value & PM_ESCAPE_FLAG_META) ? "\\M-" : "")
9153
9154 PM_PARSER_WARN_TOKEN_FORMAT(
9155 parser,
9156 &parser->current,
9157 PM_WARN_INVALID_CHARACTER,
9158 FLAG(flags),
9159 FLAG(flag),
9160 type
9161 );
9162
9163#undef FLAG
9164}
9165
9169static void
9170escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9171 uint8_t peeked = peek(parser);
9172 switch (peeked) {
9173 case '\\': {
9174 parser->current.end++;
9175 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\\', flags));
9176 return;
9177 }
9178 case '\'': {
9179 parser->current.end++;
9180 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\'', flags));
9181 return;
9182 }
9183 case 'a': {
9184 parser->current.end++;
9185 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\a', flags));
9186 return;
9187 }
9188 case 'b': {
9189 parser->current.end++;
9190 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\b', flags));
9191 return;
9192 }
9193 case 'e': {
9194 parser->current.end++;
9195 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\033', flags));
9196 return;
9197 }
9198 case 'f': {
9199 parser->current.end++;
9200 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\f', flags));
9201 return;
9202 }
9203 case 'n': {
9204 parser->current.end++;
9205 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\n', flags));
9206 return;
9207 }
9208 case 'r': {
9209 parser->current.end++;
9210 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\r', flags));
9211 return;
9212 }
9213 case 's': {
9214 parser->current.end++;
9215 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(' ', flags));
9216 return;
9217 }
9218 case 't': {
9219 parser->current.end++;
9220 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\t', flags));
9221 return;
9222 }
9223 case 'v': {
9224 parser->current.end++;
9225 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\v', flags));
9226 return;
9227 }
9228 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': {
9229 uint8_t value = (uint8_t) (*parser->current.end - '0');
9230 parser->current.end++;
9231
9232 if (pm_char_is_octal_digit(peek(parser))) {
9233 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9234 parser->current.end++;
9235
9236 if (pm_char_is_octal_digit(peek(parser))) {
9237 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9238 parser->current.end++;
9239 }
9240 }
9241
9242 value = escape_byte(value, flags);
9243 escape_write_byte(parser, buffer, regular_expression_buffer, flags, value);
9244 return;
9245 }
9246 case 'x': {
9247 const uint8_t *start = parser->current.end - 1;
9248
9249 parser->current.end++;
9250 uint8_t byte = peek(parser);
9251
9252 if (pm_char_is_hexadecimal_digit(byte)) {
9253 uint8_t value = escape_hexadecimal_digit(byte);
9254 parser->current.end++;
9255
9256 byte = peek(parser);
9257 if (pm_char_is_hexadecimal_digit(byte)) {
9258 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(byte));
9259 parser->current.end++;
9260 }
9261
9262 value = escape_byte(value, flags);
9263 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9264 if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) {
9265 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", value);
9266 } else {
9267 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9268 }
9269 }
9270
9271 escape_write_byte_encoded(parser, buffer, flags, value);
9272 } else {
9273 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL);
9274 }
9275
9276 return;
9277 }
9278 case 'u': {
9279 const uint8_t *start = parser->current.end - 1;
9280 parser->current.end++;
9281
9282 if (parser->current.end == parser->end) {
9283 const uint8_t *start = parser->current.end - 2;
9284 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9285 } else if (peek(parser) == '{') {
9286 const uint8_t *unicode_codepoints_start = parser->current.end - 2;
9287 parser->current.end++;
9288
9289 size_t whitespace;
9290 while (true) {
9291 if ((whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end)) > 0) {
9292 parser->current.end += whitespace;
9293 } else if (peek(parser) == '\\' && peek_offset(parser, 1) == 'n') {
9294 // This is super hacky, but it gets us nicer error
9295 // messages because we can still pass it off to the
9296 // regular expression engine even if we hit an
9297 // unterminated regular expression.
9298 parser->current.end += 2;
9299 } else {
9300 break;
9301 }
9302 }
9303
9304 const uint8_t *extra_codepoints_start = NULL;
9305 int codepoints_count = 0;
9306
9307 while ((parser->current.end < parser->end) && (*parser->current.end != '}')) {
9308 const uint8_t *unicode_start = parser->current.end;
9309 size_t hexadecimal_length = pm_strspn_hexadecimal_digit(parser->current.end, parser->end - parser->current.end);
9310
9311 if (hexadecimal_length > 6) {
9312 // \u{nnnn} character literal allows only 1-6 hexadecimal digits
9313 pm_parser_err(parser, U32(unicode_start - parser->start), U32(hexadecimal_length), PM_ERR_ESCAPE_INVALID_UNICODE_LONG);
9314 } else if (hexadecimal_length == 0) {
9315 // there are not hexadecimal characters
9316
9317 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9318 // If this is a regular expression, we are going to
9319 // let the regular expression engine handle this
9320 // error instead of us because we don't know at this
9321 // point if we're inside a comment in /x mode.
9322 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9323 } else {
9324 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE);
9325 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9326 }
9327
9328 return;
9329 }
9330
9331 parser->current.end += hexadecimal_length;
9332 codepoints_count++;
9333 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count == 2) {
9334 extra_codepoints_start = unicode_start;
9335 }
9336
9337 uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL, flags);
9338 escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value);
9339
9340 parser->current.end += pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
9341 }
9342
9343 // ?\u{nnnn} character literal should contain only one codepoint
9344 // and cannot be like ?\u{nnnn mmmm}.
9345 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count > 1) {
9346 pm_parser_err(parser, U32(extra_codepoints_start - parser->start), U32(parser->current.end - 1 - extra_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL);
9347 }
9348
9349 if (parser->current.end == parser->end) {
9350 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);
9351 } else if (peek(parser) == '}') {
9352 parser->current.end++;
9353 } else {
9354 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9355 // If this is a regular expression, we are going to let
9356 // the regular expression engine handle this error
9357 // instead of us because we don't know at this point if
9358 // we're inside a comment in /x mode.
9359 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9360 } else {
9361 pm_parser_err(parser, U32(unicode_codepoints_start - parser->start), U32(parser->current.end - unicode_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9362 }
9363 }
9364
9365 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9366 pm_buffer_append_bytes(regular_expression_buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start));
9367 }
9368 } else {
9369 size_t length = pm_strspn_hexadecimal_digit(parser->current.end, MIN(parser->end - parser->current.end, 4));
9370
9371 if (length == 0) {
9372 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9373 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9374 } else {
9375 const uint8_t *start = parser->current.end - 2;
9376 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9377 }
9378 } else if (length == 4) {
9379 uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL, flags);
9380
9381 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9382 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start));
9383 }
9384
9385 escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value);
9386 parser->current.end += 4;
9387 } else {
9388 parser->current.end += length;
9389
9390 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9391 // If this is a regular expression, we are going to let
9392 // the regular expression engine handle this error
9393 // instead of us.
9394 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9395 } else {
9396 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE);
9397 }
9398 }
9399 }
9400
9401 return;
9402 }
9403 case 'c': {
9404 parser->current.end++;
9405 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9406 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9407 }
9408
9409 if (parser->current.end == parser->end) {
9410 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9411 return;
9412 }
9413
9414 uint8_t peeked = peek(parser);
9415 switch (peeked) {
9416 case '?': {
9417 parser->current.end++;
9418 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9419 return;
9420 }
9421 case '\\':
9422 parser->current.end++;
9423
9424 if (match(parser, 'u') || match(parser, 'U')) {
9425 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9426 return;
9427 }
9428
9429 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9430 return;
9431 case ' ':
9432 parser->current.end++;
9433 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9434 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9435 return;
9436 case '\t':
9437 parser->current.end++;
9438 escape_read_warn(parser, flags, 0, "\\t");
9439 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9440 return;
9441 default: {
9442 if (!char_is_ascii_printable(peeked)) {
9443 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9444 return;
9445 }
9446
9447 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);
9448 parser->current.end++;
9449 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9450 return;
9451 }
9452 }
9453 }
9454 case 'C': {
9455 parser->current.end++;
9456 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9457 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9458 }
9459
9460 if (peek(parser) != '-') {
9461 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9462 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9463 return;
9464 }
9465
9466 parser->current.end++;
9467 if (parser->current.end == parser->end) {
9468 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9469 return;
9470 }
9471
9472 uint8_t peeked = peek(parser);
9473 switch (peeked) {
9474 case '?': {
9475 parser->current.end++;
9476 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9477 return;
9478 }
9479 case '\\':
9480 parser->current.end++;
9481
9482 if (match(parser, 'u') || match(parser, 'U')) {
9483 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9484 return;
9485 }
9486
9487 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9488 return;
9489 case ' ':
9490 parser->current.end++;
9491 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9492 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9493 return;
9494 case '\t':
9495 parser->current.end++;
9496 escape_read_warn(parser, flags, 0, "\\t");
9497 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9498 return;
9499 default: {
9500 if (!char_is_ascii_printable(peeked)) {
9501 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9502 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9503 return;
9504 }
9505
9506 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);
9507 parser->current.end++;
9508 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9509 return;
9510 }
9511 }
9512 }
9513 case 'M': {
9514 parser->current.end++;
9515 if (flags & PM_ESCAPE_FLAG_META) {
9516 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
9517 }
9518
9519 if (peek(parser) != '-') {
9520 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9521 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9522 return;
9523 }
9524
9525 parser->current.end++;
9526 if (parser->current.end == parser->end) {
9527 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META);
9528 return;
9529 }
9530
9531 uint8_t peeked = peek(parser);
9532 switch (peeked) {
9533 case '\\':
9534 parser->current.end++;
9535
9536 if (match(parser, 'u') || match(parser, 'U')) {
9537 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9538 return;
9539 }
9540
9541 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_META);
9542 return;
9543 case ' ':
9544 parser->current.end++;
9545 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_META, "\\s");
9546 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9547 return;
9548 case '\t':
9549 parser->current.end++;
9550 escape_read_warn(parser, flags & ((uint8_t) ~PM_ESCAPE_FLAG_CONTROL), PM_ESCAPE_FLAG_META, "\\t");
9551 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9552 return;
9553 default:
9554 if (!char_is_ascii_printable(peeked)) {
9555 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9556 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9557 return;
9558 }
9559
9560 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);
9561 parser->current.end++;
9562 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9563 return;
9564 }
9565 }
9566 case '\r': {
9567 if (peek_offset(parser, 1) == '\n') {
9568 if (parser->heredoc_end == NULL) pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 2);
9569 parser->current.end += 2;
9570 escape_write_byte_encoded(parser, buffer, flags, escape_byte('\n', flags));
9571 return;
9572 }
9574 }
9575 default: {
9576 if ((flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) && !char_is_ascii_printable(peeked)) {
9577 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9578 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9579 return;
9580 }
9581 if (parser->current.end < parser->end) {
9582 escape_write_escape_encoded(parser, buffer, regular_expression_buffer, flags);
9583 } else {
9584 pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER);
9585 }
9586 return;
9587 }
9588 }
9589}
9590
9616static pm_token_type_t
9617lex_question_mark(pm_parser_t *parser) {
9618 if (lex_state_end_p(parser)) {
9619 lex_state_set(parser, PM_LEX_STATE_BEG);
9620 return PM_TOKEN_QUESTION_MARK;
9621 }
9622
9623 /*
9624 * A literal takes its encoding from its own contents. Literals that push a
9625 * lex mode clear this in lex_mode_push_*; a character literal is lexed
9626 * inline, so it clears the encoding here.
9627 */
9628 parser->explicit_encoding = NULL;
9629
9630 if (parser->current.end >= parser->end) {
9631 pm_parser_err_current(parser, PM_ERR_INCOMPLETE_QUESTION_MARK);
9632 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9633 return PM_TOKEN_CHARACTER_LITERAL;
9634 }
9635
9636 if (pm_char_is_whitespace(*parser->current.end)) {
9637 lex_state_set(parser, PM_LEX_STATE_BEG);
9638 return PM_TOKEN_QUESTION_MARK;
9639 }
9640
9641 lex_state_set(parser, PM_LEX_STATE_BEG);
9642
9643 if (match(parser, '\\')) {
9644 lex_state_set(parser, PM_LEX_STATE_END);
9645
9646 pm_buffer_t buffer;
9647 pm_buffer_init(&buffer, 3);
9648
9649 escape_read(parser, &buffer, NULL, PM_ESCAPE_FLAG_SINGLE);
9650
9651 // Copy buffer data into the arena and free the heap buffer.
9652 void *arena_data = pm_arena_memdup(parser->arena, buffer.value, buffer.length, PRISM_ALIGNOF(uint8_t));
9653 pm_string_constant_init(&parser->current_string, (const char *) arena_data, buffer.length);
9654 pm_buffer_cleanup(&buffer);
9655
9656 return PM_TOKEN_CHARACTER_LITERAL;
9657 } else {
9658 size_t encoding_width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9659
9660 // Ternary operators can have a ? immediately followed by an identifier
9661 // which starts with an underscore. We check for this case here.
9662 if (
9663 !(parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end) || peek(parser) == '_') ||
9664 (
9665 (parser->current.end + encoding_width >= parser->end) ||
9666 !char_is_identifier(parser, parser->current.end + encoding_width, parser->end - (parser->current.end + encoding_width))
9667 )
9668 ) {
9669 lex_state_set(parser, PM_LEX_STATE_END);
9670 parser->current.end += encoding_width;
9671 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9672 return PM_TOKEN_CHARACTER_LITERAL;
9673 }
9674 }
9675
9676 return PM_TOKEN_QUESTION_MARK;
9677}
9678
9683static pm_token_type_t
9684lex_at_variable(pm_parser_t *parser) {
9685 pm_token_type_t type = match(parser, '@') ? PM_TOKEN_CLASS_VARIABLE : PM_TOKEN_INSTANCE_VARIABLE;
9686 const uint8_t *end = parser->end;
9687
9688 size_t width;
9689 if ((width = char_is_identifier_start(parser, parser->current.end, end - parser->current.end)) > 0) {
9690 parser->current.end += width;
9691
9692 while ((width = char_is_identifier(parser, parser->current.end, end - parser->current.end)) > 0) {
9693 parser->current.end += width;
9694 }
9695 } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) {
9696 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
9697 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) {
9698 diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3;
9699 }
9700
9701 size_t width = parser->encoding->char_width(parser->current.end, end - parser->current.end);
9702 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start);
9703 } else {
9704 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_CLASS_VARIABLE_BARE : PM_ERR_INSTANCE_VARIABLE_BARE;
9705 pm_parser_err_token(parser, &parser->current, diag_id);
9706 }
9707
9708 // If we're lexing an embedded variable, then we need to pop back into the
9709 // parent lex context.
9710 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
9711 lex_mode_pop(parser);
9712 }
9713
9714 return type;
9715}
9716
9720static PRISM_INLINE void
9721parser_lex_callback(pm_parser_t *parser) {
9722 if (parser->lex_callback.callback) {
9723 parser->lex_callback.callback(parser, &parser->current, parser->lex_callback.data);
9724 }
9725}
9726
9731parser_comment(pm_parser_t *parser, pm_comment_type_t type) {
9732 pm_comment_t *comment = (pm_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_comment_t), PRISM_ALIGNOF(pm_comment_t));
9733
9734 *comment = (pm_comment_t) {
9735 .type = type,
9736 .location = TOK2LOC(parser, &parser->current)
9737 };
9738
9739 return comment;
9740}
9741
9747static pm_token_type_t
9748lex_embdoc(pm_parser_t *parser) {
9749 // First, lex out the EMBDOC_BEGIN token.
9750 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9751
9752 if (newline == NULL) {
9753 parser->current.end = parser->end;
9754 } else {
9755 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9756 parser->current.end = newline + 1;
9757 }
9758
9759 parser->current.type = PM_TOKEN_EMBDOC_BEGIN;
9760 parser_lex_callback(parser);
9761
9762 // Now, create a comment that is going to be attached to the parser.
9763 const uint8_t *comment_start = parser->current.start;
9764 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC);
9765
9766 // Now, loop until we find the end of the embedded documentation or the end
9767 // of the file.
9768 while (parser->current.end + 4 <= parser->end) {
9769 parser->current.start = parser->current.end;
9770
9771 // If we've hit the end of the embedded documentation then we'll return
9772 // that token here.
9773 if (
9774 (memcmp(parser->current.end, "=end", 4) == 0) &&
9775 (
9776 (parser->current.end + 4 == parser->end) || // end of file
9777 pm_char_is_whitespace(parser->current.end[4]) || // whitespace
9778 (parser->current.end[4] == '\0') || // NUL or end of script
9779 (parser->current.end[4] == '\004') || // ^D
9780 (parser->current.end[4] == '\032') // ^Z
9781 )
9782 ) {
9783 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9784
9785 if (newline == NULL) {
9786 parser->current.end = parser->end;
9787 } else {
9788 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9789 parser->current.end = newline + 1;
9790 }
9791
9792 parser->current.type = PM_TOKEN_EMBDOC_END;
9793 parser_lex_callback(parser);
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_EMBDOC_END;
9799 }
9800
9801 // Otherwise, we'll parse until the end of the line and return a line of
9802 // embedded documentation.
9803 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9804
9805 if (newline == NULL) {
9806 parser->current.end = parser->end;
9807 } else {
9808 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9809 parser->current.end = newline + 1;
9810 }
9811
9812 parser->current.type = PM_TOKEN_EMBDOC_LINE;
9813 parser_lex_callback(parser);
9814 }
9815
9816 pm_parser_err_current(parser, PM_ERR_EMBDOC_TERM);
9817
9818 comment->location.length = (uint32_t) (parser->current.end - comment_start);
9819 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9820
9821 return PM_TOKEN_EOF;
9822}
9823
9829static PRISM_INLINE void
9830parser_lex_ignored_newline(pm_parser_t *parser) {
9831 parser->current.type = PM_TOKEN_IGNORED_NEWLINE;
9832 parser_lex_callback(parser);
9833}
9834
9844static PRISM_INLINE void
9845parser_flush_heredoc_end(pm_parser_t *parser) {
9846 assert(parser->heredoc_end <= parser->end);
9847 parser->next_start = parser->heredoc_end;
9848 parser->heredoc_end = NULL;
9849}
9850
9854static bool
9855parser_end_of_line_p(const pm_parser_t *parser) {
9856 const uint8_t *cursor = parser->current.end;
9857
9858 while (cursor < parser->end && *cursor != '\n' && *cursor != '#') {
9859 if (!pm_char_is_inline_whitespace(*cursor++)) return false;
9860 }
9861
9862 return true;
9863}
9864
9883typedef struct {
9889
9894 const uint8_t *cursor;
9896
9916
9920static PRISM_INLINE void
9921pm_token_buffer_push_byte(pm_token_buffer_t *token_buffer, uint8_t byte) {
9922 pm_buffer_append_byte(&token_buffer->buffer, byte);
9923}
9924
9925static PRISM_INLINE void
9926pm_regexp_token_buffer_push_byte(pm_regexp_token_buffer_t *token_buffer, uint8_t byte) {
9927 pm_buffer_append_byte(&token_buffer->regexp_buffer, byte);
9928}
9929
9933static PRISM_INLINE size_t
9934parser_char_width(const pm_parser_t *parser) {
9935 size_t width;
9936 if (parser->encoding_changed) {
9937 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9938 } else {
9939 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9940 }
9941
9942 // TODO: If the character is invalid in the given encoding, then we'll just
9943 // push one byte into the buffer. This should actually be an error.
9944 return (width == 0 ? 1 : width);
9945}
9946
9950static void
9951pm_token_buffer_push_escaped(pm_token_buffer_t *token_buffer, pm_parser_t *parser) {
9952 size_t width = parser_char_width(parser);
9953 pm_buffer_append_bytes(&token_buffer->buffer, parser->current.end, width);
9954 parser->current.end += width;
9955}
9956
9957static void
9958pm_regexp_token_buffer_push_escaped(pm_regexp_token_buffer_t *token_buffer, pm_parser_t *parser) {
9959 size_t width = parser_char_width(parser);
9960 const uint8_t *start = parser->current.end;
9961 pm_buffer_append_bytes(&token_buffer->base.buffer, start, width);
9962 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, width);
9963 parser->current.end += width;
9964}
9965
9972static PRISM_INLINE void
9973pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9974 // Copy buffer data into the arena and free the heap buffer.
9975 size_t len = pm_buffer_length(&token_buffer->buffer);
9976 void *arena_data = pm_arena_memdup(parser->arena, pm_buffer_value(&token_buffer->buffer), len, PRISM_ALIGNOF(uint8_t));
9977 pm_string_constant_init(&parser->current_string, (const char *) arena_data, len);
9978 pm_buffer_cleanup(&token_buffer->buffer);
9979}
9980
9981static PRISM_INLINE void
9982pm_regexp_token_buffer_copy(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9983 pm_token_buffer_copy(parser, &token_buffer->base);
9984 pm_buffer_cleanup(&token_buffer->regexp_buffer);
9985}
9986
9996static void
9997pm_token_buffer_flush(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9998 if (token_buffer->cursor == NULL) {
9999 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
10000 } else {
10001 pm_buffer_append_bytes(&token_buffer->buffer, token_buffer->cursor, (size_t) (parser->current.end - token_buffer->cursor));
10002 pm_token_buffer_copy(parser, token_buffer);
10003 }
10004}
10005
10006static void
10007pm_regexp_token_buffer_flush(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
10008 if (token_buffer->base.cursor == NULL) {
10009 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
10010 } else {
10011 const uint8_t *cursor = token_buffer->base.cursor;
10012 size_t length = (size_t) (parser->current.end - cursor);
10013 pm_buffer_append_bytes(&token_buffer->base.buffer, cursor, length);
10014 pm_buffer_append_bytes(&token_buffer->regexp_buffer, cursor, length);
10015 pm_regexp_token_buffer_copy(parser, token_buffer);
10016 }
10017}
10018
10019#define PM_TOKEN_BUFFER_DEFAULT_SIZE 16
10020
10029static void
10030pm_token_buffer_escape(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
10031 const uint8_t *start;
10032 if (token_buffer->cursor == NULL) {
10033 pm_buffer_init(&token_buffer->buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
10034 start = parser->current.start;
10035 } else {
10036 start = token_buffer->cursor;
10037 }
10038
10039 const uint8_t *end = parser->current.end - 1;
10040 assert(end >= start);
10041 pm_buffer_append_bytes(&token_buffer->buffer, start, (size_t) (end - start));
10042
10043 token_buffer->cursor = end;
10044}
10045
10046static void
10047pm_regexp_token_buffer_escape(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
10048 const uint8_t *start;
10049 if (token_buffer->base.cursor == NULL) {
10050 pm_buffer_init(&token_buffer->base.buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
10051 pm_buffer_init(&token_buffer->regexp_buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
10052 start = parser->current.start;
10053 } else {
10054 start = token_buffer->base.cursor;
10055 }
10056
10057 const uint8_t *end = parser->current.end - 1;
10058 pm_buffer_append_bytes(&token_buffer->base.buffer, start, (size_t) (end - start));
10059 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, (size_t) (end - start));
10060
10061 token_buffer->base.cursor = end;
10062}
10063
10064#undef PM_TOKEN_BUFFER_DEFAULT_SIZE
10065
10070static PRISM_INLINE size_t
10071pm_heredoc_strspn_inline_whitespace(pm_parser_t *parser, const uint8_t **cursor, pm_heredoc_indent_t indent) {
10072 size_t whitespace = 0;
10073
10074 switch (indent) {
10075 case PM_HEREDOC_INDENT_NONE:
10076 // Do nothing, we can't match a terminator with
10077 // indentation and there's no need to calculate common
10078 // whitespace.
10079 break;
10080 case PM_HEREDOC_INDENT_DASH:
10081 // Skip past inline whitespace.
10082 *cursor += pm_strspn_inline_whitespace(*cursor, parser->end - *cursor);
10083 break;
10084 case PM_HEREDOC_INDENT_TILDE:
10085 // Skip past inline whitespace and calculate common
10086 // whitespace.
10087 while (*cursor < parser->end && pm_char_is_inline_whitespace(**cursor)) {
10088 if (**cursor == '\t') {
10089 whitespace = (whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
10090 } else {
10091 whitespace++;
10092 }
10093 (*cursor)++;
10094 }
10095
10096 break;
10097 }
10098
10099 return whitespace;
10100}
10101
10106static uint8_t
10107pm_lex_percent_delimiter(pm_parser_t *parser) {
10108 size_t eol_length = match_eol(parser);
10109
10110 if (eol_length) {
10111 if (parser->heredoc_end) {
10112 // If we have already lexed a heredoc, then the newline has already
10113 // been added to the list. In this case we want to just flush the
10114 // heredoc end.
10115 parser_flush_heredoc_end(parser);
10116 } else {
10117 // Otherwise, we'll add the newline to the list of newlines.
10118 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + U32(eol_length));
10119 }
10120
10121 uint8_t delimiter = *parser->current.end;
10122
10123 // If our delimiter is \r\n, we want to treat it as if it's \n.
10124 // For example, %\r\nfoo\r\n should be "foo"
10125 if (eol_length == 2) {
10126 delimiter = *(parser->current.end + 1);
10127 }
10128
10129 parser->current.end += eol_length;
10130 return delimiter;
10131 }
10132
10133 return *parser->current.end++;
10134}
10135
10140#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return
10141
10148static void
10149parser_lex(pm_parser_t *parser) {
10150 assert(parser->current.end <= parser->end);
10151 parser->previous = parser->current;
10152
10153 // This value mirrors cmd_state from CRuby.
10154 bool previous_command_start = parser->command_start;
10155 parser->command_start = false;
10156
10157 // This is used to communicate to the newline lexing function that we've
10158 // already seen a comment.
10159 bool lexed_comment = false;
10160
10161 // Here we cache the current value of the semantic token seen flag. This is
10162 // used to reset it in case we find a token that shouldn't flip this flag.
10163 unsigned int semantic_token_seen = parser->semantic_token_seen;
10164 parser->semantic_token_seen = true;
10165
10166 // We'll jump to this label when we are about to encounter an EOF.
10167 // If we still have lex_modes on the stack, we pop them so that cleanup
10168 // can happen. For example, we should still continue parsing after a heredoc
10169 // identifier, even if the heredoc body was syntax invalid.
10170 switch_lex_modes:
10171
10172 switch (parser->lex_modes.current->mode) {
10173 case PM_LEX_DEFAULT:
10174 case PM_LEX_EMBEXPR:
10175 case PM_LEX_EMBVAR:
10176
10177 // We have a specific named label here because we are going to jump back to
10178 // this location in the event that we have lexed a token that should not be
10179 // returned to the parser. This includes comments, ignored newlines, and
10180 // invalid tokens of some form.
10181 lex_next_token: {
10182 // If we have the special next_start pointer set, then we're going to jump
10183 // to that location and start lexing from there.
10184 if (parser->next_start != NULL) {
10185 parser->current.end = parser->next_start;
10186 parser->next_start = NULL;
10187 }
10188
10189 // This value mirrors space_seen from CRuby. It tracks whether or not
10190 // space has been eaten before the start of the next token.
10191 bool space_seen = false;
10192
10193 // First, we're going to skip past any whitespace at the front of the next
10194 // token. Skip runs of inline whitespace in bulk to avoid per-character
10195 // stores back to parser->current.end.
10196 bool chomping = true;
10197 while (chomping) {
10198 /* Skip the run of inline whitespace in bulk, then decide what
10199 * to do based on the first byte after it. Handling both in a
10200 * single pass avoids re-entering the scan when the run was
10201 * non-empty, which is the common case. */
10202 {
10203 static const uint8_t inline_whitespace[256] = {
10204 [' '] = 1, ['\t'] = 1, ['\f'] = 1, ['\v'] = 1
10205 };
10206 const uint8_t *scan = parser->current.end;
10207 while (scan < parser->end && inline_whitespace[*scan]) scan++;
10208 if (scan > parser->current.end) {
10209 parser->current.end = scan;
10210 space_seen = true;
10211 }
10212 if (scan >= parser->end) break;
10213 }
10214
10215 switch (*parser->current.end) {
10216 case '\r':
10217 if (match_eol_offset(parser, 1)) {
10218 chomping = false;
10219 } else {
10220 pm_parser_warn(parser, PM_TOKEN_END(parser, &parser->current), 1, PM_WARN_UNEXPECTED_CARRIAGE_RETURN);
10221 parser->current.end++;
10222 space_seen = true;
10223 }
10224 break;
10225 case '\\': {
10226 size_t eol_length = match_eol_offset(parser, 1);
10227 if (eol_length) {
10228 if (parser->heredoc_end) {
10229 parser->current.end = parser->heredoc_end;
10230 parser->heredoc_end = NULL;
10231 } else {
10232 parser->current.end += eol_length + 1;
10233 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10234 space_seen = true;
10235 }
10236 } else if (pm_char_is_inline_whitespace(*parser->current.end)) {
10237 parser->current.end += 2;
10238 } else {
10239 chomping = false;
10240 }
10241
10242 break;
10243 }
10244 default:
10245 chomping = false;
10246 break;
10247 }
10248 }
10249
10250 // Next, we'll set to start of this token to be the current end.
10251 parser->current.start = parser->current.end;
10252
10253 // We'll check if we're at the end of the file. If we are, then we
10254 // need to return the EOF token.
10255 if (parser->current.end >= parser->end) {
10256 // We may be missing closing tokens. We should pop modes one by one
10257 // to do the appropriate cleanup like moving next_start for heredocs.
10258 // Only when no mode is remaining will we actually emit the EOF token.
10259 if (parser->lex_modes.current->mode != PM_LEX_DEFAULT) {
10260 lex_mode_pop(parser);
10261 goto switch_lex_modes;
10262 }
10263
10264 // If we hit EOF, but the EOF came immediately after a newline,
10265 // set the start of the token to the newline. This way any EOF
10266 // errors will be reported as happening on that line rather than
10267 // a line after. For example "foo(\n" should report an error
10268 // on line 1 even though EOF technically occurs on line 2.
10269 if (parser->current.start > parser->start && (*(parser->current.start - 1) == '\n')) {
10270 parser->current.start -= 1;
10271 }
10272 LEX(PM_TOKEN_EOF);
10273 }
10274
10275 // Finally, we'll check the current character to determine the next
10276 // token.
10277 switch (*parser->current.end++) {
10278 case '\0': // NUL or end of script
10279 case '\004': // ^D
10280 case '\032': // ^Z
10281 parser->current.end--;
10282 LEX(PM_TOKEN_EOF);
10283
10284 case '#': { // comments
10285 const uint8_t *ending = next_newline(parser->current.end, parser->end - parser->current.end);
10286 parser->current.end = ending == NULL ? parser->end : ending;
10287
10288 // If we found a comment while lexing, then we're going to
10289 // add it to the list of comments in the file and keep
10290 // lexing.
10291 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE);
10292 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
10293
10294 parser->current.type = PM_TOKEN_COMMENT;
10295 parser_lex_callback(parser);
10296
10297 // Here, parse the comment to see if it's a magic comment
10298 // and potentially change state on the parser.
10299 if (!parser_lex_magic_comment(parser, semantic_token_seen) && (parser->current.start == parser->encoding_comment_start)) {
10300 ptrdiff_t length = parser->current.end - parser->current.start;
10301
10302 // If we didn't find a magic comment within the first
10303 // pass and we're at the start of the file, then we need
10304 // to do another pass to potentially find other patterns
10305 // for encoding comments.
10306 if (length >= 10 && !parser->encoding_locked) {
10307 parser_lex_magic_comment_encoding(parser);
10308 }
10309 }
10310
10311 /* The comment does not include its terminating newline,
10312 * which lexes through the newline handling below as its
10313 * own token. A comment that ends the file has no newline,
10314 * so the newline handling runs without one to emit. */
10315 if (ending == NULL) {
10316 lexed_comment = true;
10317 } else {
10318 parser->current.start = ending;
10319 parser->current.end = ending + 1;
10320 }
10321 }
10323 case '\r':
10324 case '\n': {
10325 parser->semantic_token_seen = semantic_token_seen & 0x1;
10326 size_t eol_length = match_eol_at(parser, parser->current.end - 1);
10327
10328 if (eol_length) {
10329 // The only way you can have carriage returns in this
10330 // particular loop is if you have a carriage return
10331 // followed by a newline. In that case we'll just skip
10332 // over the carriage return and continue lexing, in
10333 // order to make it so that the newline token
10334 // encapsulates both the carriage return and the
10335 // newline. Note that we need to check that we haven't
10336 // already lexed a comment here because that falls
10337 // through into here as well.
10338 if (!lexed_comment) {
10339 parser->current.end += eol_length - 1; // skip CR
10340 }
10341
10342 if (parser->heredoc_end == NULL) {
10343 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10344 }
10345 }
10346
10347 if (parser->heredoc_end) {
10348 parser_flush_heredoc_end(parser);
10349 }
10350
10351 // If this is an ignored newline, then we can continue lexing after
10352 // calling the callback with the ignored newline token.
10353 switch (lex_state_ignored_p(parser)) {
10354 case PM_IGNORED_NEWLINE_NONE:
10355 break;
10356 case PM_IGNORED_NEWLINE_PATTERN:
10357 if (parser->pattern_matching_newlines || parser->in_keyword_arg) {
10358 if (!lexed_comment) {
10359 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10360 parser_lex_callback(parser);
10361 }
10362
10363 lex_state_set(parser, PM_LEX_STATE_BEG);
10364 parser->command_start = true;
10365 parser->current.type = PM_TOKEN_NEWLINE;
10366 return;
10367 }
10369 case PM_IGNORED_NEWLINE_ALL:
10370 if (!lexed_comment) parser_lex_ignored_newline(parser);
10371 lexed_comment = false;
10372 goto lex_next_token;
10373 }
10374
10375 // Here we need to look ahead and see if there is a call operator
10376 // (either . or &.) that starts the next line. If there is, then this
10377 // is going to become an ignored newline and we're going to instead
10378 // return the call operator.
10379 const uint8_t *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start;
10380 next_content += pm_strspn_inline_whitespace(next_content, parser->end - next_content);
10381
10382 if (next_content < parser->end) {
10383 // If we hit a comment after a newline, then we're going to check
10384 // if it's ignored or if it's followed by a method call ('.').
10385 // If it is, then we're going to call the
10386 // callback with an ignored newline and then continue lexing.
10387 // Otherwise we'll return a regular newline.
10388 if (next_content[0] == '#') {
10389 // Here we look for a "." or "&." following a "\n".
10390 const uint8_t *following = next_newline(next_content, parser->end - next_content);
10391
10392 while (following && (following + 1 < parser->end)) {
10393 following++;
10394 following += pm_strspn_inline_whitespace(following, parser->end - following);
10395
10396 // If this is not followed by a comment, then we can break out
10397 // of this loop.
10398 if (peek_at(parser, following) != '#') break;
10399
10400 // If there is a comment, then we need to find the end of the
10401 // comment and continue searching from there.
10402 following = next_newline(following, parser->end - following);
10403 }
10404
10405 // If the lex state was ignored, we will lex the
10406 // ignored newline.
10407 if (lex_state_ignored_p(parser)) {
10408 if (!lexed_comment) parser_lex_ignored_newline(parser);
10409 lexed_comment = false;
10410 goto lex_next_token;
10411 }
10412
10413 // If we hit a '.' or a '&.' we will lex the ignored
10414 // newline.
10415 if (following && (
10416 (peek_at(parser, following) == '.') ||
10417 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10418 )) {
10419 if (!lexed_comment) parser_lex_ignored_newline(parser);
10420 lexed_comment = false;
10421 goto lex_next_token;
10422 }
10423
10424
10425 // If we are parsing as CRuby 4.0 or later and we
10426 // hit a '&&' or a '||' then we will lex the ignored
10427 // newline.
10428 if (
10429 (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) &&
10430 following && (
10431 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
10432 (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
10433 (
10434 peek_at(parser, following) == 'a' &&
10435 peek_at(parser, following + 1) == 'n' &&
10436 peek_at(parser, following + 2) == 'd' &&
10437 peek_at(parser, next_content + 3) != '!' &&
10438 peek_at(parser, next_content + 3) != '?' &&
10439 !char_is_identifier(parser, following + 3, parser->end - (following + 3))
10440 ) ||
10441 (
10442 peek_at(parser, following) == 'o' &&
10443 peek_at(parser, following + 1) == 'r' &&
10444 peek_at(parser, next_content + 2) != '!' &&
10445 peek_at(parser, next_content + 2) != '?' &&
10446 !char_is_identifier(parser, following + 2, parser->end - (following + 2))
10447 )
10448 )
10449 ) {
10450 if (!lexed_comment) parser_lex_ignored_newline(parser);
10451 lexed_comment = false;
10452 goto lex_next_token;
10453 }
10454 }
10455
10456 // If we hit a . after a newline, then we're in a call chain and
10457 // we need to return the call operator.
10458 if (next_content[0] == '.') {
10459 /* A beginless range on the next line means this
10460 * newline terminates the statement rather than
10461 * continuing a method chain. */
10462 if (peek_at(parser, next_content + 1) == '.') {
10463 if (!lexed_comment) {
10464 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10465 parser_lex_callback(parser);
10466 }
10467
10468 lex_state_set(parser, PM_LEX_STATE_BEG);
10469 parser->command_start = true;
10470 parser->current.type = PM_TOKEN_NEWLINE;
10471 return;
10472 }
10473
10474 if (!lexed_comment) parser_lex_ignored_newline(parser);
10475 lex_state_set(parser, PM_LEX_STATE_DOT);
10476 parser->current.start = next_content;
10477 parser->current.end = next_content + 1;
10478 parser->next_start = NULL;
10479 LEX(PM_TOKEN_DOT);
10480 }
10481
10482 // If we hit a &. after a newline, then we're in a call chain and
10483 // we need to return the call 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_DOT);
10487 parser->current.start = next_content;
10488 parser->current.end = next_content + 2;
10489 parser->next_start = NULL;
10490 LEX(PM_TOKEN_AMPERSAND_DOT);
10491 }
10492
10493 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
10494 // If we hit an && then we are in a logical chain
10495 // and we need to return the logical operator.
10496 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
10497 if (!lexed_comment) parser_lex_ignored_newline(parser);
10498 lex_state_set(parser, PM_LEX_STATE_BEG);
10499 parser->current.start = next_content;
10500 parser->current.end = next_content + 2;
10501 parser->next_start = NULL;
10502 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10503 }
10504
10505 // If we hit a || then we are in a logical chain and
10506 // we need to return the logical operator.
10507 if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
10508 if (!lexed_comment) parser_lex_ignored_newline(parser);
10509 lex_state_set(parser, PM_LEX_STATE_BEG);
10510 parser->current.start = next_content;
10511 parser->current.end = next_content + 2;
10512 parser->next_start = NULL;
10513 LEX(PM_TOKEN_PIPE_PIPE);
10514 }
10515
10516 // If we hit an 'and' then we are in a logical chain
10517 // and we need to return the logical operator.
10518 if (
10519 peek_at(parser, next_content) == 'a' &&
10520 peek_at(parser, next_content + 1) == 'n' &&
10521 peek_at(parser, next_content + 2) == 'd' &&
10522 peek_at(parser, next_content + 3) != '!' &&
10523 peek_at(parser, next_content + 3) != '?' &&
10524 !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
10525 ) {
10526 if (!lexed_comment) parser_lex_ignored_newline(parser);
10527 lex_state_set(parser, PM_LEX_STATE_BEG);
10528 parser->current.start = next_content;
10529 parser->current.end = next_content + 3;
10530 parser->next_start = NULL;
10531 parser->command_start = true;
10532 LEX(PM_TOKEN_KEYWORD_AND);
10533 }
10534
10535 // If we hit a 'or' then we are in a logical chain
10536 // and we need to return the logical operator.
10537 if (
10538 peek_at(parser, next_content) == 'o' &&
10539 peek_at(parser, next_content + 1) == 'r' &&
10540 peek_at(parser, next_content + 2) != '!' &&
10541 peek_at(parser, next_content + 2) != '?' &&
10542 !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
10543 ) {
10544 if (!lexed_comment) parser_lex_ignored_newline(parser);
10545 lex_state_set(parser, PM_LEX_STATE_BEG);
10546 parser->current.start = next_content;
10547 parser->current.end = next_content + 2;
10548 parser->next_start = NULL;
10549 parser->command_start = true;
10550 LEX(PM_TOKEN_KEYWORD_OR);
10551 }
10552 }
10553 }
10554
10555 // At this point we know this is a regular newline, and we can set the
10556 // necessary state and return the token.
10557 lex_state_set(parser, PM_LEX_STATE_BEG);
10558 parser->command_start = true;
10559 parser->current.type = PM_TOKEN_NEWLINE;
10560 if (!lexed_comment) parser_lex_callback(parser);
10561 return;
10562 }
10563
10564 // ,
10565 case ',':
10566 if ((parser->previous.type == PM_TOKEN_COMMA) && (parser->enclosure_nesting > 0)) {
10567 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
10568 }
10569
10570 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10571 LEX(PM_TOKEN_COMMA);
10572
10573 // (
10574 case '(': {
10575 /* A parenthesis scanned at the beginning of an expression
10576 * groups the expression it wraps, while one scanned in
10577 * argument position with a preceding space wraps a command
10578 * argument. Everything else opens an argument list. */
10579 pm_token_type_t type = PM_TOKEN_PARENTHESIS_LEFT;
10580
10581 if (lex_state_beg_p(parser)) {
10582 type = PM_TOKEN_PARENTHESIS_LEFT_GROUPING;
10583 } else if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (PM_LEX_STATE_END | PM_LEX_STATE_LABEL))) {
10584 type = PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES;
10585 }
10586
10587 parser->enclosure_nesting++;
10588 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10589 pm_enclosure_frame_push(parser);
10590 LEX(type);
10591 }
10592
10593 // )
10594 case ')':
10595 parser->enclosure_nesting--;
10596 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10597 pm_enclosure_frame_pop(parser);
10598 LEX(PM_TOKEN_PARENTHESIS_RIGHT);
10599
10600 // ;
10601 case ';':
10602 lex_state_set(parser, PM_LEX_STATE_BEG);
10603 parser->command_start = true;
10604 LEX(PM_TOKEN_SEMICOLON);
10605
10606 // [ [] []=
10607 case '[':
10608 parser->enclosure_nesting++;
10609 pm_token_type_t type = PM_TOKEN_BRACKET_LEFT;
10610
10611 if (lex_state_operator_p(parser)) {
10612 if (match(parser, ']')) {
10613 parser->enclosure_nesting--;
10614 lex_state_set(parser, PM_LEX_STATE_ARG);
10615 LEX(match(parser, '=') ? PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : PM_TOKEN_BRACKET_LEFT_RIGHT);
10616 }
10617
10618 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABEL);
10619 LEX(type);
10620 }
10621
10622 if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, PM_LEX_STATE_LABELED)))) {
10623 type = PM_TOKEN_BRACKET_LEFT_ARRAY;
10624 }
10625
10626 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10627 pm_enclosure_frame_push(parser);
10628 LEX(type);
10629
10630 // ]
10631 case ']':
10632 parser->enclosure_nesting--;
10633 lex_state_set(parser, PM_LEX_STATE_END);
10634 pm_enclosure_frame_pop(parser);
10635 LEX(PM_TOKEN_BRACKET_RIGHT);
10636
10637 // {
10638 case '{': {
10639 pm_token_type_t type = PM_TOKEN_BRACE_LEFT;
10640
10641 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
10642 /* This { begins a lambda */
10643 parser->command_start = true;
10644 lex_state_set(parser, PM_LEX_STATE_BEG);
10645 type = PM_TOKEN_LAMBDA_BEGIN;
10646 } else if (lex_state_p(parser, PM_LEX_STATE_LABELED)) {
10647 /* This { begins a hash literal */
10648 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10649 type = PM_TOKEN_BRACE_LEFT_HASH;
10650 } else if (lex_state_p(parser, PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_END | PM_LEX_STATE_ENDFN)) {
10651 /* This { begins a block */
10652 parser->command_start = true;
10653 lex_state_set(parser, PM_LEX_STATE_BEG);
10654 } else if (lex_state_p(parser, PM_LEX_STATE_ENDARG)) {
10655 /* This { begins a block following a parenthesized
10656 * command argument */
10657 parser->command_start = true;
10658 lex_state_set(parser, PM_LEX_STATE_BEG);
10659 type = PM_TOKEN_BRACE_LEFT_ARGUMENT;
10660 } else {
10661 /* This { begins a hash literal */
10662 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10663 type = PM_TOKEN_BRACE_LEFT_HASH;
10664 }
10665
10666 parser->enclosure_nesting++;
10667 parser->brace_nesting++;
10668 pm_enclosure_frame_push(parser);
10669
10670 LEX(type);
10671 }
10672
10673 // }
10674 case '}':
10675 parser->enclosure_nesting--;
10676 pm_enclosure_frame_pop(parser);
10677
10678 if ((parser->lex_modes.current->mode == PM_LEX_EMBEXPR) && (parser->brace_nesting == 0)) {
10679 lex_mode_pop(parser);
10680 LEX(PM_TOKEN_EMBEXPR_END);
10681 }
10682
10683 parser->brace_nesting--;
10684 lex_state_set(parser, PM_LEX_STATE_END);
10685 LEX(PM_TOKEN_BRACE_RIGHT);
10686
10687 // * ** **= *=
10688 case '*': {
10689 if (match(parser, '*')) {
10690 if (match(parser, '=')) {
10691 lex_state_set(parser, PM_LEX_STATE_BEG);
10692 LEX(PM_TOKEN_STAR_STAR_EQUAL);
10693 }
10694
10695 pm_token_type_t type = PM_TOKEN_STAR_STAR;
10696
10697 if (lex_state_spcarg_p(parser, space_seen)) {
10698 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR);
10699 type = PM_TOKEN_USTAR_STAR;
10700 } else if (lex_state_beg_p(parser)) {
10701 type = PM_TOKEN_USTAR_STAR;
10702 } else if (ambiguous_operator_p(parser, space_seen)) {
10703 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "**", "argument prefix");
10704 }
10705
10706 if (lex_state_operator_p(parser)) {
10707 lex_state_set(parser, PM_LEX_STATE_ARG);
10708 } else {
10709 lex_state_set(parser, PM_LEX_STATE_BEG);
10710 }
10711
10712 LEX(type);
10713 }
10714
10715 if (match(parser, '=')) {
10716 lex_state_set(parser, PM_LEX_STATE_BEG);
10717 LEX(PM_TOKEN_STAR_EQUAL);
10718 }
10719
10720 pm_token_type_t type = PM_TOKEN_STAR;
10721
10722 if (lex_state_spcarg_p(parser, space_seen)) {
10723 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR);
10724 type = PM_TOKEN_USTAR;
10725 } else if (lex_state_beg_p(parser)) {
10726 type = PM_TOKEN_USTAR;
10727 } else if (ambiguous_operator_p(parser, space_seen)) {
10728 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "*", "argument prefix");
10729 }
10730
10731 if (lex_state_operator_p(parser)) {
10732 lex_state_set(parser, PM_LEX_STATE_ARG);
10733 } else {
10734 lex_state_set(parser, PM_LEX_STATE_BEG);
10735 }
10736
10737 LEX(type);
10738 }
10739
10740 // ! != !~ !@
10741 case '!':
10742 if (lex_state_operator_p(parser)) {
10743 lex_state_set(parser, PM_LEX_STATE_ARG);
10744 if (match(parser, '@')) {
10745 LEX(PM_TOKEN_BANG);
10746 }
10747 } else {
10748 lex_state_set(parser, PM_LEX_STATE_BEG);
10749 }
10750
10751 if (match(parser, '=')) {
10752 LEX(PM_TOKEN_BANG_EQUAL);
10753 }
10754
10755 if (match(parser, '~')) {
10756 LEX(PM_TOKEN_BANG_TILDE);
10757 }
10758
10759 LEX(PM_TOKEN_BANG);
10760
10761 // = => =~ == === =begin
10762 case '=':
10763 if (
10764 current_token_starts_line(parser) &&
10765 (parser->current.end + 5 <= parser->end) &&
10766 memcmp(parser->current.end, "begin", 5) == 0 &&
10767 (pm_char_is_whitespace(peek_offset(parser, 5)) || (peek_offset(parser, 5) == '\0'))
10768 ) {
10769 pm_token_type_t type = lex_embdoc(parser);
10770 if (type == PM_TOKEN_EOF) {
10771 LEX(type);
10772 }
10773
10774 goto lex_next_token;
10775 }
10776
10777 if (lex_state_operator_p(parser)) {
10778 lex_state_set(parser, PM_LEX_STATE_ARG);
10779 } else {
10780 lex_state_set(parser, PM_LEX_STATE_BEG);
10781 }
10782
10783 if (match(parser, '>')) {
10784 LEX(PM_TOKEN_EQUAL_GREATER);
10785 }
10786
10787 if (match(parser, '~')) {
10788 LEX(PM_TOKEN_EQUAL_TILDE);
10789 }
10790
10791 if (match(parser, '=')) {
10792 LEX(match(parser, '=') ? PM_TOKEN_EQUAL_EQUAL_EQUAL : PM_TOKEN_EQUAL_EQUAL);
10793 }
10794
10795 LEX(PM_TOKEN_EQUAL);
10796
10797 // < << <<= <= <=>
10798 case '<':
10799 if (match(parser, '<')) {
10800 if (
10801 !lex_state_p(parser, PM_LEX_STATE_DOT | PM_LEX_STATE_CLASS) &&
10802 !lex_state_end_p(parser) &&
10803 (!lex_state_p(parser, PM_LEX_STATE_ARG_ANY) || lex_state_p(parser, PM_LEX_STATE_LABELED) || space_seen)
10804 ) {
10805 const uint8_t *end = parser->current.end;
10806
10807 pm_heredoc_quote_t quote = PM_HEREDOC_QUOTE_NONE;
10808 pm_heredoc_indent_t indent = PM_HEREDOC_INDENT_NONE;
10809
10810 if (match(parser, '-')) {
10811 indent = PM_HEREDOC_INDENT_DASH;
10812 }
10813 else if (match(parser, '~')) {
10814 indent = PM_HEREDOC_INDENT_TILDE;
10815 }
10816
10817 if (match(parser, '`')) {
10818 quote = PM_HEREDOC_QUOTE_BACKTICK;
10819 }
10820 else if (match(parser, '"')) {
10821 quote = PM_HEREDOC_QUOTE_DOUBLE;
10822 }
10823 else if (match(parser, '\'')) {
10824 quote = PM_HEREDOC_QUOTE_SINGLE;
10825 }
10826
10827 const uint8_t *ident_start = parser->current.end;
10828 size_t width = 0;
10829
10830 if (parser->current.end >= parser->end) {
10831 parser->current.end = end;
10832 } else if (quote == PM_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) == 0) {
10833 parser->current.end = end;
10834 } else {
10835 if (quote == PM_HEREDOC_QUOTE_NONE) {
10836 parser->current.end += width;
10837
10838 while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end))) {
10839 parser->current.end += width;
10840 }
10841 } else {
10842 // If we have quotes, then we're going to go until we find the
10843 // end quote.
10844 while ((parser->current.end < parser->end) && quote != (pm_heredoc_quote_t) (*parser->current.end)) {
10845 if (*parser->current.end == '\r' || *parser->current.end == '\n') break;
10846 parser->current.end++;
10847 }
10848 }
10849
10850 size_t ident_length = (size_t) (parser->current.end - ident_start);
10851 bool ident_error = false;
10852
10853 if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) {
10854 pm_parser_err(parser, U32(ident_start - parser->start), U32(ident_length), PM_ERR_HEREDOC_IDENTIFIER);
10855 ident_error = true;
10856 }
10857
10858 parser->explicit_encoding = NULL;
10859 lex_mode_push(parser, (pm_lex_mode_t) {
10860 .mode = PM_LEX_HEREDOC,
10861 .as.heredoc = {
10862 .base = {
10863 .ident_start = ident_start,
10864 .ident_length = ident_length,
10865 .quote = quote,
10866 .indent = indent
10867 },
10868 .next_start = parser->current.end,
10869 .common_whitespace = NULL,
10870 .line_continuation = false
10871 }
10872 });
10873
10874 if (parser->heredoc_end == NULL) {
10875 const uint8_t *body_start = next_newline(parser->current.end, parser->end - parser->current.end);
10876
10877 if (body_start == NULL) {
10878 // If there is no newline after the heredoc identifier, then
10879 // this is not a valid heredoc declaration. In this case we
10880 // will add an error, but we will still return a heredoc
10881 // start.
10882 if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length);
10883 body_start = parser->end;
10884 } else {
10885 // Otherwise, we want to indicate that the body of the
10886 // heredoc starts on the character after the next newline.
10887 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(body_start - parser->start + 1));
10888 body_start++;
10889 }
10890
10891 parser->next_start = body_start;
10892 } else {
10893 parser->next_start = parser->heredoc_end;
10894 }
10895
10896 LEX(PM_TOKEN_HEREDOC_START);
10897 }
10898 }
10899
10900 if (match(parser, '=')) {
10901 lex_state_set(parser, PM_LEX_STATE_BEG);
10902 LEX(PM_TOKEN_LESS_LESS_EQUAL);
10903 }
10904
10905 if (ambiguous_operator_p(parser, space_seen)) {
10906 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "<<", "here document");
10907 }
10908
10909 if (lex_state_operator_p(parser)) {
10910 lex_state_set(parser, PM_LEX_STATE_ARG);
10911 } else {
10912 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10913 lex_state_set(parser, PM_LEX_STATE_BEG);
10914 }
10915
10916 LEX(PM_TOKEN_LESS_LESS);
10917 }
10918
10919 if (lex_state_operator_p(parser)) {
10920 lex_state_set(parser, PM_LEX_STATE_ARG);
10921 } else {
10922 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10923 lex_state_set(parser, PM_LEX_STATE_BEG);
10924 }
10925
10926 if (match(parser, '=')) {
10927 if (match(parser, '>')) {
10928 LEX(PM_TOKEN_LESS_EQUAL_GREATER);
10929 }
10930
10931 LEX(PM_TOKEN_LESS_EQUAL);
10932 }
10933
10934 LEX(PM_TOKEN_LESS);
10935
10936 // > >> >>= >=
10937 case '>':
10938 if (match(parser, '>')) {
10939 if (lex_state_operator_p(parser)) {
10940 lex_state_set(parser, PM_LEX_STATE_ARG);
10941 } else {
10942 lex_state_set(parser, PM_LEX_STATE_BEG);
10943 }
10944 LEX(match(parser, '=') ? PM_TOKEN_GREATER_GREATER_EQUAL : PM_TOKEN_GREATER_GREATER);
10945 }
10946
10947 if (lex_state_operator_p(parser)) {
10948 lex_state_set(parser, PM_LEX_STATE_ARG);
10949 } else {
10950 lex_state_set(parser, PM_LEX_STATE_BEG);
10951 }
10952
10953 LEX(match(parser, '=') ? PM_TOKEN_GREATER_EQUAL : PM_TOKEN_GREATER);
10954
10955 // double-quoted string literal
10956 case '"': {
10957 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10958 lex_mode_push_string(parser, true, label_allowed, '\0', '"');
10959 LEX(PM_TOKEN_STRING_BEGIN);
10960 }
10961
10962 // xstring literal
10963 case '`': {
10964 if (lex_state_p(parser, PM_LEX_STATE_FNAME)) {
10965 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10966 LEX(PM_TOKEN_BACKTICK);
10967 }
10968
10969 if (lex_state_p(parser, PM_LEX_STATE_DOT)) {
10970 if (previous_command_start) {
10971 lex_state_set(parser, PM_LEX_STATE_CMDARG);
10972 } else {
10973 lex_state_set(parser, PM_LEX_STATE_ARG);
10974 }
10975
10976 LEX(PM_TOKEN_BACKTICK);
10977 }
10978
10979 lex_mode_push_string(parser, true, false, '\0', '`');
10980 LEX(PM_TOKEN_XSTRING_BEGIN);
10981 }
10982
10983 // single-quoted string literal
10984 case '\'': {
10985 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10986 lex_mode_push_string(parser, false, label_allowed, '\0', '\'');
10987 LEX(PM_TOKEN_STRING_BEGIN);
10988 }
10989
10990 // ? character literal
10991 case '?':
10992 LEX(lex_question_mark(parser));
10993
10994 // & && &&= &=
10995 case '&': {
10996 if (match(parser, '&')) {
10997 lex_state_set(parser, PM_LEX_STATE_BEG);
10998
10999 if (match(parser, '=')) {
11000 LEX(PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
11001 }
11002
11003 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
11004 }
11005
11006 if (match(parser, '=')) {
11007 lex_state_set(parser, PM_LEX_STATE_BEG);
11008 LEX(PM_TOKEN_AMPERSAND_EQUAL);
11009 }
11010
11011 if (match(parser, '.')) {
11012 lex_state_set(parser, PM_LEX_STATE_DOT);
11013 LEX(PM_TOKEN_AMPERSAND_DOT);
11014 }
11015
11016 pm_token_type_t type = PM_TOKEN_AMPERSAND;
11017 if (lex_state_spcarg_p(parser, space_seen)) {
11018 if ((peek(parser) != ':') || (peek_offset(parser, 1) == '\0')) {
11019 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
11020 } else {
11021 const uint8_t delim = peek_offset(parser, 1);
11022
11023 if ((delim != '\'') && (delim != '"') && !char_is_identifier(parser, parser->current.end + 1, parser->end - (parser->current.end + 1))) {
11024 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
11025 }
11026 }
11027
11028 type = PM_TOKEN_UAMPERSAND;
11029 } else if (lex_state_beg_p(parser)) {
11030 type = PM_TOKEN_UAMPERSAND;
11031 } else if (ambiguous_operator_p(parser, space_seen)) {
11032 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "&", "argument prefix");
11033 }
11034
11035 if (lex_state_operator_p(parser)) {
11036 lex_state_set(parser, PM_LEX_STATE_ARG);
11037 } else {
11038 lex_state_set(parser, PM_LEX_STATE_BEG);
11039 }
11040
11041 LEX(type);
11042 }
11043
11044 // | || ||= |=
11045 case '|':
11046 if (match(parser, '|')) {
11047 if (match(parser, '=')) {
11048 lex_state_set(parser, PM_LEX_STATE_BEG);
11049 LEX(PM_TOKEN_PIPE_PIPE_EQUAL);
11050 }
11051
11052 if (lex_state_p(parser, PM_LEX_STATE_BEG)) {
11053 parser->current.end--;
11054 LEX(PM_TOKEN_PIPE);
11055 }
11056
11057 lex_state_set(parser, PM_LEX_STATE_BEG);
11058 LEX(PM_TOKEN_PIPE_PIPE);
11059 }
11060
11061 if (match(parser, '=')) {
11062 lex_state_set(parser, PM_LEX_STATE_BEG);
11063 LEX(PM_TOKEN_PIPE_EQUAL);
11064 }
11065
11066 if (lex_state_operator_p(parser)) {
11067 lex_state_set(parser, PM_LEX_STATE_ARG);
11068 } else {
11069 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
11070 }
11071
11072 LEX(PM_TOKEN_PIPE);
11073
11074 // + += +@
11075 case '+': {
11076 if (lex_state_operator_p(parser)) {
11077 lex_state_set(parser, PM_LEX_STATE_ARG);
11078
11079 if (match(parser, '@')) {
11080 LEX(PM_TOKEN_UPLUS);
11081 }
11082
11083 LEX(PM_TOKEN_PLUS);
11084 }
11085
11086 if (match(parser, '=')) {
11087 lex_state_set(parser, PM_LEX_STATE_BEG);
11088 LEX(PM_TOKEN_PLUS_EQUAL);
11089 }
11090
11091 if (
11092 lex_state_beg_p(parser) ||
11093 (lex_state_spcarg_p(parser, space_seen) ? (pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS), true) : false)
11094 ) {
11095 lex_state_set(parser, PM_LEX_STATE_BEG);
11096
11097 if (pm_char_is_decimal_digit(peek(parser))) {
11098 parser->current.end++;
11099 pm_token_type_t type = lex_numeric(parser);
11100 lex_state_set(parser, PM_LEX_STATE_END);
11101 LEX(type);
11102 }
11103
11104 LEX(PM_TOKEN_UPLUS);
11105 }
11106
11107 if (ambiguous_operator_p(parser, space_seen)) {
11108 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "+", "unary operator");
11109 }
11110
11111 lex_state_set(parser, PM_LEX_STATE_BEG);
11112 LEX(PM_TOKEN_PLUS);
11113 }
11114
11115 // - -= -@
11116 case '-': {
11117 if (lex_state_operator_p(parser)) {
11118 lex_state_set(parser, PM_LEX_STATE_ARG);
11119
11120 if (match(parser, '@')) {
11121 LEX(PM_TOKEN_UMINUS);
11122 }
11123
11124 LEX(PM_TOKEN_MINUS);
11125 }
11126
11127 if (match(parser, '=')) {
11128 lex_state_set(parser, PM_LEX_STATE_BEG);
11129 LEX(PM_TOKEN_MINUS_EQUAL);
11130 }
11131
11132 if (match(parser, '>')) {
11133 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11134 LEX(PM_TOKEN_MINUS_GREATER);
11135 }
11136
11137 bool spcarg = lex_state_spcarg_p(parser, space_seen);
11138 bool is_beg = lex_state_beg_p(parser);
11139 if (!is_beg && spcarg) {
11140 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS);
11141 }
11142
11143 if (is_beg || spcarg) {
11144 lex_state_set(parser, PM_LEX_STATE_BEG);
11145 LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS);
11146 }
11147
11148 if (ambiguous_operator_p(parser, space_seen)) {
11149 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "-", "unary operator");
11150 }
11151
11152 lex_state_set(parser, PM_LEX_STATE_BEG);
11153 LEX(PM_TOKEN_MINUS);
11154 }
11155
11156 // . .. ...
11157 case '.': {
11158 bool beg_p = lex_state_beg_p(parser);
11159
11160 if (match(parser, '.')) {
11161 if (match(parser, '.')) {
11162 // If we're _not_ inside a range within default parameters
11163 if (!context_p(parser, PM_CONTEXT_DEFAULT_PARAMS) && context_p(parser, PM_CONTEXT_DEF_PARAMS)) {
11164 if (lex_state_p(parser, PM_LEX_STATE_END)) {
11165 lex_state_set(parser, PM_LEX_STATE_BEG);
11166 } else {
11167 lex_state_set(parser, PM_LEX_STATE_ENDARG);
11168 }
11169 LEX(PM_TOKEN_UDOT_DOT_DOT);
11170 }
11171
11172 if (parser->enclosure_nesting == 0 && parser_end_of_line_p(parser)) {
11173 pm_parser_warn_token(parser, &parser->current, PM_WARN_DOT_DOT_DOT_EOL);
11174 }
11175
11176 lex_state_set(parser, PM_LEX_STATE_BEG);
11177 LEX(beg_p ? PM_TOKEN_UDOT_DOT_DOT : PM_TOKEN_DOT_DOT_DOT);
11178 }
11179
11180 lex_state_set(parser, PM_LEX_STATE_BEG);
11181 LEX(beg_p ? PM_TOKEN_UDOT_DOT : PM_TOKEN_DOT_DOT);
11182 }
11183
11184 lex_state_set(parser, PM_LEX_STATE_DOT);
11185 LEX(PM_TOKEN_DOT);
11186 }
11187
11188 // integer
11189 case '0':
11190 case '1':
11191 case '2':
11192 case '3':
11193 case '4':
11194 case '5':
11195 case '6':
11196 case '7':
11197 case '8':
11198 case '9': {
11199 pm_token_type_t type = lex_numeric(parser);
11200 lex_state_set(parser, PM_LEX_STATE_END);
11201 LEX(type);
11202 }
11203
11204 // :: symbol
11205 case ':':
11206 if (match(parser, ':')) {
11207 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)) {
11208 lex_state_set(parser, PM_LEX_STATE_BEG);
11209 LEX(PM_TOKEN_UCOLON_COLON);
11210 }
11211
11212 lex_state_set(parser, PM_LEX_STATE_DOT);
11213 LEX(PM_TOKEN_COLON_COLON);
11214 }
11215
11216 if (lex_state_end_p(parser) || pm_char_is_whitespace(peek(parser)) || peek(parser) == '#') {
11217 lex_state_set(parser, PM_LEX_STATE_BEG);
11218 LEX(PM_TOKEN_COLON);
11219 }
11220
11221 if (peek(parser) == '"' || peek(parser) == '\'') {
11222 lex_mode_push_string(parser, peek(parser) == '"', false, '\0', *parser->current.end);
11223 parser->current.end++;
11224 } else {
11225 /*
11226 * A quoted symbol clears its encoding by pushing a lex
11227 * mode above. A bare symbol is lexed inline, so it
11228 * clears the encoding here.
11229 */
11230 parser->explicit_encoding = NULL;
11231 }
11232
11233 lex_state_set(parser, PM_LEX_STATE_FNAME);
11234 LEX(PM_TOKEN_SYMBOL_BEGIN);
11235
11236 // / /=
11237 case '/':
11238 if (lex_state_beg_p(parser)) {
11239 lex_mode_push_regexp(parser, '\0', '/');
11240 LEX(PM_TOKEN_REGEXP_BEGIN);
11241 }
11242
11243 if (match(parser, '=')) {
11244 lex_state_set(parser, PM_LEX_STATE_BEG);
11245 LEX(PM_TOKEN_SLASH_EQUAL);
11246 }
11247
11248 if (lex_state_spcarg_p(parser, space_seen)) {
11249 // https://bugs.ruby-lang.org/issues/21994
11250 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_4_0) {
11251 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_SLASH);
11252 }
11253 lex_mode_push_regexp(parser, '\0', '/');
11254 LEX(PM_TOKEN_REGEXP_BEGIN);
11255 }
11256
11257 if (ambiguous_operator_p(parser, space_seen)) {
11258 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "/", "regexp literal");
11259 }
11260
11261 if (lex_state_operator_p(parser)) {
11262 lex_state_set(parser, PM_LEX_STATE_ARG);
11263 } else {
11264 lex_state_set(parser, PM_LEX_STATE_BEG);
11265 }
11266
11267 LEX(PM_TOKEN_SLASH);
11268
11269 // ^ ^=
11270 case '^':
11271 if (lex_state_operator_p(parser)) {
11272 lex_state_set(parser, PM_LEX_STATE_ARG);
11273 } else {
11274 lex_state_set(parser, PM_LEX_STATE_BEG);
11275 }
11276 LEX(match(parser, '=') ? PM_TOKEN_CARET_EQUAL : PM_TOKEN_CARET);
11277
11278 // ~ ~@
11279 case '~':
11280 if (lex_state_operator_p(parser)) {
11281 (void) match(parser, '@');
11282 lex_state_set(parser, PM_LEX_STATE_ARG);
11283 } else {
11284 lex_state_set(parser, PM_LEX_STATE_BEG);
11285 }
11286
11287 LEX(PM_TOKEN_TILDE);
11288
11289 // % %= %i %I %q %Q %w %W
11290 case '%': {
11291 // If there is no subsequent character then we have an
11292 // invalid token. We're going to say it's the percent
11293 // operator because we don't want to move into the string
11294 // lex mode unnecessarily.
11295 if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) {
11296 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT_EOF);
11297 LEX(PM_TOKEN_PERCENT);
11298 }
11299
11300 if (!lex_state_beg_p(parser) && match(parser, '=')) {
11301 lex_state_set(parser, PM_LEX_STATE_BEG);
11302 LEX(PM_TOKEN_PERCENT_EQUAL);
11303 } else if (
11304 lex_state_beg_p(parser) ||
11305 (lex_state_p(parser, PM_LEX_STATE_FITEM) && (peek(parser) == 's')) ||
11306 lex_state_spcarg_p(parser, space_seen)
11307 ) {
11308 if (!parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end)) {
11309 if (*parser->current.end >= 0x80) {
11310 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11311 goto lex_next_token;
11312 }
11313
11314 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11315 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11316 LEX(PM_TOKEN_STRING_BEGIN);
11317 }
11318
11319 // Delimiters for %-literals cannot be alphanumeric. We
11320 // validate that here.
11321 uint8_t delimiter = peek_offset(parser, 1);
11322 if (delimiter >= 0x80 || parser->encoding->alnum_char(&delimiter, 1)) {
11323 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11324 goto lex_next_token;
11325 }
11326
11327 switch (peek(parser)) {
11328 case 'i': {
11329 parser->current.end++;
11330
11331 if (parser->current.end < parser->end) {
11332 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11333 } else {
11334 lex_mode_push_list_eof(parser);
11335 }
11336
11337 LEX(PM_TOKEN_PERCENT_LOWER_I);
11338 }
11339 case 'I': {
11340 parser->current.end++;
11341
11342 if (parser->current.end < parser->end) {
11343 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11344 } else {
11345 lex_mode_push_list_eof(parser);
11346 }
11347
11348 LEX(PM_TOKEN_PERCENT_UPPER_I);
11349 }
11350 case 'r': {
11351 parser->current.end++;
11352
11353 if (parser->current.end < parser->end) {
11354 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11355 lex_mode_push_regexp(parser, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11356 } else {
11357 lex_mode_push_regexp(parser, '\0', '\0');
11358 }
11359
11360 LEX(PM_TOKEN_REGEXP_BEGIN);
11361 }
11362 case 'q': {
11363 parser->current.end++;
11364
11365 if (parser->current.end < parser->end) {
11366 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11367 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11368 } else {
11369 lex_mode_push_string_eof(parser);
11370 }
11371
11372 LEX(PM_TOKEN_STRING_BEGIN);
11373 }
11374 case 'Q': {
11375 parser->current.end++;
11376
11377 if (parser->current.end < parser->end) {
11378 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11379 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11380 } else {
11381 lex_mode_push_string_eof(parser);
11382 }
11383
11384 LEX(PM_TOKEN_STRING_BEGIN);
11385 }
11386 case 's': {
11387 parser->current.end++;
11388
11389 if (parser->current.end < parser->end) {
11390 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11391 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11392 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
11393 } else {
11394 lex_mode_push_string_eof(parser);
11395 }
11396
11397 LEX(PM_TOKEN_SYMBOL_BEGIN);
11398 }
11399 case 'w': {
11400 parser->current.end++;
11401
11402 if (parser->current.end < parser->end) {
11403 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11404 } else {
11405 lex_mode_push_list_eof(parser);
11406 }
11407
11408 LEX(PM_TOKEN_PERCENT_LOWER_W);
11409 }
11410 case 'W': {
11411 parser->current.end++;
11412
11413 if (parser->current.end < parser->end) {
11414 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11415 } else {
11416 lex_mode_push_list_eof(parser);
11417 }
11418
11419 LEX(PM_TOKEN_PERCENT_UPPER_W);
11420 }
11421 case 'x': {
11422 parser->current.end++;
11423
11424 if (parser->current.end < parser->end) {
11425 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11426 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11427 } else {
11428 lex_mode_push_string_eof(parser);
11429 }
11430
11431 LEX(PM_TOKEN_PERCENT_LOWER_X);
11432 }
11433 default:
11434 // If we get to this point, then we have a % that is completely
11435 // unparsable. In this case we'll just drop it from the parser
11436 // and skip past it and hope that the next token is something
11437 // that we can parse.
11438 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11439 goto lex_next_token;
11440 }
11441 }
11442
11443 if (ambiguous_operator_p(parser, space_seen)) {
11444 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "%", "string literal");
11445 }
11446
11447 lex_state_set(parser, lex_state_operator_p(parser) ? PM_LEX_STATE_ARG : PM_LEX_STATE_BEG);
11448 LEX(PM_TOKEN_PERCENT);
11449 }
11450
11451 // global variable
11452 case '$': {
11453 pm_token_type_t type = lex_global_variable(parser);
11454
11455 // If we're lexing an embedded variable, then we need to pop back into
11456 // the parent lex context.
11457 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
11458 lex_mode_pop(parser);
11459 }
11460
11461 lex_state_set(parser, PM_LEX_STATE_END);
11462 LEX(type);
11463 }
11464
11465 // instance variable, class variable
11466 case '@':
11467 lex_state_set(parser, parser->lex_state & PM_LEX_STATE_FNAME ? PM_LEX_STATE_ENDFN : PM_LEX_STATE_END);
11468 LEX(lex_at_variable(parser));
11469
11470 default: {
11471 if (*parser->current.start != '_') {
11472 size_t width = char_is_identifier_start(parser, parser->current.start, parser->end - parser->current.start);
11473
11474 // If this isn't the beginning of an identifier, then
11475 // it's an invalid token as we've exhausted all of the
11476 // other options. We'll skip past it and return the next
11477 // token after adding an appropriate error message.
11478 if (!width) {
11479 if (*parser->current.start >= 0x80) {
11480 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *parser->current.start);
11481 } else if (*parser->current.start == '\\') {
11482 switch (peek_at(parser, parser->current.start + 1)) {
11483 case ' ':
11484 parser->current.end++;
11485 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped space");
11486 break;
11487 case '\f':
11488 parser->current.end++;
11489 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped form feed");
11490 break;
11491 case '\t':
11492 parser->current.end++;
11493 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped horizontal tab");
11494 break;
11495 case '\v':
11496 parser->current.end++;
11497 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped vertical tab");
11498 break;
11499 case '\r':
11500 if (peek_at(parser, parser->current.start + 2) != '\n') {
11501 parser->current.end++;
11502 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped carriage return");
11503 break;
11504 }
11506 default:
11507 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "backslash");
11508 break;
11509 }
11510 } else if (char_is_ascii_printable(*parser->current.start)) {
11511 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_PRINTABLE_CHARACTER, *parser->current.start);
11512 } else {
11513 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_CHARACTER, *parser->current.start);
11514 }
11515
11516 goto lex_next_token;
11517 }
11518
11519 parser->current.end = parser->current.start + width;
11520 }
11521
11522 pm_token_type_t type = lex_identifier(parser, previous_command_start);
11523
11524 // If we've hit a __END__ and it was at the start of the
11525 // line or the start of the file and it is followed by
11526 // either a \n or a \r\n, then this is the last token of the
11527 // file.
11528 if (
11529 ((parser->current.end - parser->current.start) == 7) &&
11530 current_token_starts_line(parser) &&
11531 (memcmp(parser->current.start, "__END__", 7) == 0) &&
11532 (parser->current.end == parser->end || match_eol(parser))
11533 ) {
11534 // Since we know we're about to add an __END__ comment,
11535 // we know we need to add all of the newlines to get the
11536 // correct column information for it.
11537 const uint8_t *cursor = parser->current.end;
11538 while ((cursor = next_newline(cursor, parser->end - cursor)) != NULL) {
11539 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(++cursor - parser->start));
11540 }
11541
11542 parser->current.end = parser->end;
11543 parser->current.type = PM_TOKEN___END__;
11544 parser_lex_callback(parser);
11545
11546 parser->data_loc.start = PM_TOKEN_START(parser, &parser->current);
11547 parser->data_loc.length = PM_TOKEN_LENGTH(&parser->current);
11548
11549 LEX(PM_TOKEN_EOF);
11550 }
11551
11552 pm_lex_state_t last_state = parser->lex_state;
11553
11554 if (type == PM_TOKEN_IDENTIFIER || type == PM_TOKEN_CONSTANT || type == PM_TOKEN_METHOD_NAME) {
11555 if (lex_state_p(parser, PM_LEX_STATE_BEG_ANY | PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_DOT)) {
11556 if (previous_command_start) {
11557 lex_state_set(parser, PM_LEX_STATE_CMDARG);
11558 } else {
11559 lex_state_set(parser, PM_LEX_STATE_ARG);
11560 }
11561 } else if (parser->lex_state == PM_LEX_STATE_FNAME) {
11562 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11563 } else {
11564 lex_state_set(parser, PM_LEX_STATE_END);
11565 }
11566 }
11567
11568 if (
11569 !(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) &&
11570 (type == PM_TOKEN_IDENTIFIER) &&
11571 ((pm_parser_local_depth(parser, &parser->current) != -1) ||
11572 pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)))
11573 ) {
11574 lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL);
11575 }
11576
11577 LEX(type);
11578 }
11579 }
11580 }
11581 case PM_LEX_LIST: {
11582 if (parser->next_start != NULL) {
11583 parser->current.end = parser->next_start;
11584 parser->next_start = NULL;
11585 }
11586
11587 // First we'll set the beginning of the token.
11588 parser->current.start = parser->current.end;
11589
11590 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11591
11592 // If there's any whitespace at the start of the list, then we're
11593 // going to trim it off the beginning and create a new token.
11594 size_t whitespace;
11595
11596 if (parser->heredoc_end) {
11597 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11598 if (peek_offset(parser, (ptrdiff_t)whitespace) == '\n') {
11599 whitespace += 1;
11600 }
11601 } else if (lex_mode->as.list.terminator == '\n') {
11602 // When the list delimiter is a newline (e.g. `%w` followed by a
11603 // newline), the newline is the terminator rather than a word
11604 // separator. We only trim inline whitespace here so that the
11605 // terminating newline is left for the terminator handling below.
11606 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11607 } else {
11608 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));
11609 }
11610
11611 if (whitespace > 0) {
11612 parser->current.end += whitespace;
11613 if (peek_offset(parser, -1) == '\n') {
11614 // mutates next_start
11615 parser_flush_heredoc_end(parser);
11616 }
11617
11618 lex_mode->as.list.started = true;
11619 lex_mode->as.list.separated = true;
11620 LEX(PM_TOKEN_WORDS_SEP);
11621 }
11622
11623 // We'll check if we're at the end of the file. If we are, then we
11624 // need to return the EOF token.
11625 if (parser->current.end >= parser->end) {
11626 LEX(PM_TOKEN_EOF);
11627 }
11628
11629 /* A word separator delimits the opener from the first word (or
11630 * from the terminator when the list is empty). The parser accepts
11631 * the words without it, so when the list does not start with
11632 * whitespace the implicit separator goes to the lex callback alone
11633 * and lexing continues on to the token the parser receives. */
11634 if (!lex_mode->as.list.started) {
11635 lex_mode->as.list.started = true;
11636 lex_mode->as.list.separated = true;
11637 parser->current.type = PM_TOKEN_WORDS_SEP_IMPLICIT;
11638 parser_lex_callback(parser);
11639 }
11640
11641 // Here we'll get a list of the places where strpbrk should break,
11642 // and then find the first one.
11643 const uint8_t *breakpoints = lex_mode->as.list.breakpoints;
11644 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11645
11646 // If we haven't found an escape yet, then this buffer will be
11647 // unallocated since we can refer directly to the source string.
11648 pm_token_buffer_t token_buffer = { 0 };
11649
11650 while (breakpoint != NULL) {
11651 // If we hit whitespace, then we must have received content by
11652 // now, so we can return an element of the list. A whitespace
11653 // character that is also the terminator (e.g. a newline
11654 // delimiter) is handled by the terminator check below, not here.
11655 if (pm_char_is_whitespace(*breakpoint) && *breakpoint != lex_mode->as.list.terminator) {
11656 parser->current.end = breakpoint;
11657 pm_token_buffer_flush(parser, &token_buffer);
11658 lex_mode->as.list.separated = false;
11659 LEX(PM_TOKEN_STRING_CONTENT);
11660 }
11661
11662 // If we hit the terminator, we need to check which token to
11663 // return.
11664 if (*breakpoint == lex_mode->as.list.terminator) {
11665 // If this terminator doesn't actually close the list, then
11666 // we need to continue on past it.
11667 if (lex_mode->as.list.nesting > 0) {
11668 parser->current.end = breakpoint + 1;
11669 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11670 lex_mode->as.list.nesting--;
11671 continue;
11672 }
11673
11674 // If we've hit the terminator and we've already skipped
11675 // past content, then we can return a list node.
11676 if (breakpoint > parser->current.start) {
11677 parser->current.end = breakpoint;
11678 pm_token_buffer_flush(parser, &token_buffer);
11679 lex_mode->as.list.separated = false;
11680 LEX(PM_TOKEN_STRING_CONTENT);
11681 }
11682
11683 /* A word separator delimits the last word from the
11684 * terminator. The parser accepts the terminator without
11685 * it, so when the list does not end with whitespace the
11686 * implicit separator goes to the lex callback alone and
11687 * lexing continues on to the terminator. */
11688 if (!lex_mode->as.list.separated) {
11689 lex_mode->as.list.separated = true;
11690 parser->current.end = breakpoint;
11691 parser->current.type = PM_TOKEN_WORDS_SEP_IMPLICIT;
11692 parser_lex_callback(parser);
11693 }
11694
11695 // Otherwise, switch back to the default state and return
11696 // the end of the list.
11697 parser->current.end = breakpoint + 1;
11698
11699 // If the terminator is a newline (i.e. the list delimiter
11700 // was a newline), then we need to record it so that line
11701 // numbers after the list remain accurate.
11702 if (*breakpoint == '\n') {
11703 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11704 }
11705
11706 lex_mode_pop(parser);
11707 lex_state_set(parser, PM_LEX_STATE_END);
11708 LEX(PM_TOKEN_STRING_END);
11709 }
11710
11711 // If we hit a null byte, skip directly past it.
11712 if (*breakpoint == '\0') {
11713 breakpoint = pm_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1), true);
11714 continue;
11715 }
11716
11717 // If we hit escapes, then we need to treat the next token
11718 // literally. In this case we'll skip past the next character
11719 // and find the next breakpoint.
11720 if (*breakpoint == '\\') {
11721 parser->current.end = breakpoint + 1;
11722
11723 // If we've hit the end of the file, then break out of the
11724 // loop by setting the breakpoint to NULL.
11725 if (parser->current.end == parser->end) {
11726 breakpoint = NULL;
11727 continue;
11728 }
11729
11730 pm_token_buffer_escape(parser, &token_buffer);
11731 uint8_t peeked = peek(parser);
11732
11733 switch (peeked) {
11734 case ' ':
11735 case '\f':
11736 case '\t':
11737 case '\v':
11738 case '\\':
11739 pm_token_buffer_push_byte(&token_buffer, peeked);
11740 parser->current.end++;
11741 break;
11742 case '\r':
11743 parser->current.end++;
11744 if (peek(parser) != '\n') {
11745 pm_token_buffer_push_byte(&token_buffer, '\r');
11746 break;
11747 }
11749 case '\n':
11750 pm_token_buffer_push_byte(&token_buffer, '\n');
11751
11752 if (parser->heredoc_end) {
11753 // ... if we are on the same line as a heredoc,
11754 // flush the heredoc and continue parsing after
11755 // heredoc_end.
11756 parser_flush_heredoc_end(parser);
11757 pm_token_buffer_copy(parser, &token_buffer);
11758 LEX(PM_TOKEN_STRING_CONTENT);
11759 } else {
11760 // ... else track the newline.
11761 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11762 }
11763
11764 parser->current.end++;
11765 break;
11766 default:
11767 if (peeked == lex_mode->as.list.incrementor || peeked == lex_mode->as.list.terminator) {
11768 pm_token_buffer_push_byte(&token_buffer, peeked);
11769 parser->current.end++;
11770 } else if (lex_mode->as.list.interpolation) {
11771 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
11772 } else {
11773 pm_token_buffer_push_byte(&token_buffer, '\\');
11774 pm_token_buffer_push_escaped(&token_buffer, parser);
11775 }
11776
11777 break;
11778 }
11779
11780 token_buffer.cursor = parser->current.end;
11781 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11782 continue;
11783 }
11784
11785 // If we hit a #, then we will attempt to lex interpolation.
11786 if (*breakpoint == '#') {
11787 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11788
11789 if (!type) {
11790 // If we haven't returned at this point then we had something
11791 // that looked like an interpolated class or instance variable
11792 // like "#@" but wasn't actually. In this case we'll just skip
11793 // to the next breakpoint.
11794 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11795 continue;
11796 }
11797
11798 if (type == PM_TOKEN_STRING_CONTENT) {
11799 pm_token_buffer_flush(parser, &token_buffer);
11800 }
11801
11802 lex_mode->as.list.separated = false;
11803 LEX(type);
11804 }
11805
11806 // If we've hit the incrementor, then we need to skip past it
11807 // and find the next breakpoint.
11808 assert(*breakpoint == lex_mode->as.list.incrementor);
11809 parser->current.end = breakpoint + 1;
11810 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11811 lex_mode->as.list.nesting++;
11812 continue;
11813 }
11814
11815 if (parser->current.end > parser->current.start) {
11816 pm_token_buffer_flush(parser, &token_buffer);
11817 LEX(PM_TOKEN_STRING_CONTENT);
11818 }
11819
11820 // If we were unable to find a breakpoint, then this token hits the
11821 // end of the file.
11822 parser->current.end = parser->end;
11823 pm_token_buffer_flush(parser, &token_buffer);
11824 LEX(PM_TOKEN_STRING_CONTENT);
11825 }
11826 case PM_LEX_REGEXP: {
11827 // First, we'll set to start of this token to be the current end.
11828 if (parser->next_start == NULL) {
11829 parser->current.start = parser->current.end;
11830 } else {
11831 parser->current.start = parser->next_start;
11832 parser->current.end = parser->next_start;
11833 parser->next_start = NULL;
11834 }
11835
11836 // We'll check if we're at the end of the file. If we are, then we
11837 // need to return the EOF token.
11838 if (parser->current.end >= parser->end) {
11839 LEX(PM_TOKEN_EOF);
11840 }
11841
11842 // Get a reference to the current mode.
11843 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11844
11845 // These are the places where we need to split up the content of the
11846 // regular expression. We'll use strpbrk to find the first of these
11847 // characters.
11848 const uint8_t *breakpoints = lex_mode->as.regexp.breakpoints;
11849 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11850 pm_regexp_token_buffer_t token_buffer = { 0 };
11851
11852 while (breakpoint != NULL) {
11853 uint8_t term = lex_mode->as.regexp.terminator;
11854 bool is_terminator = (*breakpoint == term);
11855
11856 // If the terminator is newline, we need to consider \r\n _also_ a newline
11857 // For example: `%\nfoo\r\n`
11858 // The string should be "foo", not "foo\r"
11859 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
11860 if (term == '\n') {
11861 is_terminator = true;
11862 }
11863
11864 // If the terminator is a CR, but we see a CRLF, we need to
11865 // treat the CRLF as a newline, meaning this is _not_ the
11866 // terminator
11867 if (term == '\r') {
11868 is_terminator = false;
11869 }
11870 }
11871
11872 // If we hit the terminator, we need to determine what kind of
11873 // token to return.
11874 if (is_terminator) {
11875 if (lex_mode->as.regexp.nesting > 0) {
11876 parser->current.end = breakpoint + 1;
11877 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11878 lex_mode->as.regexp.nesting--;
11879 continue;
11880 }
11881
11882 // Here we've hit the terminator. If we have already consumed
11883 // content then we need to return that content as string content
11884 // first.
11885 if (breakpoint > parser->current.start) {
11886 parser->current.end = breakpoint;
11887 pm_regexp_token_buffer_flush(parser, &token_buffer);
11888 LEX(PM_TOKEN_STRING_CONTENT);
11889 }
11890
11891 // Check here if we need to track the newline.
11892 size_t eol_length = match_eol_at(parser, breakpoint);
11893 if (eol_length) {
11894 parser->current.end = breakpoint + eol_length;
11895
11896 // Track the newline if we're not in a heredoc that
11897 // would have already have added the newline to the
11898 // list.
11899 if (parser->heredoc_end == NULL) {
11900 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11901 }
11902 } else {
11903 parser->current.end = breakpoint + 1;
11904 }
11905
11906 // Since we've hit the terminator of the regular expression,
11907 // we now need to parse the options.
11908 parser->current.end += pm_strspn_regexp_option(parser->current.end, parser->end - parser->current.end);
11909
11910 lex_mode_pop(parser);
11911 lex_state_set(parser, PM_LEX_STATE_END);
11912 LEX(PM_TOKEN_REGEXP_END);
11913 }
11914
11915 // If we've hit the incrementor, then we need to skip past it
11916 // and find the next breakpoint.
11917 if (*breakpoint && *breakpoint == lex_mode->as.regexp.incrementor) {
11918 parser->current.end = breakpoint + 1;
11919 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11920 lex_mode->as.regexp.nesting++;
11921 continue;
11922 }
11923
11924 switch (*breakpoint) {
11925 case '\0':
11926 // If we hit a null byte, skip directly past it.
11927 parser->current.end = breakpoint + 1;
11928 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11929 break;
11930 case '\r':
11931 if (peek_at(parser, breakpoint + 1) != '\n') {
11932 parser->current.end = breakpoint + 1;
11933 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11934 break;
11935 }
11936
11937 breakpoint++;
11938 parser->current.end = breakpoint;
11939 pm_regexp_token_buffer_escape(parser, &token_buffer);
11940 token_buffer.base.cursor = breakpoint;
11941
11943 case '\n':
11944 // If we've hit a newline, then we need to track that in
11945 // the list of newlines.
11946 if (parser->heredoc_end == NULL) {
11947 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
11948 parser->current.end = breakpoint + 1;
11949 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11950 break;
11951 }
11952
11953 parser->current.end = breakpoint + 1;
11954 parser_flush_heredoc_end(parser);
11955 pm_regexp_token_buffer_flush(parser, &token_buffer);
11956 LEX(PM_TOKEN_STRING_CONTENT);
11957 case '\\': {
11958 // If we hit escapes, then we need to treat the next
11959 // token literally. In this case we'll skip past the
11960 // next character and find the next breakpoint.
11961 parser->current.end = breakpoint + 1;
11962
11963 // If we've hit the end of the file, then break out of
11964 // the loop by setting the breakpoint to NULL.
11965 if (parser->current.end == parser->end) {
11966 breakpoint = NULL;
11967 break;
11968 }
11969
11970 pm_regexp_token_buffer_escape(parser, &token_buffer);
11971 uint8_t peeked = peek(parser);
11972
11973 switch (peeked) {
11974 case '\r':
11975 parser->current.end++;
11976 if (peek(parser) != '\n') {
11977 if (lex_mode->as.regexp.terminator != '\r') {
11978 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11979 }
11980 pm_regexp_token_buffer_push_byte(&token_buffer, '\r');
11981 pm_token_buffer_push_byte(&token_buffer.base, '\r');
11982 break;
11983 }
11985 case '\n':
11986 if (parser->heredoc_end) {
11987 // ... if we are on the same line as a heredoc,
11988 // flush the heredoc and continue parsing after
11989 // heredoc_end.
11990 parser_flush_heredoc_end(parser);
11991 pm_regexp_token_buffer_copy(parser, &token_buffer);
11992 LEX(PM_TOKEN_STRING_CONTENT);
11993 } else {
11994 // ... else track the newline.
11995 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11996 }
11997
11998 parser->current.end++;
11999 break;
12000 case 'c':
12001 case 'C':
12002 case 'M':
12003 case 'u':
12004 case 'x':
12005 escape_read(parser, &token_buffer.regexp_buffer, &token_buffer.base.buffer, PM_ESCAPE_FLAG_REGEXP);
12006 break;
12007 default:
12008 if (lex_mode->as.regexp.terminator == peeked) {
12009 // Some characters when they are used as the
12010 // terminator also receive an escape. They are
12011 // enumerated here.
12012 switch (peeked) {
12013 case '$': case ')': case '*': case '+':
12014 case '.': case '>': case '?': case ']':
12015 case '^': case '|': case '}':
12016 pm_token_buffer_push_byte(&token_buffer.base, '\\');
12017 break;
12018 default:
12019 break;
12020 }
12021
12022 pm_regexp_token_buffer_push_byte(&token_buffer, peeked);
12023 pm_token_buffer_push_byte(&token_buffer.base, peeked);
12024 parser->current.end++;
12025 break;
12026 }
12027
12028 if (peeked < 0x80) pm_token_buffer_push_byte(&token_buffer.base, '\\');
12029 pm_regexp_token_buffer_push_escaped(&token_buffer, parser);
12030 break;
12031 }
12032
12033 token_buffer.base.cursor = parser->current.end;
12034 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
12035 break;
12036 }
12037 case '#': {
12038 // If we hit a #, then we will attempt to lex
12039 // interpolation.
12040 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12041
12042 if (!type) {
12043 // If we haven't returned at this point then we had
12044 // something that looked like an interpolated class or
12045 // instance variable like "#@" but wasn't actually. In
12046 // this case we'll just skip to the next breakpoint.
12047 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
12048 break;
12049 }
12050
12051 if (type == PM_TOKEN_STRING_CONTENT) {
12052 pm_regexp_token_buffer_flush(parser, &token_buffer);
12053 }
12054
12055 LEX(type);
12056 }
12057 default:
12058 assert(false && "unreachable");
12059 break;
12060 }
12061 }
12062
12063 if (parser->current.end > parser->current.start) {
12064 pm_regexp_token_buffer_flush(parser, &token_buffer);
12065 LEX(PM_TOKEN_STRING_CONTENT);
12066 }
12067
12068 // If we were unable to find a breakpoint, then this token hits the
12069 // end of the file.
12070 parser->current.end = parser->end;
12071 pm_regexp_token_buffer_flush(parser, &token_buffer);
12072 LEX(PM_TOKEN_STRING_CONTENT);
12073 }
12074 case PM_LEX_STRING: {
12075 // First, we'll set to start of this token to be the current end.
12076 if (parser->next_start == NULL) {
12077 parser->current.start = parser->current.end;
12078 } else {
12079 parser->current.start = parser->next_start;
12080 parser->current.end = parser->next_start;
12081 parser->next_start = NULL;
12082 }
12083
12084 // We'll check if we're at the end of the file. If we are, then we need to
12085 // return the EOF token.
12086 if (parser->current.end >= parser->end) {
12087 LEX(PM_TOKEN_EOF);
12088 }
12089
12090 // These are the places where we need to split up the content of the
12091 // string. We'll use strpbrk to find the first of these characters.
12092 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12093 const uint8_t *breakpoints = lex_mode->as.string.breakpoints;
12094 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12095
12096 // If we haven't found an escape yet, then this buffer will be
12097 // unallocated since we can refer directly to the source string.
12098 pm_token_buffer_t token_buffer = { 0 };
12099
12100 while (breakpoint != NULL) {
12101 // If we hit the incrementor, then we'll increment then nesting and
12102 // continue lexing.
12103 if (lex_mode->as.string.incrementor != '\0' && *breakpoint == lex_mode->as.string.incrementor) {
12104 lex_mode->as.string.nesting++;
12105 parser->current.end = breakpoint + 1;
12106 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12107 continue;
12108 }
12109
12110 uint8_t term = lex_mode->as.string.terminator;
12111 bool is_terminator = (*breakpoint == term);
12112
12113 // If the terminator is newline, we need to consider \r\n _also_ a newline
12114 // For example: `%r\nfoo\r\n`
12115 // The string should be /foo/, not /foo\r/
12116 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
12117 if (term == '\n') {
12118 is_terminator = true;
12119 }
12120
12121 // If the terminator is a CR, but we see a CRLF, we need to
12122 // treat the CRLF as a newline, meaning this is _not_ the
12123 // terminator
12124 if (term == '\r') {
12125 is_terminator = false;
12126 }
12127 }
12128
12129 // Note that we have to check the terminator here first because we could
12130 // potentially be parsing a % string that has a # character as the
12131 // terminator.
12132 if (is_terminator) {
12133 // If this terminator doesn't actually close the string, then we need
12134 // to continue on past it.
12135 if (lex_mode->as.string.nesting > 0) {
12136 parser->current.end = breakpoint + 1;
12137 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12138 lex_mode->as.string.nesting--;
12139 continue;
12140 }
12141
12142 // Here we've hit the terminator. If we have already consumed content
12143 // then we need to return that content as string content first.
12144 if (breakpoint > parser->current.start) {
12145 parser->current.end = breakpoint;
12146 pm_token_buffer_flush(parser, &token_buffer);
12147 LEX(PM_TOKEN_STRING_CONTENT);
12148 }
12149
12150 // Otherwise we need to switch back to the parent lex mode and
12151 // return the end of the string.
12152 size_t eol_length = match_eol_at(parser, breakpoint);
12153 if (eol_length) {
12154 parser->current.end = breakpoint + eol_length;
12155
12156 // Track the newline if we're not in a heredoc that
12157 // would have already have added the newline to the
12158 // list.
12159 if (parser->heredoc_end == NULL) {
12160 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
12161 }
12162 } else {
12163 parser->current.end = breakpoint + 1;
12164 }
12165
12166 if (lex_mode->as.string.label_allowed && (peek(parser) == ':') && (peek_offset(parser, 1) != ':')) {
12167 parser->current.end++;
12168 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
12169 lex_mode_pop(parser);
12170 LEX(PM_TOKEN_LABEL_END);
12171 }
12172
12173 // When the delimiter itself is a newline, we won't
12174 // get a chance to flush heredocs in the usual places since
12175 // the newline is already consumed.
12176 if (term == '\n' && parser->heredoc_end) {
12177 parser_flush_heredoc_end(parser);
12178 }
12179
12180 lex_state_set(parser, PM_LEX_STATE_END);
12181 lex_mode_pop(parser);
12182 LEX(PM_TOKEN_STRING_END);
12183 }
12184
12185 switch (*breakpoint) {
12186 case '\0':
12187 // Skip directly past the null character.
12188 parser->current.end = breakpoint + 1;
12189 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12190 break;
12191 case '\r':
12192 if (peek_at(parser, breakpoint + 1) != '\n') {
12193 parser->current.end = breakpoint + 1;
12194 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12195 break;
12196 }
12197
12198 // If we hit a \r\n sequence, then we need to treat it
12199 // as a newline.
12200 breakpoint++;
12201 parser->current.end = breakpoint;
12202 pm_token_buffer_escape(parser, &token_buffer);
12203 token_buffer.cursor = breakpoint;
12204
12206 case '\n':
12207 // When we hit a newline, we need to flush any potential
12208 // heredocs. Note that this has to happen after we check
12209 // for the terminator in case the terminator is a
12210 // newline character.
12211 if (parser->heredoc_end == NULL) {
12212 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12213 parser->current.end = breakpoint + 1;
12214 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12215 break;
12216 }
12217
12218 parser->current.end = breakpoint + 1;
12219 parser_flush_heredoc_end(parser);
12220 pm_token_buffer_flush(parser, &token_buffer);
12221 LEX(PM_TOKEN_STRING_CONTENT);
12222 case '\\': {
12223 // Here we hit escapes.
12224 parser->current.end = breakpoint + 1;
12225
12226 // If we've hit the end of the file, then break out of
12227 // the loop by setting the breakpoint to NULL.
12228 if (parser->current.end == parser->end) {
12229 breakpoint = NULL;
12230 continue;
12231 }
12232
12233 pm_token_buffer_escape(parser, &token_buffer);
12234 uint8_t peeked = peek(parser);
12235
12236 switch (peeked) {
12237 case '\\':
12238 pm_token_buffer_push_byte(&token_buffer, '\\');
12239 parser->current.end++;
12240 break;
12241 case '\r':
12242 parser->current.end++;
12243 if (peek(parser) != '\n') {
12244 if (!lex_mode->as.string.interpolation) {
12245 pm_token_buffer_push_byte(&token_buffer, '\\');
12246 }
12247 pm_token_buffer_push_byte(&token_buffer, '\r');
12248 break;
12249 }
12251 case '\n':
12252 if (!lex_mode->as.string.interpolation) {
12253 pm_token_buffer_push_byte(&token_buffer, '\\');
12254 pm_token_buffer_push_byte(&token_buffer, '\n');
12255 }
12256
12257 if (parser->heredoc_end) {
12258 // ... if we are on the same line as a heredoc,
12259 // flush the heredoc and continue parsing after
12260 // heredoc_end.
12261 parser_flush_heredoc_end(parser);
12262 pm_token_buffer_copy(parser, &token_buffer);
12263 LEX(PM_TOKEN_STRING_CONTENT);
12264 } else {
12265 // ... else track the newline.
12266 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
12267 }
12268
12269 parser->current.end++;
12270 break;
12271 default:
12272 if (lex_mode->as.string.incrementor != '\0' && peeked == lex_mode->as.string.incrementor) {
12273 pm_token_buffer_push_byte(&token_buffer, peeked);
12274 parser->current.end++;
12275 } else if (lex_mode->as.string.terminator != '\0' && peeked == lex_mode->as.string.terminator) {
12276 pm_token_buffer_push_byte(&token_buffer, peeked);
12277 parser->current.end++;
12278 } else if (lex_mode->as.string.interpolation) {
12279 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12280 } else {
12281 pm_token_buffer_push_byte(&token_buffer, '\\');
12282 pm_token_buffer_push_escaped(&token_buffer, parser);
12283 }
12284
12285 break;
12286 }
12287
12288 token_buffer.cursor = parser->current.end;
12289 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12290 break;
12291 }
12292 case '#': {
12293 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12294
12295 if (!type) {
12296 // If we haven't returned at this point then we had something that
12297 // looked like an interpolated class or instance variable like "#@"
12298 // but wasn't actually. In this case we'll just skip to the next
12299 // breakpoint.
12300 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12301 break;
12302 }
12303
12304 if (type == PM_TOKEN_STRING_CONTENT) {
12305 pm_token_buffer_flush(parser, &token_buffer);
12306 }
12307
12308 LEX(type);
12309 }
12310 default:
12311 assert(false && "unreachable");
12312 }
12313 }
12314
12315 if (parser->current.end > parser->current.start) {
12316 pm_token_buffer_flush(parser, &token_buffer);
12317 LEX(PM_TOKEN_STRING_CONTENT);
12318 }
12319
12320 // If we've hit the end of the string, then this is an unterminated
12321 // string. In that case we'll return a string content token.
12322 parser->current.end = parser->end;
12323 pm_token_buffer_flush(parser, &token_buffer);
12324 LEX(PM_TOKEN_STRING_CONTENT);
12325 }
12326 case PM_LEX_HEREDOC: {
12327 // First, we'll set to start of this token.
12328 if (parser->next_start == NULL) {
12329 parser->current.start = parser->current.end;
12330 } else {
12331 parser->current.start = parser->next_start;
12332 parser->current.end = parser->next_start;
12333 parser->heredoc_end = NULL;
12334 parser->next_start = NULL;
12335 }
12336
12337 // Now let's grab the information about the identifier off of the
12338 // current lex mode.
12339 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12340 pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base;
12341
12342 bool line_continuation = lex_mode->as.heredoc.line_continuation;
12343 lex_mode->as.heredoc.line_continuation = false;
12344
12345 // We'll check if we're at the end of the file. If we are, then we
12346 // will add an error (because we weren't able to find the
12347 // terminator) but still continue parsing so that content after the
12348 // declaration of the heredoc can be parsed.
12349 if (parser->current.end >= parser->end) {
12350 pm_parser_err_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length);
12351 parser->next_start = lex_mode->as.heredoc.next_start;
12352 parser->heredoc_end = parser->current.end;
12353 lex_state_set(parser, PM_LEX_STATE_END);
12354 lex_mode_pop(parser);
12355 LEX(PM_TOKEN_HEREDOC_END);
12356 }
12357
12358 const uint8_t *ident_start = heredoc_lex_mode->ident_start;
12359 size_t ident_length = heredoc_lex_mode->ident_length;
12360
12361 // If we are immediately following a newline and we have hit the
12362 // terminator, then we need to return the ending of the heredoc.
12363 if (current_token_starts_line(parser)) {
12364 const uint8_t *start = parser->current.start;
12365
12366 if (!line_continuation && (start + ident_length <= parser->end)) {
12367 const uint8_t *newline = next_newline(start, parser->end - start);
12368 const uint8_t *ident_end = newline;
12369 const uint8_t *terminator_end = newline;
12370
12371 if (newline == NULL) {
12372 terminator_end = parser->end;
12373 ident_end = parser->end;
12374 } else {
12375 terminator_end++;
12376 if (newline[-1] == '\r') {
12377 ident_end--; // Remove \r
12378 }
12379 }
12380
12381 /*
12382 * The line on which we are looking for the terminator runs
12383 * from `start` to `ident_end`. If that line is shorter than
12384 * the identifier, the terminator cannot fit and we skip the
12385 * match. This also keeps `terminator_start` from pointing
12386 * before `start`.
12387 */
12388 if ((size_t) (ident_end - start) >= ident_length) {
12389 const uint8_t *terminator_start = ident_end - ident_length;
12390 const uint8_t *cursor = start;
12391
12392 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12393 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12394 cursor++;
12395 }
12396 }
12397
12398 if (
12399 (cursor == terminator_start) &&
12400 (memcmp(terminator_start, ident_start, ident_length) == 0)
12401 ) {
12402 if (newline != NULL) {
12403 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
12404 }
12405
12406 parser->current.end = terminator_end;
12407 if (*lex_mode->as.heredoc.next_start == '\\') {
12408 parser->next_start = NULL;
12409 } else {
12410 parser->next_start = lex_mode->as.heredoc.next_start;
12411 parser->heredoc_end = parser->current.end;
12412 }
12413
12414 lex_state_set(parser, PM_LEX_STATE_END);
12415 lex_mode_pop(parser);
12416 LEX(PM_TOKEN_HEREDOC_END);
12417 }
12418 }
12419 }
12420
12421 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent);
12422 if (
12423 heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE &&
12424 lex_mode->as.heredoc.common_whitespace != NULL &&
12425 (*lex_mode->as.heredoc.common_whitespace > whitespace) &&
12426 peek_at(parser, start) != '\n'
12427 ) {
12428 *lex_mode->as.heredoc.common_whitespace = whitespace;
12429 }
12430 }
12431
12432 // Otherwise we'll be parsing string content. These are the places
12433 // where we need to split up the content of the heredoc. We'll use
12434 // strpbrk to find the first of these characters.
12435 uint8_t breakpoints[PM_STRPBRK_CACHE_SIZE] = "\r\n\\#";
12436
12437 pm_heredoc_quote_t quote = heredoc_lex_mode->quote;
12438 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12439 breakpoints[3] = '\0';
12440 }
12441
12442 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12443 pm_token_buffer_t token_buffer = { 0 };
12444 bool was_line_continuation = false;
12445
12446 while (breakpoint != NULL) {
12447 switch (*breakpoint) {
12448 case '\0':
12449 // Skip directly past the null character.
12450 parser->current.end = breakpoint + 1;
12451 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12452 break;
12453 case '\r':
12454 parser->current.end = breakpoint + 1;
12455
12456 if (peek_at(parser, breakpoint + 1) != '\n') {
12457 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12458 break;
12459 }
12460
12461 // If we hit a \r\n sequence, then we want to replace it
12462 // with a single \n character in the final string.
12463 breakpoint++;
12464 pm_token_buffer_escape(parser, &token_buffer);
12465 token_buffer.cursor = breakpoint;
12466
12468 case '\n': {
12469 if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) {
12470 parser_flush_heredoc_end(parser);
12471 parser->current.end = breakpoint + 1;
12472 pm_token_buffer_flush(parser, &token_buffer);
12473 LEX(PM_TOKEN_STRING_CONTENT);
12474 }
12475
12476 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12477
12478 // If we have a - or ~ heredoc, then we can match after
12479 // some leading whitespace.
12480 const uint8_t *start = breakpoint + 1;
12481
12482 if (!was_line_continuation && (start + ident_length <= parser->end)) {
12483 // We want to match the terminator starting from the end of the line in case
12484 // there is whitespace in the ident such as <<-' DOC' or <<~' DOC'.
12485 const uint8_t *newline = next_newline(start, parser->end - start);
12486
12487 if (newline == NULL) {
12488 newline = parser->end;
12489 } else if (newline[-1] == '\r') {
12490 newline--; // Remove \r
12491 }
12492
12493 /*
12494 * The line on which we are looking for the
12495 * terminator runs from `start` to `newline`. If
12496 * that line is shorter than the identifier the
12497 * terminator cannot fit and we skip the match.
12498 * This also keeps `terminator_start` from pointing
12499 * before `start`.
12500 */
12501 if ((size_t) (newline - start) >= ident_length) {
12502 // Start of a possible terminator.
12503 const uint8_t *terminator_start = newline - ident_length;
12504
12505 // Cursor to check for the leading whitespace. We skip the
12506 // leading whitespace if we have a - or ~ heredoc.
12507 const uint8_t *cursor = start;
12508
12509 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12510 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12511 cursor++;
12512 }
12513 }
12514
12515 if (
12516 cursor == terminator_start &&
12517 (memcmp(terminator_start, ident_start, ident_length) == 0)
12518 ) {
12519 parser->current.end = breakpoint + 1;
12520 pm_token_buffer_flush(parser, &token_buffer);
12521 LEX(PM_TOKEN_STRING_CONTENT);
12522 }
12523 }
12524 }
12525
12526 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.base.indent);
12527
12528 // If we have hit a newline that is followed by a valid
12529 // terminator, then we need to return the content of the
12530 // heredoc here as string content. Then, the next time a
12531 // token is lexed, it will match again and return the
12532 // end of the heredoc.
12533 if (lex_mode->as.heredoc.base.indent == PM_HEREDOC_INDENT_TILDE) {
12534 if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
12535 *lex_mode->as.heredoc.common_whitespace = whitespace;
12536 }
12537
12538 parser->current.end = breakpoint + 1;
12539 pm_token_buffer_flush(parser, &token_buffer);
12540 LEX(PM_TOKEN_STRING_CONTENT);
12541 }
12542
12543 // Otherwise we hit a newline and it wasn't followed by
12544 // a terminator, so we can continue parsing.
12545 parser->current.end = breakpoint + 1;
12546 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12547 break;
12548 }
12549 case '\\': {
12550 // If we hit an escape, then we need to skip past
12551 // however many characters the escape takes up. However
12552 // it's important that if \n or \r\n are escaped, we
12553 // stop looping before the newline and not after the
12554 // newline so that we can still potentially find the
12555 // terminator of the heredoc.
12556 parser->current.end = breakpoint + 1;
12557
12558 // If we've hit the end of the file, then break out of
12559 // the loop by setting the breakpoint to NULL.
12560 if (parser->current.end == parser->end) {
12561 breakpoint = NULL;
12562 continue;
12563 }
12564
12565 pm_token_buffer_escape(parser, &token_buffer);
12566 uint8_t peeked = peek(parser);
12567
12568 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12569 switch (peeked) {
12570 case '\r':
12571 parser->current.end++;
12572 if (peek(parser) != '\n') {
12573 pm_token_buffer_push_byte(&token_buffer, '\\');
12574 pm_token_buffer_push_byte(&token_buffer, '\r');
12575 break;
12576 }
12578 case '\n':
12579 pm_token_buffer_push_byte(&token_buffer, '\\');
12580 pm_token_buffer_push_byte(&token_buffer, '\n');
12581 token_buffer.cursor = parser->current.end + 1;
12582 breakpoint = parser->current.end;
12583 continue;
12584 default:
12585 pm_token_buffer_push_byte(&token_buffer, '\\');
12586 pm_token_buffer_push_escaped(&token_buffer, parser);
12587 break;
12588 }
12589 } else {
12590 switch (peeked) {
12591 case '\r':
12592 parser->current.end++;
12593 if (peek(parser) != '\n') {
12594 pm_token_buffer_push_byte(&token_buffer, '\r');
12595 break;
12596 }
12598 case '\n':
12599 // If we are in a tilde here, we should
12600 // break out of the loop and return the
12601 // string content.
12602 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12603 const uint8_t *end = parser->current.end;
12604
12605 if (parser->heredoc_end == NULL) {
12606 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(end - parser->start + 1));
12607 }
12608
12609 // Here we want the buffer to only
12610 // include up to the backslash.
12611 parser->current.end = breakpoint;
12612 pm_token_buffer_flush(parser, &token_buffer);
12613
12614 // Now we can advance the end of the
12615 // token past the newline.
12616 parser->current.end = end + 1;
12617 lex_mode->as.heredoc.line_continuation = true;
12618 LEX(PM_TOKEN_STRING_CONTENT);
12619 }
12620
12621 was_line_continuation = true;
12622 token_buffer.cursor = parser->current.end + 1;
12623 breakpoint = parser->current.end;
12624 continue;
12625 default:
12626 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12627 break;
12628 }
12629 }
12630
12631 token_buffer.cursor = parser->current.end;
12632 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12633 break;
12634 }
12635 case '#': {
12636 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12637
12638 if (!type) {
12639 // If we haven't returned at this point then we had
12640 // something that looked like an interpolated class
12641 // or instance variable like "#@" but wasn't
12642 // actually. In this case we'll just skip to the
12643 // next breakpoint.
12644 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12645 break;
12646 }
12647
12648 if (type == PM_TOKEN_STRING_CONTENT) {
12649 pm_token_buffer_flush(parser, &token_buffer);
12650 }
12651
12652 LEX(type);
12653 }
12654 default:
12655 assert(false && "unreachable");
12656 }
12657
12658 was_line_continuation = false;
12659 }
12660
12661 if (parser->current.end > parser->current.start) {
12662 parser->current.end = parser->end;
12663 pm_token_buffer_flush(parser, &token_buffer);
12664 LEX(PM_TOKEN_STRING_CONTENT);
12665 }
12666
12667 // If we've hit the end of the string, then this is an unterminated
12668 // heredoc. In that case we'll return a string content token.
12669 parser->current.end = parser->end;
12670 pm_token_buffer_flush(parser, &token_buffer);
12671 LEX(PM_TOKEN_STRING_CONTENT);
12672 }
12673 }
12674
12675 assert(false && "unreachable");
12676}
12677
12678#undef LEX
12679
12680/******************************************************************************/
12681/* Parse functions */
12682/******************************************************************************/
12683
12692typedef enum {
12693 PM_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator
12694 PM_BINDING_POWER_STATEMENT = 2,
12695 PM_BINDING_POWER_MODIFIER_RESCUE = 4, // rescue
12696 PM_BINDING_POWER_MODIFIER = 6, // if unless until while
12697 PM_BINDING_POWER_COMPOSITION = 8, // and or
12698 PM_BINDING_POWER_NOT = 10, // not
12699 PM_BINDING_POWER_MATCH = 12, // => in
12700 PM_BINDING_POWER_DEFINED = 14, // defined?
12701 PM_BINDING_POWER_MULTI_ASSIGNMENT = 16, // =
12702 PM_BINDING_POWER_ASSIGNMENT = 18, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **=
12703 PM_BINDING_POWER_TERNARY = 20, // ?:
12704 PM_BINDING_POWER_RANGE = 22, // .. ...
12705 PM_BINDING_POWER_LOGICAL_OR = 24, // ||
12706 PM_BINDING_POWER_LOGICAL_AND = 26, // &&
12707 PM_BINDING_POWER_EQUALITY = 28, // <=> == === != =~ !~
12708 PM_BINDING_POWER_COMPARISON = 30, // > >= < <=
12709 PM_BINDING_POWER_BITWISE_OR = 32, // | ^
12710 PM_BINDING_POWER_BITWISE_AND = 34, // &
12711 PM_BINDING_POWER_SHIFT = 36, // << >>
12712 PM_BINDING_POWER_TERM = 38, // + -
12713 PM_BINDING_POWER_FACTOR = 40, // * / %
12714 PM_BINDING_POWER_UMINUS = 42, // -@
12715 PM_BINDING_POWER_EXPONENT = 44, // **
12716 PM_BINDING_POWER_UNARY = 46, // ! ~ +@
12717 PM_BINDING_POWER_INDEX = 48, // [] []=
12718 PM_BINDING_POWER_CALL = 50, // :: .
12719 PM_BINDING_POWER_MAX = 52
12720} pm_binding_power_t;
12721
12726typedef struct {
12728 pm_binding_power_t left;
12729
12731 pm_binding_power_t right;
12732
12735
12742
12743#define BINDING_POWER_ASSIGNMENT { PM_BINDING_POWER_UNARY, PM_BINDING_POWER_ASSIGNMENT, true, false }
12744#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, false }
12745#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true, false }
12746#define NON_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, true }
12747#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false }
12748
12749pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
12750 // rescue
12751 [PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = { PM_BINDING_POWER_MODIFIER_RESCUE, PM_BINDING_POWER_COMPOSITION, true, false },
12752
12753 // if unless until while
12754 [PM_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12755 [PM_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12756 [PM_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12757 [PM_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12758
12759 // and or
12760 [PM_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12761 [PM_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12762
12763 // => in
12764 [PM_TOKEN_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12765 [PM_TOKEN_KEYWORD_IN] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12766
12767 // &&= &= ^= = >>= <<= -= %= |= ||= += /= *= **=
12768 [PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12769 [PM_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12770 [PM_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT,
12771 [PM_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT,
12772 [PM_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT,
12773 [PM_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12774 [PM_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12775 [PM_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT,
12776 [PM_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12777 [PM_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12778 [PM_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12779 [PM_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT,
12780 [PM_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12781 [PM_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12782
12783 // ?:
12784 [PM_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_TERNARY),
12785
12786 // .. ...
12787 [PM_TOKEN_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12788 [PM_TOKEN_DOT_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12789 [PM_TOKEN_UDOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12790 [PM_TOKEN_UDOT_DOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12791
12792 // ||
12793 [PM_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_OR),
12794
12795 // &&
12796 [PM_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_AND),
12797
12798 // != !~ == === =~ <=>
12799 [PM_TOKEN_BANG_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12800 [PM_TOKEN_BANG_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12801 [PM_TOKEN_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12802 [PM_TOKEN_EQUAL_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12803 [PM_TOKEN_EQUAL_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12804 [PM_TOKEN_LESS_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12805
12806 // > >= < <=
12807 [PM_TOKEN_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12808 [PM_TOKEN_GREATER_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12809 [PM_TOKEN_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12810 [PM_TOKEN_LESS_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12811
12812 // ^ |
12813 [PM_TOKEN_CARET] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12814 [PM_TOKEN_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12815
12816 // &
12817 [PM_TOKEN_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_AND),
12818
12819 // >> <<
12820 [PM_TOKEN_GREATER_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12821 [PM_TOKEN_LESS_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12822
12823 // - +
12824 [PM_TOKEN_MINUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12825 [PM_TOKEN_PLUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12826
12827 // % / *
12828 [PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12829 [PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12830 [PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12831 [PM_TOKEN_USTAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_FACTOR),
12832
12833 // -@
12834 [PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS),
12835 [PM_TOKEN_UMINUS_NUM] = { PM_BINDING_POWER_UMINUS, PM_BINDING_POWER_MAX, false, false },
12836
12837 // **
12838 [PM_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_EXPONENT),
12839 [PM_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12840
12841 // ! ~ +@
12842 [PM_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12843 [PM_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12844 [PM_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12845
12846 // [
12847 [PM_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_INDEX),
12848
12849 // :: . &.
12850 [PM_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12851 [PM_TOKEN_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12852 [PM_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL)
12853};
12854
12855#undef BINDING_POWER_ASSIGNMENT
12856#undef LEFT_ASSOCIATIVE
12857#undef RIGHT_ASSOCIATIVE
12858#undef RIGHT_ASSOCIATIVE_UNARY
12859
12863static PRISM_INLINE bool
12864match1(const pm_parser_t *parser, pm_token_type_t type) {
12865 return parser->current.type == type;
12866}
12867
12871static PRISM_INLINE bool
12872match2(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12873 return match1(parser, type1) || match1(parser, type2);
12874}
12875
12879static PRISM_INLINE bool
12880match3(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) {
12881 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3);
12882}
12883
12887static PRISM_INLINE bool
12888match4(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) {
12889 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4);
12890}
12891
12895static PRISM_INLINE bool
12896match5(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) {
12897 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5);
12898}
12899
12903static PRISM_INLINE bool
12904match6(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) {
12905 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6);
12906}
12907
12911static PRISM_INLINE bool
12912match8(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) {
12913 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);
12914}
12915
12922static bool
12923accept1(pm_parser_t *parser, pm_token_type_t type) {
12924 if (match1(parser, type)) {
12925 parser_lex(parser);
12926 return true;
12927 }
12928 return false;
12929}
12930
12935static PRISM_INLINE bool
12936accept2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12937 if (match2(parser, type1, type2)) {
12938 parser_lex(parser);
12939 return true;
12940 }
12941 return false;
12942}
12943
12955static void
12956expect1(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id) {
12957 if (accept1(parser, type)) return;
12958
12959 const uint8_t *location = parser->previous.end;
12960 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12961
12962 parser->previous.start = location;
12963 parser->previous.type = 0;
12964}
12965
12970static void
12971expect2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_diagnostic_id_t diag_id) {
12972 if (accept2(parser, type1, type2)) return;
12973
12974 const uint8_t *location = parser->previous.end;
12975 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12976
12977 parser->previous.start = location;
12978 parser->previous.type = 0;
12979}
12980
12985static void
12986expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
12987 if (match1(parser, PM_TOKEN_HEREDOC_END)) {
12988 parser_lex(parser);
12989 } else {
12990 pm_parser_err_heredoc_term(parser, ident_start, ident_length);
12991 parser->previous.start = parser->previous.end;
12992 parser->previous.type = 0;
12993 }
12994}
12995
13002static void
13003expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id, const pm_token_t *opening) {
13004 if (accept1(parser, type)) return;
13005
13006 const uint8_t *start = opening->start;
13007 pm_parser_err(parser, U32(start - parser->start), U32(opening->end - start), diag_id);
13008
13009 parser->previous.start = parser->previous.end;
13010 parser->previous.type = 0;
13011}
13012
13014#define PM_PARSE_ACCEPTS_COMMAND_CALL ((uint8_t) 0x1)
13015#define PM_PARSE_ACCEPTS_LABEL ((uint8_t) 0x2)
13016#define PM_PARSE_ACCEPTS_DO_BLOCK ((uint8_t) 0x4)
13017#define PM_PARSE_IN_ENDLESS_DEF ((uint8_t) 0x8)
13018
13028#define PM_PARSE_ACCEPTS_STATEMENT ((uint8_t) 0x10)
13029
13041#define PM_PARSE_ACCEPTS_COMMAND_DEF ((uint8_t) 0x20)
13042
13043static pm_node_t *
13044parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
13045
13050static pm_node_t *
13051parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
13052 pm_node_t *node = parse_expression(parser, binding_power, flags, diag_id, depth);
13053 pm_assert_value_expression(parser, node);
13054 return node;
13055}
13056
13075static PRISM_INLINE bool
13076token_begins_expression_p(pm_token_type_t type) {
13077 switch (type) {
13078 case PM_TOKEN_EQUAL_GREATER:
13079 case PM_TOKEN_KEYWORD_IN:
13080 // We need to special case this because it is a binary operator that
13081 // should not be marked as beginning an expression.
13082 return false;
13083 case PM_TOKEN_BRACE_RIGHT:
13084 case PM_TOKEN_BRACKET_RIGHT:
13085 case PM_TOKEN_COLON:
13086 case PM_TOKEN_COMMA:
13087 case PM_TOKEN_EMBEXPR_END:
13088 case PM_TOKEN_EOF:
13089 case PM_TOKEN_LAMBDA_BEGIN:
13090 case PM_TOKEN_KEYWORD_DO:
13091 case PM_TOKEN_KEYWORD_DO_BLOCK:
13092 case PM_TOKEN_KEYWORD_DO_LAMBDA:
13093 case PM_TOKEN_KEYWORD_DO_LOOP:
13094 case PM_TOKEN_KEYWORD_END:
13095 case PM_TOKEN_KEYWORD_ELSE:
13096 case PM_TOKEN_KEYWORD_ELSIF:
13097 case PM_TOKEN_KEYWORD_ENSURE:
13098 case PM_TOKEN_KEYWORD_THEN:
13099 case PM_TOKEN_KEYWORD_RESCUE:
13100 case PM_TOKEN_KEYWORD_WHEN:
13101 case PM_TOKEN_NEWLINE:
13102 case PM_TOKEN_PARENTHESIS_RIGHT:
13103 case PM_TOKEN_SEMICOLON:
13104 // The reason we need this short-circuit is because we're using the
13105 // binding powers table to tell us if the subsequent token could
13106 // potentially be the start of an expression. If there _is_ a binding
13107 // power for one of these tokens, then we should remove it from this list
13108 // and let it be handled by the default case below.
13109 assert(pm_binding_powers[type].left == PM_BINDING_POWER_UNSET);
13110 return false;
13111 case PM_TOKEN_UAMPERSAND:
13112 // This is a special case because this unary operator cannot appear
13113 // as a general operator, it only appears in certain circumstances.
13114 return false;
13115 case PM_TOKEN_UCOLON_COLON:
13116 case PM_TOKEN_UMINUS:
13117 case PM_TOKEN_UMINUS_NUM:
13118 case PM_TOKEN_UPLUS:
13119 case PM_TOKEN_BANG:
13120 case PM_TOKEN_TILDE:
13121 case PM_TOKEN_UDOT_DOT:
13122 case PM_TOKEN_UDOT_DOT_DOT:
13123 // These unary tokens actually do have binding power associated with them
13124 // so that we can correctly place them into the precedence order. But we
13125 // want them to be marked as beginning an expression, so we need to
13126 // special case them here.
13127 return true;
13128 default:
13129 return pm_binding_powers[type].left == PM_BINDING_POWER_UNSET;
13130 }
13131}
13132
13148static PRISM_INLINE bool
13149token_begins_pattern_p(pm_token_type_t type) {
13150 return (
13151 token_begins_expression_p(type) ||
13152 type == PM_TOKEN_USTAR ||
13153 type == PM_TOKEN_USTAR_STAR ||
13154 type == PM_TOKEN_CARET
13155 );
13156}
13157
13165static PRISM_INLINE bool
13166token_begins_argument_p(pm_token_type_t type) {
13167 return (
13168 token_begins_expression_p(type) ||
13169 type == PM_TOKEN_UAMPERSAND ||
13170 type == PM_TOKEN_USTAR ||
13171 type == PM_TOKEN_USTAR_STAR
13172 );
13173}
13174
13179static pm_node_t *
13180parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
13181 if (accept1(parser, PM_TOKEN_USTAR)) {
13182 pm_token_t operator = parser->previous;
13183 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));
13184 return UP(pm_splat_node_create(parser, &operator, expression));
13185 }
13186
13187 return parse_value_expression(parser, binding_power, flags, diag_id, depth);
13188}
13189
13190static bool
13191pm_node_unreference_each(const pm_node_t *node, void *data) {
13192 switch (PM_NODE_TYPE(node)) {
13193 /* When we are about to destroy a set of nodes that could potentially
13194 * contain block exits for the current scope, we need to check if they
13195 * are contained in the list of block exits and remove them if they are.
13196 */
13197 case PM_BREAK_NODE:
13198 case PM_NEXT_NODE:
13199 case PM_REDO_NODE: {
13200 pm_parser_t *parser = (pm_parser_t *) data;
13201 size_t index = 0;
13202
13203 while (index < parser->current_block_exits->size) {
13204 pm_node_t *block_exit = parser->current_block_exits->nodes[index];
13205
13206 if (block_exit == node) {
13207 if (index + 1 < parser->current_block_exits->size) {
13208 memmove(
13209 &parser->current_block_exits->nodes[index],
13210 &parser->current_block_exits->nodes[index + 1],
13211 (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *)
13212 );
13213 }
13214 parser->current_block_exits->size--;
13215
13216 /* Note returning true here because these nodes could have
13217 * arguments that are themselves block exits. */
13218 return true;
13219 }
13220
13221 index++;
13222 }
13223
13224 return true;
13225 }
13226 /* When an implicit local variable is written to or targeted, it becomes
13227 * a regular, named local variable. This branch removes it from the list
13228 * of implicit parameters when that happens. */
13229 case PM_LOCAL_VARIABLE_READ_NODE:
13230 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13231 pm_parser_t *parser = (pm_parser_t *) data;
13232 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
13233
13234 for (size_t index = 0; index < implicit_parameters->size; index++) {
13235 if (implicit_parameters->nodes[index] == node) {
13236 /* If the node is not the last one in the list, we need to
13237 * shift the remaining nodes down to fill the gap. This is
13238 * extremely unlikely to happen. */
13239 if (index != implicit_parameters->size - 1) {
13240 memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *));
13241 }
13242
13243 implicit_parameters->size--;
13244 break;
13245 }
13246 }
13247
13248 return false;
13249 }
13250 default:
13251 return true;
13252 }
13253}
13254
13260static void
13261pm_node_unreference(pm_parser_t *parser, const pm_node_t *node) {
13262 pm_visit_node(node, pm_node_unreference_each, parser);
13263}
13264
13269static void
13270parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) {
13271 // The method name needs to change. If we previously had
13272 // foo, we now need foo=. In this case we'll allocate a new
13273 // owned string, copy the previous method name in, and
13274 // append an =.
13275 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *name_field);
13276 size_t length = constant->length;
13277 uint8_t *name = (uint8_t *) pm_arena_alloc(parser->arena, length + 1, 1);
13278
13279 memcpy(name, constant->start, length);
13280 name[length] = '=';
13281
13282 *name_field = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, name, length + 1);
13283}
13284
13291static pm_node_t *
13292parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) {
13293 switch (PM_NODE_TYPE(target)) {
13294 case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13295 case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13296 case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13297 case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13298 case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13299 case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13300 case PM_TRUE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13301 default: break;
13302 }
13303
13304 pm_constant_id_t name = pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target));
13305 pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0);
13306
13307 return UP(result);
13308}
13309
13318static pm_node_t *
13319parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_parent) {
13320 switch (PM_NODE_TYPE(target)) {
13321 case PM_ERROR_RECOVERY_NODE:
13322 return target;
13323 case PM_SOURCE_ENCODING_NODE:
13324 case PM_FALSE_NODE:
13325 case PM_SOURCE_FILE_NODE:
13326 case PM_SOURCE_LINE_NODE:
13327 case PM_NIL_NODE:
13328 case PM_SELF_NODE:
13329 case PM_TRUE_NODE: {
13330 // In these special cases, we have specific error messages and we
13331 // will replace them with local variable writes.
13332 return parse_unwriteable_target(parser, target);
13333 }
13334 case PM_CLASS_VARIABLE_READ_NODE:
13336 target->type = PM_CLASS_VARIABLE_TARGET_NODE;
13337 return target;
13338 case PM_CONSTANT_PATH_NODE:
13339 if (context_def_p(parser)) {
13340 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13341 }
13342
13344 target->type = PM_CONSTANT_PATH_TARGET_NODE;
13345
13346 return target;
13347 case PM_CONSTANT_READ_NODE:
13348 if (context_def_p(parser)) {
13349 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13350 }
13351
13352 assert(sizeof(pm_constant_target_node_t) == sizeof(pm_constant_read_node_t));
13353 target->type = PM_CONSTANT_TARGET_NODE;
13354
13355 return target;
13356 case PM_BACK_REFERENCE_READ_NODE:
13357 case PM_NUMBERED_REFERENCE_READ_NODE:
13358 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13359 return UP(pm_error_recovery_node_create_unexpected(parser, target));
13360 case PM_GLOBAL_VARIABLE_READ_NODE:
13362 target->type = PM_GLOBAL_VARIABLE_TARGET_NODE;
13363 return target;
13364 case PM_LOCAL_VARIABLE_READ_NODE: {
13365 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13366 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(target));
13367 pm_node_unreference(parser, target);
13368 }
13369
13370 const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target;
13371 uint32_t name = cast->name;
13372 uint32_t depth = cast->depth;
13373 pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name);
13374
13376 target->type = PM_LOCAL_VARIABLE_TARGET_NODE;
13377
13378 return target;
13379 }
13380 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13381 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13382 pm_node_t *node = UP(pm_local_variable_target_node_create(parser, &target->location, name, 0));
13383
13384 pm_node_unreference(parser, target);
13385
13386 return node;
13387 }
13388 case PM_INSTANCE_VARIABLE_READ_NODE:
13390 target->type = PM_INSTANCE_VARIABLE_TARGET_NODE;
13391 return target;
13392 case PM_MULTI_TARGET_NODE:
13393 if (splat_parent) {
13394 // Multi target is not accepted in all positions. If this is one
13395 // of them, then we need to add an error.
13396 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13397 }
13398
13399 return target;
13400 case PM_SPLAT_NODE: {
13401 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13402
13403 if (splat->expression != NULL) {
13404 splat->expression = parse_target(parser, splat->expression, multiple, true);
13405 }
13406
13407 return UP(splat);
13408 }
13409 case PM_CALL_NODE: {
13410 pm_call_node_t *call = (pm_call_node_t *) target;
13411
13412 // If we have no arguments to the call node and we need this to be a
13413 // target then this is either a method call or a local variable
13414 // write.
13415 if (
13416 (call->message_loc.length > 0) &&
13417 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13418 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13419 (call->opening_loc.length == 0) &&
13420 (call->arguments == NULL) &&
13421 (call->block == NULL)
13422 ) {
13423 if (call->receiver == NULL) {
13424 // When we get here, we have a local variable write, because it
13425 // was previously marked as a method call but now we have an =.
13426 // This looks like:
13427 //
13428 // foo = 1
13429 //
13430 // When it was parsed in the prefix position, foo was seen as a
13431 // method call with no receiver and no arguments. Now we have an
13432 // =, so we know it's a local variable write.
13433 pm_location_t message_loc = call->message_loc;
13434 pm_constant_id_t name = pm_parser_local_add_location(parser, &message_loc, 0);
13435
13436 return UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0));
13437 }
13438
13439 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)) {
13440 if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
13441 pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION);
13442 }
13443
13444 parse_write_name(parser, &call->name);
13445 return UP(pm_call_target_node_create(parser, call));
13446 }
13447 }
13448
13449 // If there is no call operator and the message is "[]" then this is
13450 // an aref expression, and we can transform it into an aset
13451 // expression.
13452 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13453 return UP(pm_index_target_node_create(parser, call));
13454 }
13455 }
13457 default:
13458 // In this case we have a node that we don't know how to convert
13459 // into a target. We need to treat it as an error. For now, we'll
13460 // mark it as an error and just skip right past it.
13461 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13462 return target;
13463 }
13464}
13465
13470static pm_node_t *
13471parse_target_validate(pm_parser_t *parser, pm_node_t *target, bool multiple) {
13472 pm_node_t *result = parse_target(parser, target, multiple, false);
13473
13474 // Ensure that we have one of an =, an 'in' in for indexes, and a ')' in
13475 // parens after the targets.
13476 if (
13477 !match1(parser, PM_TOKEN_EQUAL) &&
13478 !(context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) &&
13479 !(context_p(parser, PM_CONTEXT_PARENS) && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT))
13480 ) {
13481 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13482 }
13483
13484 return result;
13485}
13486
13491static pm_node_t *
13492parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) {
13493 pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser);
13494
13495 if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) {
13496 return UP(pm_shareable_constant_node_create(parser, write, shareable_constant));
13497 }
13498
13499 return write;
13500}
13501
13505static pm_node_t *
13506parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_node_t *value) {
13507 switch (PM_NODE_TYPE(target)) {
13508 case PM_ERROR_RECOVERY_NODE:
13509 return target;
13510 case PM_CLASS_VARIABLE_READ_NODE: {
13511 pm_class_variable_write_node_t *node = pm_class_variable_write_node_create(parser, (pm_class_variable_read_node_t *) target, operator, value);
13512 return UP(node);
13513 }
13514 case PM_CONSTANT_PATH_NODE: {
13515 pm_node_t *node = UP(pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value));
13516
13517 if (context_def_p(parser)) {
13518 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13519 }
13520
13521 return parse_shareable_constant_write(parser, node);
13522 }
13523 case PM_CONSTANT_READ_NODE: {
13524 pm_node_t *node = UP(pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value));
13525
13526 if (context_def_p(parser)) {
13527 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13528 }
13529
13530 return parse_shareable_constant_write(parser, node);
13531 }
13532 case PM_BACK_REFERENCE_READ_NODE:
13533 case PM_NUMBERED_REFERENCE_READ_NODE:
13534 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13536 case PM_GLOBAL_VARIABLE_READ_NODE: {
13537 pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value);
13538 return UP(node);
13539 }
13540 case PM_LOCAL_VARIABLE_READ_NODE: {
13542
13543 pm_location_t location = target->location;
13544 pm_constant_id_t name = local_read->name;
13545 uint32_t depth = local_read->depth;
13546 pm_scope_t *scope = pm_parser_scope_find(parser, depth);
13547
13548 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13549 pm_diagnostic_id_t diag_id = (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED;
13550 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), diag_id, parser->start + PM_NODE_START(target));
13551 pm_node_unreference(parser, target);
13552 }
13553
13554 pm_locals_unread(&scope->locals, name);
13555
13556 return UP(pm_local_variable_write_node_create(parser, name, depth, value, &location, operator));
13557 }
13558 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13559 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13560 pm_node_t *node = UP(pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator));
13561
13562 pm_node_unreference(parser, target);
13563
13564 return node;
13565 }
13566 case PM_INSTANCE_VARIABLE_READ_NODE: {
13567 pm_node_t *write_node = UP(pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value));
13568 return write_node;
13569 }
13570 case PM_MULTI_TARGET_NODE:
13571 return UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value));
13572 case PM_SPLAT_NODE: {
13573 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13574
13575 if (splat->expression != NULL) {
13576 splat->expression = parse_write(parser, splat->expression, operator, value);
13577 }
13578
13579 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
13580 pm_multi_target_node_targets_append(parser, multi_target, UP(splat));
13581
13582 return UP(pm_multi_write_node_create(parser, multi_target, operator, value));
13583 }
13584 case PM_CALL_NODE: {
13585 pm_call_node_t *call = (pm_call_node_t *) target;
13586
13587 // If we have no arguments to the call node and we need this to be a
13588 // target then this is either a method call or a local variable
13589 // write.
13590 if (
13591 (call->message_loc.length > 0) &&
13592 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13593 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13594 (call->opening_loc.length == 0) &&
13595 (call->arguments == NULL) &&
13596 (call->block == NULL)
13597 ) {
13598 if (call->receiver == NULL) {
13599 // When we get here, we have a local variable write, because it
13600 // was previously marked as a method call but now we have an =.
13601 // This looks like:
13602 //
13603 // foo = 1
13604 //
13605 // When it was parsed in the prefix position, foo was seen as a
13606 // method call with no receiver and no arguments. Now we have an
13607 // =, so we know it's a local variable write.
13608 pm_location_t message_loc = call->message_loc;
13609
13610 pm_refute_numbered_parameter(parser, message_loc.start, message_loc.length);
13611 pm_parser_local_add_location(parser, &message_loc, 0);
13612
13613 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));
13614 target = UP(pm_local_variable_write_node_create(parser, constant_id, 0, value, &message_loc, operator));
13615
13616 return target;
13617 }
13618
13619 if (char_is_identifier_start(parser, parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)) {
13620 // When we get here, we have a method call, because it was
13621 // previously marked as a method call but now we have an =. This
13622 // looks like:
13623 //
13624 // foo.bar = 1
13625 //
13626 // When it was parsed in the prefix position, foo.bar was seen as a
13627 // method call with no arguments. Now we have an =, so we know it's
13628 // a method call with an argument. In this case we will create the
13629 // arguments node, parse the argument, and add it to the list.
13630 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
13631 call->arguments = arguments;
13632
13633 pm_arguments_node_arguments_append(parser->arena, arguments, value);
13634 PM_NODE_LENGTH_SET_NODE(call, arguments);
13635 call->equal_loc = TOK2LOC(parser, operator);
13636
13637 parse_write_name(parser, &call->name);
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 UP(call);
13641 }
13642 }
13643
13644 // If there is no call operator and the message is "[]" then this is
13645 // an aref expression, and we can transform it into an aset
13646 // expression.
13647 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13648 if (call->arguments == NULL) {
13649 call->arguments = pm_arguments_node_create(parser);
13650 }
13651
13652 pm_arguments_node_arguments_append(parser->arena, call->arguments, value);
13653 PM_NODE_LENGTH_SET_NODE(target, value);
13654
13655 // Replace the name with "[]=".
13656 call->name = pm_parser_constant_id_constant(parser, "[]=", 3);
13657 call->equal_loc = TOK2LOC(parser, operator);
13658
13659 // Ensure that the arguments for []= don't contain keywords
13660 pm_index_arguments_check(parser, call->arguments, call->block);
13661 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13662
13663 return target;
13664 }
13665
13666 // If there are arguments on the call node, then it can't be a
13667 // method call ending with = or a local variable write, so it must
13668 // be a syntax error. In this case we'll fall through to our default
13669 // handling. We need to free the value that we parsed because there
13670 // is no way for us to attach it to the tree at this point.
13671 //
13672 // Since it is possible for the value to contain an implicit
13673 // parameter somewhere in its subtree, we need to walk it and remove
13674 // any implicit parameters from the list of implicit parameters for
13675 // the current scope.
13676 pm_node_unreference(parser, value);
13677 }
13679 default:
13680 // In this case we have a node that we don't know how to convert into a
13681 // target. We need to treat it as an error. For now, we'll mark it as an
13682 // error and just skip right past it.
13683 pm_parser_err_token(parser, operator, PM_ERR_WRITE_TARGET_UNEXPECTED);
13684 return target;
13685 }
13686}
13687
13694static pm_node_t *
13695parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) {
13696 switch (PM_NODE_TYPE(target)) {
13697 case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13698 case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13699 case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13700 case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13701 case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13702 case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13703 case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13704 default: break;
13705 }
13706
13707 pm_constant_id_t name = pm_parser_local_add_location(parser, &target->location, 1);
13708 pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals);
13709
13710 return UP(result);
13711}
13712
13723static pm_node_t *
13724parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13725 bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE);
13726
13727 pm_multi_target_node_t *result = pm_multi_target_node_create(parser);
13728 pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target, true, false));
13729
13730 while (accept1(parser, PM_TOKEN_COMMA)) {
13731 if (accept1(parser, PM_TOKEN_USTAR)) {
13732 // Here we have a splat operator. It can have a name or be
13733 // anonymous. It can be the final target or be in the middle if
13734 // there haven't been any others yet.
13735 if (has_rest) {
13736 pm_parser_err_previous(parser, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
13737 }
13738
13739 pm_token_t star_operator = parser->previous;
13740 pm_node_t *name = NULL;
13741
13742 if (token_begins_expression_p(parser->current.type)) {
13743 name = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
13744 name = parse_target(parser, name, true, true);
13745 }
13746
13747 pm_node_t *splat = UP(pm_splat_node_create(parser, &star_operator, name));
13748 pm_multi_target_node_targets_append(parser, result, splat);
13749 has_rest = true;
13750 } else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
13751 context_push(parser, PM_CONTEXT_MULTI_TARGET);
13752 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13753 target = parse_target(parser, target, true, false);
13754
13755 pm_multi_target_node_targets_append(parser, result, target);
13756 context_pop(parser);
13757 } else if (token_begins_expression_p(parser->current.type)) {
13758 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13759 target = parse_target(parser, target, true, false);
13760
13761 pm_multi_target_node_targets_append(parser, result, target);
13762 } else if (!match1(parser, PM_TOKEN_EOF)) {
13763 // If we get here, then we have a trailing , in a multi target node.
13764 // We'll add an implicit rest node to represent this.
13765 pm_node_t *rest = UP(pm_implicit_rest_node_create(parser, &parser->previous));
13766 pm_multi_target_node_targets_append(parser, result, rest);
13767 break;
13768 }
13769 }
13770
13771 return UP(result);
13772}
13773
13778static pm_node_t *
13779parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13780 pm_node_t *result = parse_targets(parser, first_target, binding_power, depth);
13781
13782 // If we're inside parentheses, then we allow a newline before the
13783 // closing parenthesis or equals sign. Outside of parentheses, a newline
13784 // is not allowed (e.g., `a, b\n= 1, 2` is not valid).
13785 if (context_p(parser, PM_CONTEXT_PARENS) || context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
13786 accept1(parser, PM_TOKEN_NEWLINE);
13787 }
13788
13789 // Ensure that we have either an = or a ) after the targets.
13790 if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
13791 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13792 }
13793
13794 return result;
13795}
13796
13800static pm_statements_node_t *
13801parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
13802 // First, skip past any optional terminators that might be at the beginning
13803 // of the statements.
13804 while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE));
13805
13806 // If we have a terminator, then we can just return NULL.
13807 if (context_terminator(context, &parser->current)) return NULL;
13808
13809 pm_statements_node_t *statements = pm_statements_node_create(parser);
13810
13811 // At this point we know we have at least one statement, and that it
13812 // immediately follows the current token.
13813 context_push(parser, context);
13814
13815 while (true) {
13816 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
13817 pm_statements_node_body_append(parser, statements, node, true);
13818
13819 // If we're recovering from a syntax error, then we need to stop parsing
13820 // the statements now.
13821 if (parser->recovering) {
13822 // If this is the level of context where the recovery has happened,
13823 // then we can mark the parser as done recovering.
13824 if (context_terminator(context, &parser->current)) parser->recovering = false;
13825 break;
13826 }
13827
13828 // If we have a terminator, then we will parse all consecutive
13829 // terminators and then continue parsing the statements list.
13830 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
13831 // If we have a terminator, then we will continue parsing the
13832 // statements list.
13833 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13834 if (context_terminator(context, &parser->current)) break;
13835
13836 // Now we can continue parsing the list of statements.
13837 continue;
13838 }
13839
13840 // At this point we have a list of statements that are not terminated by
13841 // a newline or semicolon. At this point we need to check if we're at
13842 // the end of the statements list. If we are, then we should break out
13843 // of the loop.
13844 if (context_terminator(context, &parser->current)) break;
13845
13846 // At this point, we have a syntax error, because the statement was not
13847 // terminated by a newline or semicolon, and we're not at the end of the
13848 // statements list. Ideally we should scan forward to determine if we
13849 // should insert a missing terminator or break out of parsing the
13850 // statements list at this point.
13851 //
13852 // We don't have that yet, so instead we'll do a more naive approach. If
13853 // we were unable to parse an expression, then we will skip past this
13854 // token and continue parsing the statements list. Otherwise we'll add
13855 // an error and continue parsing the statements list.
13856 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) {
13857 parser_lex(parser);
13858
13859 // If we are at the end of the file, then we need to stop parsing
13860 // the statements entirely at this point. Mark the parser as
13861 // recovering, as we know that EOF closes the top-level context, and
13862 // then break out of the loop.
13863 if (match1(parser, PM_TOKEN_EOF)) {
13864 parser->recovering = true;
13865 break;
13866 }
13867
13868 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13869 if (context_terminator(context, &parser->current)) break;
13870 } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) {
13871 // This is an inlined version of accept1 because the error that we
13872 // want to add has varargs. If this happens again, we should
13873 // probably extract a helper function.
13874 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
13875 parser->previous.start = parser->previous.end;
13876 parser->previous.type = 0;
13877 }
13878 }
13879
13880 context_pop(parser);
13881
13882 bool last_value = true;
13883 switch (context) {
13884 case PM_CONTEXT_BEGIN_ENSURE:
13885 case PM_CONTEXT_DEF_ENSURE:
13886 last_value = false;
13887 break;
13888 default:
13889 break;
13890 }
13891 pm_void_statements_check(parser, statements, last_value);
13892
13893 return statements;
13894}
13895
13899static void
13900pm_hash_key_duplicated_warn(pm_parser_t *parser, const pm_node_t *duplicated, const pm_node_t *node) {
13901 pm_buffer_t buffer = { 0 };
13902 pm_static_literal_inspect(&buffer, &parser->line_offsets, parser->start, parser->start_line, parser->encoding, duplicated);
13903
13904 pm_diagnostic_list_append_format(
13905 &parser->metadata_arena,
13906 &parser->warning_list,
13907 duplicated->location.start,
13908 duplicated->location.length,
13909 PM_WARN_DUPLICATED_HASH_KEY,
13910 (int) pm_buffer_length(&buffer),
13911 pm_buffer_value(&buffer),
13912 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line
13913 );
13914
13915 pm_buffer_cleanup(&buffer);
13916}
13917
13922static void
13923pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13924 const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, true);
13925
13926 if (duplicated != NULL) {
13927 pm_hash_key_duplicated_warn(parser, duplicated, node);
13928 }
13929}
13930
13938static void
13939pm_hash_key_static_literals_merge(pm_parser_t *parser, pm_static_literals_t *literals, const pm_hash_node_t *hash, uint32_t boundary) {
13940 const pm_node_list_t *elements = &hash->elements;
13941
13942 for (size_t index = 0; index < elements->size; index++) {
13943 pm_node_t *element = elements->nodes[index];
13944
13945 switch (PM_NODE_TYPE(element)) {
13946 case PM_ASSOC_NODE: {
13947 pm_node_t *key = ((pm_assoc_node_t *) element)->key;
13948 const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, key, true);
13949
13950 if (duplicated != NULL && PM_NODE_START(duplicated) < boundary) {
13951 pm_hash_key_duplicated_warn(parser, duplicated, key);
13952 }
13953
13954 break;
13955 }
13956 case PM_ASSOC_SPLAT_NODE: {
13957 const pm_node_t *value = ((pm_assoc_splat_node_t *) element)->value;
13958
13959 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
13960 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, boundary);
13961 }
13962
13963 break;
13964 }
13965 default:
13966 break;
13967 }
13968 }
13969}
13970
13975static void
13976pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13977 pm_node_t *previous;
13978
13979 if ((previous = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, false)) != NULL) {
13980 pm_diagnostic_list_append_format(
13981 &parser->metadata_arena,
13982 &parser->warning_list,
13983 PM_NODE_START(node),
13984 PM_NODE_LENGTH(node),
13985 PM_WARN_DUPLICATED_WHEN_CLAUSE,
13986 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line,
13987 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(previous), parser->start_line).line
13988 );
13989 }
13990}
13991
13995static bool
13996parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node, uint16_t depth) {
13997 assert(PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE));
13998 bool contains_keyword_splat = false;
13999
14000 while (true) {
14001 pm_node_t *element;
14002
14003 switch (parser->current.type) {
14004 case PM_TOKEN_USTAR_STAR: {
14005 parser_lex(parser);
14006 pm_token_t operator = parser->previous;
14007 pm_node_t *value = NULL;
14008
14009 if (token_begins_expression_p(parser->current.type)) {
14010 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));
14011
14012 /* If the splatted value is itself a hash literal, its keys
14013 * become part of this hash for the duplicate key warning. */
14014 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
14015 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, PM_NODE_START(value));
14016 }
14017 } else {
14018 pm_parser_scope_forwarding_keywords_check(parser, &operator);
14019 }
14020
14021 element = UP(pm_assoc_splat_node_create(parser, value, &operator));
14022 contains_keyword_splat = true;
14023 break;
14024 }
14025 case PM_TOKEN_LABEL: {
14026 pm_token_t label = parser->current;
14027 parser_lex(parser);
14028
14029 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &label));
14030 pm_hash_key_static_literals_add(parser, literals, key);
14031
14032 pm_node_t *value = NULL;
14033
14034 if (token_begins_expression_p(parser->current.type)) {
14035 value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_EXPRESSION_AFTER_LABEL, (uint16_t) (depth + 1));
14036 } else {
14037 if (parser->encoding->isupper_char(label.start, (label.end - 1) - label.start)) {
14038 pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 };
14039 value = UP(pm_constant_read_node_create(parser, &constant));
14040 } else {
14041 int depth = -1;
14042 pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 };
14043
14044 if (identifier.end[-1] == '!' || identifier.end[-1] == '?') {
14045 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ);
14046 } else {
14047 depth = pm_parser_local_depth(parser, &identifier);
14048 }
14049
14050 if (depth == -1) {
14051 value = UP(pm_call_node_variable_call_create(parser, &identifier));
14052 } else {
14053 value = UP(pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth));
14054 }
14055 }
14056
14057 value->location.length++;
14058 value = UP(pm_implicit_node_create(parser, value));
14059 }
14060
14061 element = UP(pm_assoc_node_create(parser, key, NULL, value));
14062 break;
14063 }
14064 default: {
14065 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));
14066
14067 // Hash keys that are strings are automatically frozen. We will
14068 // mark that here.
14069 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
14070 pm_node_flag_set(key, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
14071 }
14072
14073 pm_hash_key_static_literals_add(parser, literals, key);
14074
14075 pm_token_t operator = { 0 };
14076 if (!pm_symbol_node_label_p(parser, key)) {
14077 expect1(parser, PM_TOKEN_EQUAL_GREATER, PM_ERR_HASH_ROCKET);
14078 operator = parser->previous;
14079 }
14080
14081 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));
14082 element = UP(pm_assoc_node_create(parser, key, NTOK2PTR(operator), value));
14083 break;
14084 }
14085 }
14086
14087 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
14088 pm_hash_node_elements_append(parser->arena, (pm_hash_node_t *) node, element);
14089 } else {
14090 pm_keyword_hash_node_elements_append(parser->arena, (pm_keyword_hash_node_t *) node, element);
14091 }
14092
14093 // If there's no comma after the element, then we're done.
14094 if (!accept1(parser, PM_TOKEN_COMMA)) break;
14095
14096 // If the next element starts with a label or a **, then we know we have
14097 // another element in the hash, so we'll continue parsing.
14098 if (match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)) continue;
14099
14100 // Otherwise we need to check if the subsequent token begins an expression.
14101 // If it does, then we'll continue parsing.
14102 if (token_begins_expression_p(parser->current.type)) continue;
14103
14104 // Otherwise by default we will exit out of this loop.
14105 break;
14106 }
14107
14108 return contains_keyword_splat;
14109}
14110
14111static PRISM_INLINE bool
14112argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) {
14113 if (pm_symbol_node_label_p(parser, argument)) {
14114 return true;
14115 }
14116
14117 switch (PM_NODE_TYPE(argument)) {
14118 case PM_CALL_NODE: {
14119 pm_call_node_t *cast = (pm_call_node_t *) argument;
14120 if (cast->opening_loc.length == 0 && cast->arguments != NULL) {
14121 if (PM_NODE_FLAG_P(cast->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS | PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
14122 return false;
14123 }
14124 if (cast->block != NULL) {
14125 return false;
14126 }
14127 }
14128 break;
14129 }
14130 default: break;
14131 }
14132 return accept1(parser, PM_TOKEN_EQUAL_GREATER);
14133}
14134
14138static PRISM_INLINE void
14139parse_arguments_append(pm_parser_t *parser, pm_arguments_t *arguments, pm_node_t *argument) {
14140 if (arguments->arguments == NULL) {
14141 arguments->arguments = pm_arguments_node_create(parser);
14142 }
14143
14144 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, argument);
14145}
14146
14151static PRISM_INLINE bool
14152pm_call_node_command_p(const pm_call_node_t *node) {
14153 return (
14154 (node->opening_loc.length == 0) &&
14155 (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) &&
14156 (node->arguments != NULL || node->block != NULL)
14157 );
14158}
14159
14169static bool
14170pm_constant_path_command_call_p(const pm_parser_t *parser, const pm_call_node_t *call) {
14171 return (
14172 call->receiver != NULL &&
14173 call->opening_loc.length == 0 &&
14174 call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE) &&
14175 call->call_operator_loc.length > 0 &&
14176 parser->start[call->call_operator_loc.start] == ':' &&
14177 call->message_loc.length > 0 &&
14178 parser->encoding->isupper_char(parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)
14179 );
14180}
14181
14187static bool
14188pm_command_call_value_p(const pm_parser_t *parser, const pm_node_t *node) {
14189 switch (PM_NODE_TYPE(node)) {
14190 case PM_CALL_NODE: {
14191 const pm_call_node_t *call = (const pm_call_node_t *) node;
14192
14193 /* Command-style calls (e.g., foo bar, obj.foo bar). Attribute
14194 * writes (e.g., a.b = 1) are not commands. */
14195 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)) {
14196 return true;
14197 }
14198
14199 /* A constant-path command with a brace block, e.g. `Foo::Bar { }`. */
14200 if (pm_constant_path_command_call_p(parser, call)) {
14201 return true;
14202 }
14203
14204 /* A `!` or `not` prefix wrapping a command call (e.g., `!foo bar`,
14205 * `not foo bar`) is also a command-call value. */
14206 if (call->receiver != NULL && call->arguments == NULL && call->opening_loc.length == 0 && call->call_operator_loc.length == 0) {
14207 return pm_command_call_value_p(parser, call->receiver);
14208 }
14209
14210 return false;
14211 }
14212 case PM_SUPER_NODE: {
14213 /* A command-style super (no parens). A super carrying a do-block is
14214 * a block call (it permits chaining), so it is excluded here and
14215 * handled by pm_block_call_p instead. */
14216 const pm_super_node_t *cast = (const pm_super_node_t *) node;
14217 return cast->lparen_loc.length == 0 &&
14218 (cast->arguments != NULL || cast->block != NULL) &&
14219 !(cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE));
14220 }
14221 case PM_YIELD_NODE: {
14222 const pm_yield_node_t *cast = (const pm_yield_node_t *) node;
14223 return cast->lparen_loc.length == 0 && cast->arguments != NULL;
14224 }
14225 case PM_RESCUE_MODIFIER_NODE:
14226 return pm_command_call_value_p(parser, ((const pm_rescue_modifier_node_t *) node)->expression);
14227 case PM_DEF_NODE: {
14228 const pm_def_node_t *cast = (const pm_def_node_t *) node;
14229 if (cast->equal_loc.length > 0 && cast->body != NULL) {
14230 const pm_node_t *body = cast->body;
14231 if (PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE)) {
14232 body = ((const pm_statements_node_t *) body)->body.nodes[((const pm_statements_node_t *) body)->body.size - 1];
14233 }
14234 return pm_command_call_value_p(parser, body);
14235 }
14236 return false;
14237 }
14238 default:
14239 return false;
14240 }
14241}
14242
14252static bool
14253pm_command_before_comma_p(const pm_parser_t *parser, const pm_node_t *node) {
14254 switch (PM_NODE_TYPE(node)) {
14255 case PM_CALL_NODE:
14256 return pm_constant_path_command_call_p(parser, (const pm_call_node_t *) node);
14257 case PM_DEF_NODE:
14258 return pm_command_call_value_p(parser, node);
14259 default:
14260 return false;
14261 }
14262}
14263
14270static bool
14271pm_block_call_p(const pm_node_t *node) {
14272 while (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
14273 const pm_call_node_t *call = (const pm_call_node_t *) node;
14274
14275 /* Root: a command (no parentheses) carrying command arguments and a
14276 * block (brace or do), e.g. `foo bar do end`, `foo bar { }`. The
14277 * no-parentheses requirement is what distinguishes a command root from
14278 * a method call root like `foo.bar(1) { }`, which is a primary value
14279 * and may be used as an argument.
14280 */
14281 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE)) {
14282 return true;
14283 }
14284
14285 /* Walk up the receiver chain of a `.`/`::`/`&.` call (e.g.,
14286 * `foo bar do end.baz(1)`). Parentheses on the chained call are allowed
14287 * here -- in parse.y a `block_call` can be extended by
14288 * `call_op2 operation2 opt_paren_args` and remains a block call.
14289 */
14290 if (call->call_operator_loc.length > 0 && call->receiver != NULL) {
14291 node = call->receiver;
14292 continue;
14293 }
14294
14295 return false;
14296 }
14297
14298 /* A `super` with command arguments and a do-block is also a block-call root
14299 * (parse.y: `command do_block`, where the command is `keyword_super
14300 * command_args`). `super do end` with no arguments is a forwarding super
14301 * (a primary value) and is handled elsewhere.
14302 */
14303 if (PM_NODE_TYPE_P(node, PM_SUPER_NODE)) {
14304 const pm_super_node_t *super = (const pm_super_node_t *) node;
14305 return super->lparen_loc.length == 0 && super->block != NULL && PM_NODE_TYPE_P(super->block, PM_BLOCK_NODE);
14306 }
14307
14308 return false;
14309}
14310
14314static void
14315parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator, uint8_t flags, uint16_t depth) {
14316 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
14317
14318 // First we need to check if the next token is one that could be the start
14319 // of an argument. If it's not, then we can just return.
14320 if (
14321 match2(parser, terminator, PM_TOKEN_EOF) ||
14322 (binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) ||
14323 context_terminator(parser->current_context->context, &parser->current)
14324 ) {
14325 return;
14326 }
14327
14328 bool parsed_first_argument = false;
14329 bool parsed_bare_hash = false;
14330 bool parsed_block_argument = false;
14331 bool parsed_forwarding_arguments = false;
14332
14333 while (!match1(parser, PM_TOKEN_EOF)) {
14334 if (parsed_forwarding_arguments) {
14335 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES);
14336 }
14337
14338 pm_node_t *argument = NULL;
14339
14340 switch (parser->current.type) {
14341 case PM_TOKEN_USTAR_STAR:
14342 case PM_TOKEN_LABEL: {
14343 if (parsed_bare_hash) {
14344 pm_parser_err_current(parser, PM_ERR_ARGUMENT_BARE_HASH);
14345 }
14346
14347 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
14348 argument = UP(hash);
14349
14350 pm_static_literals_t hash_keys = { 0 };
14351 bool contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1));
14352
14353 parse_arguments_append(parser, arguments, argument);
14354
14355 pm_node_flags_t node_flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14356 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14357 pm_node_flag_set(UP(arguments->arguments), node_flags);
14358
14359 pm_static_literals_free(&hash_keys);
14360 parsed_bare_hash = true;
14361
14362 break;
14363 }
14364 case PM_TOKEN_UAMPERSAND: {
14365 parser_lex(parser);
14366 pm_token_t operator = parser->previous;
14367 pm_node_t *expression = NULL;
14368
14369 if (token_begins_expression_p(parser->current.type)) {
14370 expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14371 } else {
14372 pm_parser_scope_forwarding_block_check(parser, &operator);
14373 }
14374
14375 argument = UP(pm_block_argument_node_create(parser, &operator, expression));
14376 if (parsed_block_argument) {
14377 parse_arguments_append(parser, arguments, argument);
14378 } else {
14379 arguments->block = argument;
14380 }
14381
14382 if (match1(parser, PM_TOKEN_COMMA)) {
14383 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK);
14384 }
14385
14386 parsed_block_argument = true;
14387 break;
14388 }
14389 case PM_TOKEN_USTAR: {
14390 parser_lex(parser);
14391 pm_token_t operator = parser->previous;
14392
14393 if (match4(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_SEMICOLON, PM_TOKEN_BRACKET_RIGHT)) {
14394 pm_parser_scope_forwarding_positionals_check(parser, &operator);
14395 argument = UP(pm_splat_node_create(parser, &operator, NULL));
14396 if (parsed_bare_hash) {
14397 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT);
14398 }
14399 } else {
14400 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));
14401
14402 if (parsed_bare_hash) {
14403 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);
14404 }
14405
14406 argument = UP(pm_splat_node_create(parser, &operator, expression));
14407 }
14408
14409 parse_arguments_append(parser, arguments, argument);
14410 break;
14411 }
14412 case PM_TOKEN_UDOT_DOT_DOT: {
14413 if (accepts_forwarding) {
14414 parser_lex(parser);
14415
14416 if (token_begins_expression_p(parser->current.type)) {
14417 // If the token begins an expression then this ... was
14418 // not actually argument forwarding but was instead a
14419 // range.
14420 pm_token_t operator = parser->previous;
14421 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));
14422
14423 // If we parse a range, we need to validate that we
14424 // didn't accidentally violate the nonassoc rules of the
14425 // ... operator.
14426 if (PM_NODE_TYPE_P(right, PM_RANGE_NODE)) {
14427 pm_range_node_t *range = (pm_range_node_t *) right;
14428 pm_parser_err(parser, range->operator_loc.start, range->operator_loc.length, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
14429 }
14430
14431 argument = UP(pm_range_node_create(parser, NULL, &operator, right));
14432 } else {
14433 pm_parser_scope_forwarding_all_check(parser, &parser->previous);
14434 if (parsed_first_argument && terminator == PM_TOKEN_EOF) {
14435 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND);
14436 }
14437
14438 argument = UP(pm_forwarding_arguments_node_create(parser, &parser->previous));
14439 parse_arguments_append(parser, arguments, argument);
14440 pm_node_flag_set(UP(arguments->arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
14441 arguments->has_forwarding = true;
14442 parsed_forwarding_arguments = true;
14443 break;
14444 }
14445 }
14446 }
14448 default: {
14449 if (argument == NULL) {
14450 /* The first argument may be a command call, and since Ruby
14451 * 4.0 also an endless method with a command body
14452 * (`private def f = foo 1`), either of which must then be
14453 * the sole argument. */
14454 uint8_t command_flags = 0;
14455 if (!parsed_first_argument) {
14456 command_flags = PM_PARSE_ACCEPTS_COMMAND_CALL;
14457 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
14458 }
14459
14460 argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | command_flags | PM_PARSE_ACCEPTS_LABEL), PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14461 }
14462
14463 bool contains_keywords = false;
14464 bool contains_keyword_splat = false;
14465
14466 if (argument_allowed_for_bare_hash(parser, argument)) {
14467 if (parsed_bare_hash) {
14468 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH);
14469 }
14470
14471 /* A hash key must be an argument (`arg`). A command call or
14472 * block call (e.g. `Foo::Bar { } => v`, `foo bar do end =>
14473 * v`) is not an argument, so reject it as a key. Plain
14474 * command calls never reach here as a key because they
14475 * absorb the `=>` into their own arguments first.
14476 */
14477 if (pm_command_call_value_p(parser, argument) || pm_block_call_p(argument)) {
14478 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->previous.type));
14479 }
14480
14481 pm_token_t operator = { 0 };
14482 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
14483 operator = parser->previous;
14484 }
14485
14486 pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser);
14487 contains_keywords = true;
14488
14489 // Create the set of static literals for this hash.
14490 pm_static_literals_t hash_keys = { 0 };
14491 pm_hash_key_static_literals_add(parser, &hash_keys, argument);
14492
14493 // Finish parsing the one we are part way through.
14494 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));
14495 argument = UP(pm_assoc_node_create(parser, argument, NTOK2PTR(operator), value));
14496
14497 pm_keyword_hash_node_elements_append(parser->arena, bare_hash, argument);
14498 argument = UP(bare_hash);
14499
14500 // Then parse more if we have a comma
14501 if (accept1(parser, PM_TOKEN_COMMA) && (
14502 token_begins_expression_p(parser->current.type) ||
14503 match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)
14504 )) {
14505 contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(bare_hash), (uint16_t) (depth + 1));
14506 }
14507
14508 pm_static_literals_free(&hash_keys);
14509 parsed_bare_hash = true;
14510 }
14511
14512 parse_arguments_append(parser, arguments, argument);
14513
14514 pm_node_flags_t node_flags = 0;
14515 if (contains_keywords) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14516 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14517 pm_node_flag_set(UP(arguments->arguments), node_flags);
14518
14519 break;
14520 }
14521 }
14522
14523 parsed_first_argument = true;
14524
14525 // If parsing the argument failed, we need to stop parsing arguments.
14526 if (PM_NODE_TYPE_P(argument, PM_ERROR_RECOVERY_NODE) || parser->recovering) break;
14527
14528 // If the terminator of these arguments is not EOF, then we have a
14529 // specific token we're looking for. In that case we can accept a
14530 // newline here because it is not functioning as a statement terminator.
14531 bool accepted_newline = false;
14532 if (terminator != PM_TOKEN_EOF) {
14533 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14534 }
14535
14536 if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) {
14537 // If we previously were on a comma and we just parsed a bare hash,
14538 // then we want to continue parsing arguments. This is because the
14539 // comma was grabbed up by the hash parser.
14540 } else if (accept1(parser, PM_TOKEN_COMMA)) {
14541 // If there was a comma, then we need to check if we also accepted a
14542 // newline. If we did, then this is a syntax error.
14543 if (accepted_newline) {
14544 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14545 }
14546
14547 // If this is a command call and an argument takes a block,
14548 // there can be no further arguments. For example,
14549 // `foo(bar 1 do end, 2)` should be rejected.
14550 if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) {
14551 pm_call_node_t *call = (pm_call_node_t *) argument;
14552 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL) {
14553 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14554 break;
14555 }
14556 }
14557
14558 /* A command can only be the sole argument, as in
14559 * `foo Foo::Bar { }, y` or `foo def f = bar 1 rescue z, y`. */
14560 if (pm_command_before_comma_p(parser, argument)) {
14561 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14562 }
14563 } else {
14564 // If there is no comma at the end of the argument list then we're
14565 // done parsing arguments and can break out of this loop.
14566 break;
14567 }
14568
14569 // If we hit the terminator, then that means we have a trailing comma so
14570 // we can accept that output as well.
14571 if (match1(parser, terminator)) {
14572 // A forwarding `...` argument must be the last argument and cannot
14573 // be followed by a trailing comma, e.g. `foo(...,)`. A comma
14574 // followed by another argument is already rejected at the top of
14575 // this loop, so the only case left to reject here is the trailing
14576 // one.
14577 if (parsed_forwarding_arguments) {
14578 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14579 }
14580
14581 break;
14582 }
14583 }
14584}
14585
14597parse_required_destructured_parameter(pm_parser_t *parser) {
14598 expect1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER);
14599
14600 pm_multi_target_node_t *node = pm_multi_target_node_create(parser);
14601 pm_multi_target_node_opening_set(parser, node, &parser->previous);
14602
14603 do {
14604 pm_node_t *param;
14605
14606 // If we get here then we have a trailing comma, which isn't allowed in
14607 // the grammar. In other places, multi targets _do_ allow trailing
14608 // commas, so here we'll assume this is a mistake of the user not
14609 // knowing it's not allowed here.
14610 if (node->lefts.size > 0 && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
14611 param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14612 pm_multi_target_node_targets_append(parser, node, param);
14613 pm_parser_err_current(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14614 break;
14615 }
14616
14617 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
14618 param = UP(parse_required_destructured_parameter(parser));
14619 } else if (accept1(parser, PM_TOKEN_USTAR)) {
14620 pm_token_t star = parser->previous;
14621 pm_node_t *value = NULL;
14622
14623 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14624 pm_token_t name = parser->previous;
14625 value = UP(pm_required_parameter_node_create(parser, &name));
14626 if (pm_parser_parameter_name_check(parser, &name)) {
14627 pm_node_flag_set_repeated_parameter(value);
14628 }
14629 pm_parser_local_add_token(parser, &name, 1);
14630 }
14631
14632 param = UP(pm_splat_node_create(parser, &star, value));
14633 } else {
14634 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER);
14635 pm_token_t name = parser->previous;
14636
14637 param = UP(pm_required_parameter_node_create(parser, &name));
14638 if (pm_parser_parameter_name_check(parser, &name)) {
14639 pm_node_flag_set_repeated_parameter(param);
14640 }
14641 pm_parser_local_add_token(parser, &name, 1);
14642 }
14643
14644 pm_multi_target_node_targets_append(parser, node, param);
14645 } while (accept1(parser, PM_TOKEN_COMMA));
14646
14647 accept1(parser, PM_TOKEN_NEWLINE);
14648 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN_REQ_PARAMETER);
14649 pm_multi_target_node_closing_set(parser, node, &parser->previous);
14650
14651 return node;
14652}
14653
14658typedef enum {
14659 PM_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state
14660 PM_PARAMETERS_ORDER_NOTHING_AFTER = 1,
14661 PM_PARAMETERS_ORDER_KEYWORDS_REST,
14662 PM_PARAMETERS_ORDER_KEYWORDS,
14663 PM_PARAMETERS_ORDER_REST,
14664 PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14665 PM_PARAMETERS_ORDER_OPTIONAL,
14666 PM_PARAMETERS_ORDER_NAMED,
14667 PM_PARAMETERS_ORDER_NONE,
14668} pm_parameters_order_t;
14669
14673static pm_parameters_order_t parameters_ordering[PM_TOKEN_MAXIMUM] = {
14674 [0] = PM_PARAMETERS_NO_CHANGE,
14675 [PM_TOKEN_UAMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14676 [PM_TOKEN_AMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14677 [PM_TOKEN_UDOT_DOT_DOT] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14678 [PM_TOKEN_IDENTIFIER] = PM_PARAMETERS_ORDER_NAMED,
14679 [PM_TOKEN_PARENTHESIS_LEFT_GROUPING] = PM_PARAMETERS_ORDER_NAMED,
14680 [PM_TOKEN_EQUAL] = PM_PARAMETERS_ORDER_OPTIONAL,
14681 [PM_TOKEN_LABEL] = PM_PARAMETERS_ORDER_KEYWORDS,
14682 [PM_TOKEN_USTAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14683 [PM_TOKEN_STAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14684 [PM_TOKEN_USTAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST,
14685 [PM_TOKEN_STAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST
14686};
14687
14695static bool
14696update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_order_t *current) {
14697 pm_parameters_order_t state = parameters_ordering[token->type];
14698 if (state == PM_PARAMETERS_NO_CHANGE) return true;
14699
14700 // If we see another ordered argument after a optional argument
14701 // we only continue parsing ordered arguments until we stop seeing ordered arguments.
14702 if (*current == PM_PARAMETERS_ORDER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14703 *current = PM_PARAMETERS_ORDER_AFTER_OPTIONAL;
14704 return true;
14705 } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14706 return true;
14707 }
14708
14709 if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14710 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR);
14711 return false;
14712 } else if (token->type == PM_TOKEN_UDOT_DOT_DOT && (*current >= PM_PARAMETERS_ORDER_KEYWORDS_REST && *current <= PM_PARAMETERS_ORDER_AFTER_OPTIONAL)) {
14713 pm_parser_err_token(parser, token, *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL ? PM_ERR_PARAMETER_FORWARDING_AFTER_REST : PM_ERR_PARAMETER_ORDER);
14714 return false;
14715 } else if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) {
14716 // We know what transition we failed on, so we can provide a better error here.
14717 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_ORDER);
14718 return false;
14719 }
14720
14721 if (state < *current) *current = state;
14722 return true;
14723}
14724
14725static PRISM_INLINE void
14726parse_parameters_handle_trailing_comma(
14727 pm_parser_t *parser,
14728 pm_parameters_node_t *params,
14729 pm_parameters_order_t order,
14730 bool in_block,
14731 bool allows_trailing_comma
14732) {
14733 if (!allows_trailing_comma) {
14734 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14735 return;
14736 }
14737
14738 if (in_block) {
14739 if (order >= PM_PARAMETERS_ORDER_NAMED) {
14740 // foo do |bar,|; end
14741 pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14742
14743 if (params->rest == NULL) {
14744 pm_parameters_node_rest_set(params, param);
14745 } else {
14746 pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI);
14747 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14748 }
14749 } else {
14750 // foo do |*bar,|; end
14751 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14752 }
14753 } else {
14754 // https://bugs.ruby-lang.org/issues/19107
14755 // Allow `def foo(bar,); end`, `def foo(*bar,); end`, etc. but not `def foo(...,); end`
14756 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1 || order == PM_PARAMETERS_ORDER_NOTHING_AFTER) {
14757 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14758 }
14759 }
14760}
14761
14765static pm_parameters_node_t *
14766parse_parameters(
14767 pm_parser_t *parser,
14768 pm_binding_power_t binding_power,
14769 bool uses_parentheses,
14770 bool allows_trailing_comma,
14771 bool allows_forwarding_parameters,
14772 bool accepts_blocks_in_defaults,
14773 bool in_block,
14774 pm_diagnostic_id_t diag_id_forwarding,
14775 uint16_t depth
14776) {
14777 pm_do_loop_stack_push(parser, false);
14778
14779 pm_parameters_node_t *params = pm_parameters_node_create(parser);
14780 pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE;
14781
14782 while (true) {
14783 bool parsing = true;
14784
14785 switch (parser->current.type) {
14786 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
14787 update_parameter_state(parser, &parser->current, &order);
14788 pm_node_t *param = UP(parse_required_destructured_parameter(parser));
14789
14790 if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14791 pm_parameters_node_requireds_append(parser->arena, params, param);
14792 } else {
14793 pm_parameters_node_posts_append(parser->arena, params, param);
14794 }
14795 break;
14796 }
14797 case PM_TOKEN_UAMPERSAND:
14798 case PM_TOKEN_AMPERSAND: {
14799 update_parameter_state(parser, &parser->current, &order);
14800 parser_lex(parser);
14801
14802 pm_token_t operator = parser->previous;
14803 pm_node_t *param;
14804
14805 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1 && accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
14806 param = (pm_node_t *) pm_no_block_parameter_node_create(parser, &operator, &parser->previous);
14807 } else {
14808 pm_token_t name = {0};
14809
14810 bool repeated = false;
14811 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14812 name = parser->previous;
14813 repeated = pm_parser_parameter_name_check(parser, &name);
14814 pm_parser_local_add_token(parser, &name, 1);
14815 } else {
14816 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
14817 }
14818
14819 param = (pm_node_t *) pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator);
14820 if (repeated) {
14821 pm_node_flag_set_repeated_parameter(param);
14822 }
14823 }
14824
14825 if (params->block == NULL) {
14826 pm_parameters_node_block_set(params, param);
14827 } else {
14828 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_BLOCK_MULTI);
14829 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
14830 }
14831
14832 break;
14833 }
14834 case PM_TOKEN_UDOT_DOT_DOT: {
14835 if (!allows_forwarding_parameters) {
14836 pm_parser_err_current(parser, diag_id_forwarding);
14837 }
14838
14839 bool succeeded = update_parameter_state(parser, &parser->current, &order);
14840 parser_lex(parser);
14841
14842 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
14843 pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
14844
14845 if (params->keyword_rest != NULL) {
14846 // If we already have a keyword rest parameter, then we replace it with the
14847 // forwarding parameter and move the keyword rest parameter to the posts list.
14848 pm_node_t *keyword_rest = params->keyword_rest;
14849 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, keyword_rest)));
14850 if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD);
14851 params->keyword_rest = NULL;
14852 }
14853
14854 pm_parameters_node_keyword_rest_set(params, UP(param));
14855 break;
14856 }
14857 case PM_TOKEN_CLASS_VARIABLE:
14858 case PM_TOKEN_IDENTIFIER:
14859 case PM_TOKEN_CONSTANT:
14860 case PM_TOKEN_INSTANCE_VARIABLE:
14861 case PM_TOKEN_GLOBAL_VARIABLE:
14862 case PM_TOKEN_METHOD_NAME: {
14863 parser_lex(parser);
14864 switch (parser->previous.type) {
14865 case PM_TOKEN_CONSTANT:
14866 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14867 break;
14868 case PM_TOKEN_INSTANCE_VARIABLE:
14869 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
14870 break;
14871 case PM_TOKEN_GLOBAL_VARIABLE:
14872 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
14873 break;
14874 case PM_TOKEN_CLASS_VARIABLE:
14875 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
14876 break;
14877 case PM_TOKEN_METHOD_NAME:
14878 pm_parser_err_previous(parser, PM_ERR_PARAMETER_METHOD_NAME);
14879 break;
14880 default: break;
14881 }
14882
14883 if (parser->current.type == PM_TOKEN_EQUAL) {
14884 update_parameter_state(parser, &parser->current, &order);
14885 } else {
14886 update_parameter_state(parser, &parser->previous, &order);
14887 }
14888
14889 pm_token_t name = parser->previous;
14890 bool repeated = pm_parser_parameter_name_check(parser, &name);
14891 pm_parser_local_add_token(parser, &name, 1);
14892
14893 if (match1(parser, PM_TOKEN_EQUAL)) {
14894 pm_token_t operator = parser->current;
14895 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14896 parser_lex(parser);
14897
14898 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
14899 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14900
14901 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14902 pm_node_t *value = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1));
14903 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14904
14905 pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
14906
14907 if (repeated) {
14908 pm_node_flag_set_repeated_parameter(UP(param));
14909 }
14910 pm_parameters_node_optionals_append(parser->arena, params, param);
14911
14912 // If the value of the parameter increased the number of
14913 // reads of that parameter, then we need to warn that we
14914 // have a circular definition.
14915 if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14916 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &name, PM_ERR_PARAMETER_CIRCULAR);
14917 }
14918
14919 context_pop(parser);
14920
14921 // If parsing the value of the parameter resulted in error recovery,
14922 // then we can put a missing node in its place and stop parsing the
14923 // parameters entirely now.
14924 if (parser->recovering) {
14925 parsing = false;
14926 break;
14927 }
14928 } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14929 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14930 if (repeated) {
14931 pm_node_flag_set_repeated_parameter(UP(param));
14932 }
14933 pm_parameters_node_requireds_append(parser->arena, params, UP(param));
14934 } else {
14935 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14936 if (repeated) {
14937 pm_node_flag_set_repeated_parameter(UP(param));
14938 }
14939 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14940 }
14941
14942 break;
14943 }
14944 case PM_TOKEN_LABEL: {
14945 if (!uses_parentheses && !in_block) parser->in_keyword_arg = true;
14946 update_parameter_state(parser, &parser->current, &order);
14947
14948 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14949 parser_lex(parser);
14950
14951 pm_token_t name = parser->previous;
14952 pm_token_t local = name;
14953 local.end -= 1;
14954
14955 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)) {
14956 pm_parser_err(parser, PM_TOKEN_START(parser, &local), PM_TOKEN_LENGTH(&local), PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14957 } else if (local.end[-1] == '!' || local.end[-1] == '?') {
14958 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE);
14959 }
14960
14961 bool repeated = pm_parser_parameter_name_check(parser, &local);
14962 pm_parser_local_add_token(parser, &local, 1);
14963
14964 switch (parser->current.type) {
14965 case PM_TOKEN_COMMA:
14966 case PM_TOKEN_PARENTHESIS_RIGHT:
14967 case PM_TOKEN_PIPE: {
14968 context_pop(parser);
14969
14970 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14971 if (repeated) {
14972 pm_node_flag_set_repeated_parameter(param);
14973 }
14974
14975 pm_parameters_node_keywords_append(parser->arena, params, param);
14976 break;
14977 }
14978 case PM_TOKEN_SEMICOLON:
14979 case PM_TOKEN_NEWLINE: {
14980 context_pop(parser);
14981
14982 if (uses_parentheses) {
14983 parsing = false;
14984 break;
14985 }
14986
14987 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14988 if (repeated) {
14989 pm_node_flag_set_repeated_parameter(param);
14990 }
14991
14992 pm_parameters_node_keywords_append(parser->arena, params, param);
14993 break;
14994 }
14995 default: {
14996 pm_node_t *param;
14997
14998 if (token_begins_expression_p(parser->current.type)) {
14999 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
15000 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
15001
15002 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
15003 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));
15004 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
15005
15006 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
15007 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_PARAMETER_CIRCULAR);
15008 }
15009
15010 param = UP(pm_optional_keyword_parameter_node_create(parser, &name, value));
15011 }
15012 else {
15013 param = UP(pm_required_keyword_parameter_node_create(parser, &name));
15014 }
15015
15016 if (repeated) {
15017 pm_node_flag_set_repeated_parameter(param);
15018 }
15019
15020 context_pop(parser);
15021 pm_parameters_node_keywords_append(parser->arena, params, param);
15022
15023 // If parsing the value of the parameter resulted in error recovery,
15024 // then we can put a missing node in its place and stop parsing the
15025 // parameters entirely now.
15026 if (parser->recovering) {
15027 parsing = false;
15028 break;
15029 }
15030 }
15031 }
15032
15033 parser->in_keyword_arg = false;
15034 break;
15035 }
15036 case PM_TOKEN_USTAR:
15037 case PM_TOKEN_STAR: {
15038 update_parameter_state(parser, &parser->current, &order);
15039 parser_lex(parser);
15040
15041 pm_token_t operator = parser->previous;
15042 pm_token_t name = { 0 };
15043 bool repeated = false;
15044
15045 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
15046 name = parser->previous;
15047 repeated = pm_parser_parameter_name_check(parser, &name);
15048 pm_parser_local_add_token(parser, &name, 1);
15049 } else {
15050 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
15051 }
15052
15053 pm_node_t *param = UP(pm_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
15054 if (repeated) {
15055 pm_node_flag_set_repeated_parameter(param);
15056 }
15057
15058 if (params->rest == NULL) {
15059 pm_parameters_node_rest_set(params, param);
15060 } else {
15061 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_SPLAT_MULTI);
15062 pm_parameters_node_posts_append(parser->arena, params, param);
15063 }
15064
15065 break;
15066 }
15067 case PM_TOKEN_STAR_STAR:
15068 case PM_TOKEN_USTAR_STAR: {
15069 pm_parameters_order_t previous_order = order;
15070 update_parameter_state(parser, &parser->current, &order);
15071 parser_lex(parser);
15072
15073 pm_token_t operator = parser->previous;
15074 pm_node_t *param;
15075
15076 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
15077 if (previous_order <= PM_PARAMETERS_ORDER_KEYWORDS) {
15078 pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW);
15079 }
15080
15081 param = UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
15082 } else {
15083 pm_token_t name = { 0 };
15084
15085 bool repeated = false;
15086 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
15087 name = parser->previous;
15088 repeated = pm_parser_parameter_name_check(parser, &name);
15089 pm_parser_local_add_token(parser, &name, 1);
15090 } else {
15091 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS;
15092 }
15093
15094 param = UP(pm_keyword_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
15095 if (repeated) {
15096 pm_node_flag_set_repeated_parameter(param);
15097 }
15098 }
15099
15100 if (params->keyword_rest == NULL) {
15101 pm_parameters_node_keyword_rest_set(params, param);
15102 } else {
15103 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI);
15104 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
15105 }
15106
15107 break;
15108 }
15109 default:
15110 if (parser->previous.type == PM_TOKEN_COMMA) {
15111 parse_parameters_handle_trailing_comma(parser, params, order, in_block, allows_trailing_comma);
15112 }
15113
15114 parsing = false;
15115 break;
15116 }
15117
15118 // If we hit some kind of issue while parsing the parameter, this would
15119 // have been set to false. In that case, we need to break out of the
15120 // loop.
15121 if (!parsing) break;
15122
15123 bool accepted_newline = false;
15124 if (uses_parentheses) {
15125 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
15126 }
15127
15128 if (accept1(parser, PM_TOKEN_COMMA)) {
15129 // If there was a comma, but we also accepted a newline, then this
15130 // is a syntax error.
15131 if (accepted_newline) {
15132 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
15133 }
15134 } else {
15135 // If there was no comma, then we're done parsing parameters.
15136 break;
15137 }
15138 }
15139
15140 pm_do_loop_stack_pop(parser);
15141
15142 // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`.
15143 if (PM_NODE_START(params) == PM_NODE_END(params)) {
15144 return NULL;
15145 }
15146
15147 return params;
15148}
15149
15154static size_t
15155token_newline_index(const pm_parser_t *parser) {
15156 if (parser->heredoc_end == NULL) {
15157 // This is the common case. In this case we can look at the previously
15158 // recorded newline in the newline list and subtract from the current
15159 // offset.
15160 return parser->line_offsets.size - 1;
15161 } else {
15162 // This is unlikely. This is the case that we have already parsed the
15163 // start of a heredoc, so we cannot rely on looking at the previous
15164 // offset of the newline list, and instead must go through the whole
15165 // process of a binary search for the line number.
15166 return (size_t) pm_line_offset_list_line(&parser->line_offsets, PM_TOKEN_START(parser, &parser->current), 0);
15167 }
15168}
15169
15174static int64_t
15175token_column(const pm_parser_t *parser, size_t newline_index, const pm_token_t *token, bool break_on_non_space) {
15176 const uint8_t *cursor = parser->start + parser->line_offsets.offsets[newline_index];
15177 const uint8_t *end = token->start;
15178
15179 // Skip over the BOM if it is present.
15180 if (
15181 newline_index == 0 &&
15182 parser->start[0] == 0xef &&
15183 parser->start[1] == 0xbb &&
15184 parser->start[2] == 0xbf
15185 ) cursor += 3;
15186
15187 int64_t column = 0;
15188 for (; cursor < end; cursor++) {
15189 switch (*cursor) {
15190 case '\t':
15191 column = ((column / PM_TAB_WHITESPACE_SIZE) + 1) * PM_TAB_WHITESPACE_SIZE;
15192 break;
15193 case ' ':
15194 column++;
15195 break;
15196 default:
15197 column++;
15198 if (break_on_non_space) return -1;
15199 break;
15200 }
15201 }
15202
15203 return column;
15204}
15205
15210static void
15211parser_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) {
15212 // If these warnings are disabled (unlikely), then we can just return.
15213 if (!parser->warn_mismatched_indentation) return;
15214
15215 // If the tokens are on the same line, we do not warn.
15216 size_t closing_newline_index = token_newline_index(parser);
15217 if (opening_newline_index == closing_newline_index) return;
15218
15219 // If the opening token has anything other than spaces or tabs before it,
15220 // then we do not warn. This is unless we are matching up an `if`/`end` pair
15221 // and the `if` immediately follows an `else` keyword.
15222 int64_t opening_column = token_column(parser, opening_newline_index, opening_token, !if_after_else);
15223 if (!if_after_else && (opening_column == -1)) return;
15224
15225 // Get a reference to the closing token off the current parser. This assumes
15226 // that the caller has placed this in the correct position.
15227 pm_token_t *closing_token = &parser->current;
15228
15229 // If the tokens are at the same indentation, we do not warn.
15230 int64_t closing_column = token_column(parser, closing_newline_index, closing_token, true);
15231 if ((closing_column == -1) || (opening_column == closing_column)) return;
15232
15233 // If the closing column is greater than the opening column and we are
15234 // allowing indentation, then we do not warn.
15235 if (allow_indent && (closing_column > opening_column)) return;
15236
15237 // Otherwise, add a warning.
15238 PM_PARSER_WARN_FORMAT(
15239 parser,
15240 PM_TOKEN_START(parser, closing_token),
15241 PM_TOKEN_LENGTH(closing_token),
15242 PM_WARN_INDENTATION_MISMATCH,
15243 (int) (closing_token->end - closing_token->start),
15244 (const char *) closing_token->start,
15245 (int) (opening_token->end - opening_token->start),
15246 (const char *) opening_token->start,
15247 ((int32_t) opening_newline_index) + parser->start_line
15248 );
15249}
15250
15251typedef enum {
15252 PM_RESCUES_BEGIN = 1,
15253 PM_RESCUES_BLOCK,
15254 PM_RESCUES_CLASS,
15255 PM_RESCUES_DEF,
15256 PM_RESCUES_LAMBDA,
15257 PM_RESCUES_MODULE,
15258 PM_RESCUES_SCLASS
15259} pm_rescues_type_t;
15260
15265static PRISM_INLINE void
15266parse_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) {
15267 pm_rescue_node_t *current = NULL;
15268
15269 while (match1(parser, PM_TOKEN_KEYWORD_RESCUE)) {
15270 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15271 parser_lex(parser);
15272
15273 pm_rescue_node_t *rescue = pm_rescue_node_create(parser, &parser->previous);
15274
15275 switch (parser->current.type) {
15276 case PM_TOKEN_EQUAL_GREATER: {
15277 // Here we have an immediate => after the rescue keyword, in which case
15278 // we're going to have an empty list of exceptions to rescue (which
15279 // implies StandardError).
15280 parser_lex(parser);
15281 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15282
15283 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15284 reference = parse_target(parser, reference, false, false);
15285
15286 pm_rescue_node_reference_set(rescue, reference);
15287 break;
15288 }
15289 case PM_TOKEN_NEWLINE:
15290 case PM_TOKEN_SEMICOLON:
15291 case PM_TOKEN_KEYWORD_THEN:
15292 // Here we have a terminator for the rescue keyword, in which
15293 // case we're going to just continue on.
15294 break;
15295 default: {
15296 if (token_begins_expression_p(parser->current.type) || match1(parser, PM_TOKEN_USTAR)) {
15297 // Here we have something that could be an exception expression, so
15298 // we'll attempt to parse it here and any others delimited by commas.
15299
15300 do {
15301 pm_node_t *expression = parse_starred_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_RESCUE_EXPRESSION, (uint16_t) (depth + 1));
15302 pm_rescue_node_exceptions_append(parser->arena, rescue, expression);
15303
15304 // If we hit a newline, then this is the end of the rescue expression. We
15305 // can continue on to parse the statements.
15306 if (match3(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_THEN)) break;
15307
15308 // If we hit a `=>` then we're going to parse the exception variable. Once
15309 // we've done that, we'll break out of the loop and parse the statements.
15310 if (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
15311 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15312
15313 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15314 reference = parse_target(parser, reference, false, false);
15315
15316 pm_rescue_node_reference_set(rescue, reference);
15317 break;
15318 }
15319 } while (accept1(parser, PM_TOKEN_COMMA));
15320 }
15321 }
15322 }
15323
15324 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
15325 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
15326 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15327 }
15328 } else {
15329 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM);
15330 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15331 }
15332
15333 if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
15334 pm_accepts_block_stack_push(parser, true);
15335 pm_context_t context;
15336
15337 switch (type) {
15338 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break;
15339 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break;
15340 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break;
15341 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break;
15342 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break;
15343 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break;
15344 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break;
15345 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15346 }
15347
15348 pm_statements_node_t *statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15349 if (statements != NULL) pm_rescue_node_statements_set(rescue, statements);
15350
15351 pm_accepts_block_stack_pop(parser);
15352 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15353 }
15354
15355 if (current == NULL) {
15356 pm_begin_node_rescue_clause_set(parent_node, rescue);
15357 } else {
15358 pm_rescue_node_subsequent_set(current, rescue);
15359 }
15360
15361 current = rescue;
15362 }
15363
15364 // The end node locations on rescue nodes will not be set correctly
15365 // since we won't know the end until we've found all subsequent
15366 // clauses. This sets the end location on all rescues once we know it.
15367 if (current != NULL) {
15368 pm_rescue_node_t *clause = parent_node->rescue_clause;
15369
15370 while (clause != NULL) {
15371 PM_NODE_LENGTH_SET_NODE(clause, current);
15372 clause = clause->subsequent;
15373 }
15374 }
15375
15376 pm_token_t else_keyword;
15377 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
15378 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15379 opening_newline_index = token_newline_index(parser);
15380
15381 else_keyword = parser->current;
15382 opening = &else_keyword;
15383
15384 parser_lex(parser);
15385 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15386
15387 pm_statements_node_t *else_statements = NULL;
15388 if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) {
15389 pm_accepts_block_stack_push(parser, true);
15390 pm_context_t context;
15391
15392 switch (type) {
15393 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break;
15394 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break;
15395 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break;
15396 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break;
15397 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break;
15398 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break;
15399 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break;
15400 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_ELSE; break;
15401 }
15402
15403 else_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15404 pm_accepts_block_stack_pop(parser);
15405
15406 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15407 }
15408
15409 pm_else_node_t *else_clause = pm_else_node_create(parser, &else_keyword, else_statements, &parser->current);
15410 pm_begin_node_else_clause_set(parent_node, else_clause);
15411
15412 // If we don't have a `current` rescue node, then this is a dangling
15413 // else, and it's an error.
15414 if (current == NULL) pm_parser_err_node(parser, UP(else_clause), PM_ERR_BEGIN_LONELY_ELSE);
15415 }
15416
15417 if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) {
15418 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15419 pm_token_t ensure_keyword = parser->current;
15420
15421 parser_lex(parser);
15422 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15423
15424 pm_statements_node_t *ensure_statements = NULL;
15425 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15426 pm_accepts_block_stack_push(parser, true);
15427 pm_context_t context;
15428
15429 switch (type) {
15430 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break;
15431 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break;
15432 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break;
15433 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break;
15434 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break;
15435 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break;
15436 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break;
15437 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15438 }
15439
15440 ensure_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15441 pm_accepts_block_stack_pop(parser);
15442
15443 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15444 }
15445
15446 pm_ensure_node_t *ensure_clause = pm_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current);
15447 pm_begin_node_ensure_clause_set(parent_node, ensure_clause);
15448 }
15449
15450 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
15451 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15452 pm_begin_node_end_keyword_set(parser, parent_node, &parser->current);
15453 } else {
15454 pm_token_t end_keyword = (pm_token_t) { .type = PM_TOKEN_KEYWORD_END, .start = parser->previous.end, .end = parser->previous.end };
15455 pm_begin_node_end_keyword_set(parser, parent_node, &end_keyword);
15456 }
15457}
15458
15463static pm_begin_node_t *
15464parse_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) {
15465 pm_begin_node_t *node = pm_begin_node_create(parser, NULL, statements);
15466 parse_rescues(parser, opening_newline_index, opening, node, type, (uint16_t) (depth + 1));
15467
15468 node->base.location.start = U32(start - parser->start);
15469 PM_NODE_LENGTH_SET_TOKEN(parser, node, &parser->current);
15470
15471 return node;
15472}
15473
15478parse_block_parameters(
15479 pm_parser_t *parser,
15480 bool allows_trailing_comma,
15481 const pm_token_t *opening,
15482 bool is_lambda_literal,
15483 bool accepts_blocks_in_defaults,
15484 uint16_t depth
15485) {
15486 pm_parameters_node_t *parameters = NULL;
15487 if (!match1(parser, PM_TOKEN_SEMICOLON)) {
15488 if (!is_lambda_literal) {
15489 context_push(parser, PM_CONTEXT_BLOCK_PARAMETERS);
15490 }
15491 parameters = parse_parameters(
15492 parser,
15493 is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX,
15494 false,
15495 allows_trailing_comma,
15496 false,
15497 accepts_blocks_in_defaults,
15498 true,
15499 is_lambda_literal ? PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA : PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK,
15500 (uint16_t) (depth + 1)
15501 );
15502 if (!is_lambda_literal) {
15503 context_pop(parser);
15504 }
15505 }
15506
15507 pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening);
15508 if (opening != NULL) {
15509 accept1(parser, PM_TOKEN_NEWLINE);
15510
15511 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
15512 do {
15513 switch (parser->current.type) {
15514 case PM_TOKEN_CONSTANT:
15515 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
15516 parser_lex(parser);
15517 break;
15518 case PM_TOKEN_INSTANCE_VARIABLE:
15519 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
15520 parser_lex(parser);
15521 break;
15522 case PM_TOKEN_GLOBAL_VARIABLE:
15523 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
15524 parser_lex(parser);
15525 break;
15526 case PM_TOKEN_CLASS_VARIABLE:
15527 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
15528 parser_lex(parser);
15529 break;
15530 default:
15531 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
15532 break;
15533 }
15534
15535 bool repeated = pm_parser_parameter_name_check(parser, &parser->previous);
15536 pm_parser_local_add_token(parser, &parser->previous, 1);
15537
15538 pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous);
15539 if (repeated) pm_node_flag_set_repeated_parameter(UP(local));
15540
15541 pm_block_parameters_node_append_local(parser->arena, block_parameters, local);
15542 } while (accept1(parser, PM_TOKEN_COMMA));
15543 }
15544 }
15545
15546 return block_parameters;
15547}
15548
15553static bool
15554outer_scope_using_numbered_parameters_p(pm_parser_t *parser) {
15555 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15556 if (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) return true;
15557 }
15558
15559 return false;
15560}
15561
15567static const char * const pm_numbered_parameter_names[] = {
15568 "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9"
15569};
15570
15576static pm_node_t *
15577parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_token_t *opening, const pm_token_t *closing) {
15578 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
15579
15580 // If we have ordinary parameters, then we will return them as the set of
15581 // parameters.
15582 if (parameters != NULL) {
15583 // If we also have implicit parameters, then this is an error.
15584 if (implicit_parameters->size > 0) {
15585 pm_node_t *node = implicit_parameters->nodes[0];
15586
15587 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15588 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_ORDINARY);
15589 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15590 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_ORDINARY);
15591 } else {
15592 assert(false && "unreachable");
15593 }
15594 }
15595
15596 return parameters;
15597 }
15598
15599 // If we don't have any implicit parameters, then the set of parameters is
15600 // NULL.
15601 if (implicit_parameters->size == 0) {
15602 return NULL;
15603 }
15604
15605 // If we don't have ordinary parameters, then we now must validate our set
15606 // of implicit parameters. We can only have numbered parameters or it, but
15607 // they cannot be mixed.
15608 uint8_t numbered_parameter = 0;
15609 bool it_parameter = false;
15610
15611 for (size_t index = 0; index < implicit_parameters->size; index++) {
15612 pm_node_t *node = implicit_parameters->nodes[index];
15613
15614 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15615 if (it_parameter) {
15616 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_IT);
15617 } else if (outer_scope_using_numbered_parameters_p(parser)) {
15618 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK);
15619 } else if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_INNER) {
15620 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK);
15621 } else if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
15622 numbered_parameter = MAX(numbered_parameter, (uint8_t) (parser->start[node->location.start + 1] - '0'));
15623 } else {
15624 assert(false && "unreachable");
15625 }
15626 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15627 if (numbered_parameter > 0) {
15628 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_NUMBERED);
15629 } else {
15630 it_parameter = true;
15631 }
15632 }
15633 }
15634
15635 if (numbered_parameter > 0) {
15636 // Go through the parent scopes and mark them as being disallowed from
15637 // using numbered parameters because this inner scope is using them.
15638 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15639 scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_INNER;
15640 }
15641 return UP(pm_numbered_parameters_node_create(parser, opening, closing, numbered_parameter));
15642 }
15643
15644 if (it_parameter) {
15645 return UP(pm_it_parameters_node_create(parser, opening, closing));
15646 }
15647
15648 return NULL;
15649}
15650
15654static pm_block_node_t *
15655parse_block(pm_parser_t *parser, uint16_t depth) {
15656 pm_token_t opening = parser->previous;
15657 accept1(parser, PM_TOKEN_NEWLINE);
15658
15659 /* A brace block is delimited by `{`/`}`, whose block-accepting frame is
15660 * managed by the lexer. A `do`/`end` block is delimited by keywords, so we
15661 * push the frame here (covering the block parameters and body) and pop it
15662 * before consuming `end`, mirroring parse.y's `do_body` rule. */
15663 bool do_block = opening.type != PM_TOKEN_BRACE_LEFT && opening.type != PM_TOKEN_BRACE_LEFT_ARGUMENT;
15664 if (do_block) pm_accepts_block_stack_push(parser, true);
15665 pm_parser_scope_push(parser, false);
15666
15667 pm_block_parameters_node_t *block_parameters = NULL;
15668
15669 if (accept1(parser, PM_TOKEN_PIPE)) {
15670 pm_token_t block_parameters_opening = parser->previous;
15671 if (match1(parser, PM_TOKEN_PIPE)) {
15672 block_parameters = pm_block_parameters_node_create(parser, NULL, &block_parameters_opening);
15673 parser->command_start = true;
15674 parser_lex(parser);
15675 } else {
15676 block_parameters = parse_block_parameters(parser, true, &block_parameters_opening, false, true, (uint16_t) (depth + 1));
15677 accept1(parser, PM_TOKEN_NEWLINE);
15678 parser->command_start = true;
15679 expect1(parser, PM_TOKEN_PIPE, PM_ERR_BLOCK_PARAM_PIPE_TERM);
15680 }
15681
15682 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
15683 }
15684
15685 accept1(parser, PM_TOKEN_NEWLINE);
15686 pm_node_t *statements = NULL;
15687
15688 if (!do_block) {
15689 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
15690 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1)));
15691 }
15692
15693 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening);
15694 } else {
15695 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15696 if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) {
15697 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS, (uint16_t) (depth + 1)));
15698 }
15699
15700 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
15701 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
15702 statements = UP(parse_rescues_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1)));
15703 }
15704 }
15705
15706 /* Pop the `do`/`end` frame before consuming `end` so the token
15707 * following the block is lexed in the enclosing context. */
15708 pm_accepts_block_stack_pop(parser);
15709 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening);
15710 }
15711
15712 pm_constant_id_list_t locals;
15713 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
15714 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &opening, &parser->previous);
15715
15716 pm_parser_scope_pop(parser);
15717 return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous);
15718}
15719
15731static bool
15732parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_arguments, uint8_t flags, uint16_t depth) {
15733 /* Fast path: if the current token can't begin an expression and isn't
15734 * a parenthesis, block opener, or splat/block-pass operator, there are
15735 * no arguments to parse. */
15736 if (
15737 !token_begins_expression_p(parser->current.type) &&
15738 !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)
15739 ) {
15740 return false;
15741 }
15742
15743 bool found = false;
15744 bool parsed_command_args = false;
15745
15746 if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
15747 found |= true;
15748 arguments->opening_loc = TOK2LOC(parser, &parser->previous);
15749
15750 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15751 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15752 } else {
15753 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_PARENTHESIS_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
15754
15755 // `yield` parses its arguments through the restricted `call_args`
15756 // grammar, which (unlike the `opt_call_args` that method calls and
15757 // `super` use) permits neither a block argument nor a trailing
15758 // comma. `full_arguments` is false only for `yield`, so we use it
15759 // to reject the trailing comma in `yield(a,)` that the arguments
15760 // parser otherwise accepts before the closing parenthesis.
15761 if (!full_arguments && parser->previous.type == PM_TOKEN_COMMA) {
15762 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15763 }
15764
15765 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15766 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_str(parser->current.type));
15767 parser->previous.start = parser->previous.end;
15768 parser->previous.type = 0;
15769 }
15770
15771 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15772 }
15773 } else if ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
15774 found |= true;
15775 parsed_command_args = true;
15776
15777 /* The command-args frame does not accept blocks, so that a trailing
15778 * `do` binds to this command rather than to an argument. Mirroring
15779 * parse.y's `command_args` rule: when the first argument begins with an
15780 * opening delimiter, the lexer has already pushed that delimiter's
15781 * (block-accepting) frame. We must push the command-args frame beneath
15782 * it, so pop the delimiter frame, push the command-args frame, and then
15783 * restore the delimiter frame on top (the delimiter's closing token
15784 * will pop it back off during argument parsing). */
15785 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);
15786 if (lookahead_delimiter) pm_accepts_block_stack_pop(parser);
15787 pm_accepts_block_stack_push(parser, false);
15788 if (lookahead_delimiter) pm_accepts_block_stack_push(parser, true);
15789
15790 // If we get here, then the subsequent token cannot be used as an infix
15791 // operator. In this case we assume the subsequent token is part of an
15792 // argument to this method call.
15793 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
15794
15795 // If we have done with the arguments and still not consumed the comma,
15796 // then we have a trailing comma where we need to check whether it is
15797 // allowed or not.
15798 if (parser->previous.type == PM_TOKEN_COMMA && !match1(parser, PM_TOKEN_SEMICOLON)) {
15799 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15800 }
15801
15802 /* Symmetrically, if the command arguments are followed by a brace block
15803 * (`m args { }`), the lexer has already pushed that block's frame. Pop
15804 * it, pop the command-args frame beneath it, and restore the block
15805 * frame so the block's `}` still pops it. This mirrors the `tLBRACE_ARG`
15806 * lookahead handling in parse.y's `command_args` rule. */
15807 bool lookahead_brace = match2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT);
15808 if (lookahead_brace) pm_accepts_block_stack_pop(parser);
15809 pm_accepts_block_stack_pop(parser);
15810 if (lookahead_brace) pm_accepts_block_stack_push(parser, true);
15811 }
15812
15813 // If we're at the end of the arguments, we can now check if there is a block
15814 // node that starts with a {. If there is, then we can parse it and add it to
15815 // the arguments.
15816 if (full_arguments) {
15817 pm_block_node_t *block = NULL;
15818
15819 if (accept2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT)) {
15820 found |= true;
15821 block = parse_block(parser, (uint16_t) (depth + 1));
15822 pm_arguments_validate_block(parser, arguments, block);
15823 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
15824 found |= true;
15825 block = parse_block(parser, (uint16_t) (depth + 1));
15826 } else if (parsed_command_args && pm_accepts_block_stack_p(parser) && (flags & PM_PARSE_ACCEPTS_DO_BLOCK) && accept1(parser, PM_TOKEN_KEYWORD_DO_BLOCK)) {
15827 found |= true;
15828 block = parse_block(parser, (uint16_t) (depth + 1));
15829 }
15830
15831 if (block != NULL) {
15832 if (arguments->block == NULL && !arguments->has_forwarding) {
15833 arguments->block = UP(block);
15834 } else {
15835 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_BLOCK_MULTI);
15836
15837 if (arguments->block != NULL) {
15838 if (arguments->arguments == NULL) {
15839 arguments->arguments = pm_arguments_node_create(parser);
15840 }
15841 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, arguments->block);
15842 }
15843 arguments->block = UP(block);
15844 }
15845 }
15846 }
15847
15848 return found;
15849}
15850
15855static void
15856ensure_valid_return(pm_parser_t *parser, pm_node_t *node) {
15857 bool in_sclass = false;
15858 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15859 switch (context_node->context) {
15860 case PM_CONTEXT_BEGIN_ELSE:
15861 case PM_CONTEXT_BEGIN_ENSURE:
15862 case PM_CONTEXT_BEGIN_RESCUE:
15863 case PM_CONTEXT_BEGIN:
15864 case PM_CONTEXT_CASE_IN:
15865 case PM_CONTEXT_CASE_WHEN:
15866 case PM_CONTEXT_DEFAULT_PARAMS:
15867 case PM_CONTEXT_DEFINED:
15868 case PM_CONTEXT_ELSE:
15869 case PM_CONTEXT_ELSIF:
15870 case PM_CONTEXT_EMBEXPR:
15871 case PM_CONTEXT_FOR_INDEX:
15872 case PM_CONTEXT_FOR:
15873 case PM_CONTEXT_IF:
15874 case PM_CONTEXT_LOOP_PREDICATE:
15875 case PM_CONTEXT_MAIN:
15876 case PM_CONTEXT_MULTI_TARGET:
15877 case PM_CONTEXT_PARENS:
15878 case PM_CONTEXT_POSTEXE:
15879 case PM_CONTEXT_PREDICATE:
15880 case PM_CONTEXT_PREEXE:
15881 case PM_CONTEXT_RESCUE_MODIFIER:
15882 case PM_CONTEXT_TERNARY:
15883 case PM_CONTEXT_UNLESS:
15884 case PM_CONTEXT_UNTIL:
15885 case PM_CONTEXT_WHILE:
15886 // Keep iterating up the lists of contexts, because returns can
15887 // see through these.
15888 continue;
15889 case PM_CONTEXT_SCLASS_ELSE:
15890 case PM_CONTEXT_SCLASS_ENSURE:
15891 case PM_CONTEXT_SCLASS_RESCUE:
15892 case PM_CONTEXT_SCLASS:
15893 in_sclass = true;
15894 continue;
15895 case PM_CONTEXT_CLASS_ELSE:
15896 case PM_CONTEXT_CLASS_ENSURE:
15897 case PM_CONTEXT_CLASS_RESCUE:
15898 case PM_CONTEXT_CLASS:
15899 case PM_CONTEXT_MODULE_ELSE:
15900 case PM_CONTEXT_MODULE_ENSURE:
15901 case PM_CONTEXT_MODULE_RESCUE:
15902 case PM_CONTEXT_MODULE:
15903 // These contexts are invalid for a return.
15904 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15905 return;
15906 case PM_CONTEXT_BLOCK_BRACES:
15907 case PM_CONTEXT_BLOCK_ELSE:
15908 case PM_CONTEXT_BLOCK_ENSURE:
15909 case PM_CONTEXT_BLOCK_KEYWORDS:
15910 case PM_CONTEXT_BLOCK_RESCUE:
15911 case PM_CONTEXT_BLOCK_PARAMETERS:
15912 case PM_CONTEXT_DEF_ELSE:
15913 case PM_CONTEXT_DEF_ENSURE:
15914 case PM_CONTEXT_DEF_PARAMS:
15915 case PM_CONTEXT_DEF_RESCUE:
15916 case PM_CONTEXT_DEF:
15917 case PM_CONTEXT_LAMBDA_BRACES:
15918 case PM_CONTEXT_LAMBDA_DO_END:
15919 case PM_CONTEXT_LAMBDA_ELSE:
15920 case PM_CONTEXT_LAMBDA_ENSURE:
15921 case PM_CONTEXT_LAMBDA_RESCUE:
15922 // These contexts are valid for a return, and we should not
15923 // continue to loop.
15924 return;
15925 case PM_CONTEXT_NONE:
15926 case PM_CONTEXT_MAXIMUM:
15927 // This case should never happen.
15928 assert(false && "unreachable");
15929 break;
15930 }
15931 }
15932 if (in_sclass && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
15933 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15934 }
15935}
15936
15941static void
15942ensure_valid_jump(pm_parser_t *parser, pm_node_t *node) {
15943 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15944 switch (context_node->context) {
15945 case PM_CONTEXT_BLOCK_BRACES:
15946 case PM_CONTEXT_BLOCK_KEYWORDS:
15947 case PM_CONTEXT_BLOCK_ELSE:
15948 case PM_CONTEXT_BLOCK_ENSURE:
15949 case PM_CONTEXT_BLOCK_PARAMETERS:
15950 case PM_CONTEXT_BLOCK_RESCUE:
15951 case PM_CONTEXT_DEFINED:
15952 case PM_CONTEXT_FOR:
15953 case PM_CONTEXT_LAMBDA_BRACES:
15954 case PM_CONTEXT_LAMBDA_DO_END:
15955 case PM_CONTEXT_LAMBDA_ELSE:
15956 case PM_CONTEXT_LAMBDA_ENSURE:
15957 case PM_CONTEXT_LAMBDA_RESCUE:
15958 case PM_CONTEXT_LOOP_PREDICATE:
15959 case PM_CONTEXT_UNTIL:
15960 case PM_CONTEXT_WHILE:
15961 // These are the good cases. We're allowed to have a block exit
15962 // in these contexts.
15963 return;
15964 case PM_CONTEXT_POSTEXE:
15965 // https://bugs.ruby-lang.org/issues/20409
15966 if (context_node->context == PM_CONTEXT_POSTEXE) {
15967 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
15968 return;
15969 }
15970 }
15972 case PM_CONTEXT_DEF:
15973 case PM_CONTEXT_DEF_PARAMS:
15974 case PM_CONTEXT_DEF_ELSE:
15975 case PM_CONTEXT_DEF_ENSURE:
15976 case PM_CONTEXT_DEF_RESCUE:
15977 case PM_CONTEXT_MAIN:
15978 case PM_CONTEXT_PREEXE:
15979 case PM_CONTEXT_SCLASS:
15980 case PM_CONTEXT_SCLASS_ELSE:
15981 case PM_CONTEXT_SCLASS_ENSURE:
15982 case PM_CONTEXT_SCLASS_RESCUE:
15983 // These are the bad cases. We're not allowed to have a block
15984 // exit in these contexts.
15985 //
15986 // If we get here, then we're about to mark this block exit
15987 // as invalid. However, it could later _become_ valid if we
15988 // find a trailing while/until on the expression. In this
15989 // case instead of adding the error here, we'll add the
15990 // block exit to the list of exits for the expression, and
15991 // the node parsing will handle validating it instead.
15992 assert(parser->current_block_exits != NULL);
15993 pm_node_list_append(parser->arena, parser->current_block_exits, node);
15994 return;
15995 case PM_CONTEXT_BEGIN_ELSE:
15996 case PM_CONTEXT_BEGIN_ENSURE:
15997 case PM_CONTEXT_BEGIN_RESCUE:
15998 case PM_CONTEXT_BEGIN:
15999 case PM_CONTEXT_CASE_IN:
16000 case PM_CONTEXT_CASE_WHEN:
16001 case PM_CONTEXT_CLASS_ELSE:
16002 case PM_CONTEXT_CLASS_ENSURE:
16003 case PM_CONTEXT_CLASS_RESCUE:
16004 case PM_CONTEXT_CLASS:
16005 case PM_CONTEXT_DEFAULT_PARAMS:
16006 case PM_CONTEXT_ELSE:
16007 case PM_CONTEXT_ELSIF:
16008 case PM_CONTEXT_EMBEXPR:
16009 case PM_CONTEXT_FOR_INDEX:
16010 case PM_CONTEXT_IF:
16011 case PM_CONTEXT_MODULE_ELSE:
16012 case PM_CONTEXT_MODULE_ENSURE:
16013 case PM_CONTEXT_MODULE_RESCUE:
16014 case PM_CONTEXT_MODULE:
16015 case PM_CONTEXT_MULTI_TARGET:
16016 case PM_CONTEXT_PARENS:
16017 case PM_CONTEXT_PREDICATE:
16018 case PM_CONTEXT_RESCUE_MODIFIER:
16019 case PM_CONTEXT_TERNARY:
16020 case PM_CONTEXT_UNLESS:
16021 // In these contexts we should continue walking up the list of
16022 // contexts.
16023 break;
16024 case PM_CONTEXT_NONE:
16025 case PM_CONTEXT_MAXIMUM:
16026 // This case should never happen.
16027 assert(false && "unreachable");
16028 break;
16029 }
16030 }
16031}
16032
16037static pm_node_list_t *
16038push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
16039 pm_node_list_t *previous_block_exits = parser->current_block_exits;
16040 parser->current_block_exits = current_block_exits;
16041 return previous_block_exits;
16042}
16043
16049static void
16050flush_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
16051 pm_node_t *block_exit;
16052 PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
16053 const char *type;
16054
16055 switch (PM_NODE_TYPE(block_exit)) {
16056 case PM_BREAK_NODE: type = "break"; break;
16057 case PM_NEXT_NODE: type = "next"; break;
16058 case PM_REDO_NODE: type = "redo"; break;
16059 default: assert(false && "unreachable"); type = ""; break;
16060 }
16061
16062 PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
16063 }
16064
16065 parser->current_block_exits = previous_block_exits;
16066}
16067
16072static void
16073pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
16074 if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) {
16075 // If we matched a trailing while/until, then all of the block exits in
16076 // the contained list are valid. In this case we do not need to do
16077 // anything.
16078 parser->current_block_exits = previous_block_exits;
16079 } else if (previous_block_exits != NULL) {
16080 // If we did not matching a trailing while/until, then all of the block
16081 // exits contained in the list are invalid for this specific context.
16082 // However, they could still become valid in a higher level context if
16083 // there is another list above this one. In this case we'll push all of
16084 // the block exits up to the previous list.
16085 pm_node_list_concat(parser->arena, previous_block_exits, parser->current_block_exits);
16086 parser->current_block_exits = previous_block_exits;
16087 } else {
16088 // If we did not match a trailing while/until and this was the last
16089 // chance to do so, then all of the block exits in the list are invalid
16090 // and we need to add an error for each of them.
16091 flush_block_exits(parser, previous_block_exits);
16092 }
16093}
16094
16095static PRISM_INLINE pm_node_t *
16096parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword, uint16_t depth) {
16097 context_push(parser, PM_CONTEXT_PREDICATE);
16098 pm_diagnostic_id_t error_id = context == PM_CONTEXT_IF ? PM_ERR_CONDITIONAL_IF_PREDICATE : PM_ERR_CONDITIONAL_UNLESS_PREDICATE;
16099 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));
16100
16101 // Predicates are closed by a term, a "then", or a term and then a "then".
16102 bool predicate_closed = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16103
16104 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
16105 predicate_closed = true;
16106 *then_keyword = parser->previous;
16107 }
16108
16109 if (!predicate_closed) {
16110 pm_parser_err_current(parser, PM_ERR_CONDITIONAL_PREDICATE_TERM);
16111 }
16112
16113 context_pop(parser);
16114 return predicate;
16115}
16116
16117static PRISM_INLINE pm_node_t *
16118parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newline_index, bool if_after_else, uint16_t depth) {
16119 pm_node_list_t current_block_exits = { 0 };
16120 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
16121
16122 pm_token_t keyword = parser->previous;
16123 pm_token_t then_keyword = { 0 };
16124
16125 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, context, &then_keyword, (uint16_t) (depth + 1));
16126 pm_statements_node_t *statements = NULL;
16127
16128 if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
16129 pm_accepts_block_stack_push(parser, true);
16130 statements = parse_statements(parser, context, (uint16_t) (depth + 1));
16131 pm_accepts_block_stack_pop(parser);
16132 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16133 }
16134
16135 pm_node_t *parent = NULL;
16136
16137 switch (context) {
16138 case PM_CONTEXT_IF:
16139 parent = UP(pm_if_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
16140 break;
16141 case PM_CONTEXT_UNLESS:
16142 parent = UP(pm_unless_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements));
16143 break;
16144 default:
16145 assert(false && "unreachable");
16146 break;
16147 }
16148
16149 pm_node_t *current = parent;
16150
16151 // Parse any number of elsif clauses. This will form a linked list of if
16152 // nodes pointing to each other from the top.
16153 if (context == PM_CONTEXT_IF) {
16154 while (match1(parser, PM_TOKEN_KEYWORD_ELSIF)) {
16155 if (parser_end_of_line_p(parser)) {
16156 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
16157 }
16158
16159 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
16160 pm_token_t elsif_keyword = parser->current;
16161 parser_lex(parser);
16162
16163 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1));
16164 pm_accepts_block_stack_push(parser, true);
16165
16166 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1));
16167 pm_accepts_block_stack_pop(parser);
16168 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16169
16170 pm_node_t *elsif = UP(pm_if_node_create(parser, &elsif_keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
16171 ((pm_if_node_t *) current)->subsequent = elsif;
16172 current = elsif;
16173 }
16174 }
16175
16176 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
16177 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
16178 opening_newline_index = token_newline_index(parser);
16179
16180 parser_lex(parser);
16181 pm_token_t else_keyword = parser->previous;
16182
16183 pm_accepts_block_stack_push(parser, true);
16184 pm_statements_node_t *else_statements = parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1));
16185 pm_accepts_block_stack_pop(parser);
16186
16187 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16188 parser_warn_indentation_mismatch(parser, opening_newline_index, &else_keyword, false, false);
16189 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE, &keyword);
16190
16191 pm_else_node_t *else_node = pm_else_node_create(parser, &else_keyword, else_statements, &parser->previous);
16192
16193 switch (context) {
16194 case PM_CONTEXT_IF:
16195 ((pm_if_node_t *) current)->subsequent = UP(else_node);
16196 break;
16197 case PM_CONTEXT_UNLESS:
16198 ((pm_unless_node_t *) parent)->else_clause = else_node;
16199 break;
16200 default:
16201 assert(false && "unreachable");
16202 break;
16203 }
16204 } else {
16205 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false);
16206 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM, &keyword);
16207 }
16208
16209 // Set the appropriate end location for all of the nodes in the subtree.
16210 switch (context) {
16211 case PM_CONTEXT_IF: {
16212 pm_node_t *current = parent;
16213 bool recursing = true;
16214
16215 while (recursing) {
16216 switch (PM_NODE_TYPE(current)) {
16217 case PM_IF_NODE:
16218 pm_if_node_end_keyword_loc_set(parser, (pm_if_node_t *) current, &parser->previous);
16219 current = ((pm_if_node_t *) current)->subsequent;
16220 recursing = current != NULL;
16221 break;
16222 case PM_ELSE_NODE:
16223 pm_else_node_end_keyword_loc_set(parser, (pm_else_node_t *) current, &parser->previous);
16224 recursing = false;
16225 break;
16226 default: {
16227 recursing = false;
16228 break;
16229 }
16230 }
16231 }
16232 break;
16233 }
16234 case PM_CONTEXT_UNLESS:
16235 pm_unless_node_end_keyword_loc_set(parser, (pm_unless_node_t *) parent, &parser->previous);
16236 break;
16237 default:
16238 assert(false && "unreachable");
16239 break;
16240 }
16241
16242 pop_block_exits(parser, previous_block_exits);
16243 return parent;
16244}
16245
16250#define PM_CASE_KEYWORD PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16251 case PM_TOKEN_KEYWORD_ALIAS: case PM_TOKEN_KEYWORD_AND: case PM_TOKEN_KEYWORD_BEGIN: case PM_TOKEN_KEYWORD_BEGIN_UPCASE: \
16252 case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_CASE: case PM_TOKEN_KEYWORD_CLASS: case PM_TOKEN_KEYWORD_DEF: \
16253 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: \
16254 case PM_TOKEN_KEYWORD_ELSIF: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_END_UPCASE: case PM_TOKEN_KEYWORD_ENSURE: \
16255 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD_FOR: case PM_TOKEN_KEYWORD_IF: case PM_TOKEN_KEYWORD_IN: \
16256 case PM_TOKEN_KEYWORD_MODULE: case PM_TOKEN_KEYWORD_NEXT: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_NOT: \
16257 case PM_TOKEN_KEYWORD_OR: case PM_TOKEN_KEYWORD_REDO: case PM_TOKEN_KEYWORD_RESCUE: case PM_TOKEN_KEYWORD_RETRY: \
16258 case PM_TOKEN_KEYWORD_RETURN: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_SUPER: case PM_TOKEN_KEYWORD_THEN: \
16259 case PM_TOKEN_KEYWORD_TRUE: case PM_TOKEN_KEYWORD_UNDEF: case PM_TOKEN_KEYWORD_UNLESS: case PM_TOKEN_KEYWORD_UNTIL: \
16260 case PM_TOKEN_KEYWORD_WHEN: case PM_TOKEN_KEYWORD_WHILE: case PM_TOKEN_KEYWORD_YIELD
16261
16266#define PM_CASE_OPERATOR PM_TOKEN_AMPERSAND: case PM_TOKEN_BACKTICK: case PM_TOKEN_BANG_EQUAL: \
16267 case PM_TOKEN_BANG_TILDE: case PM_TOKEN_BANG: case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \
16268 case PM_TOKEN_BRACKET_LEFT_RIGHT: case PM_TOKEN_CARET: case PM_TOKEN_EQUAL_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL: \
16269 case PM_TOKEN_EQUAL_TILDE: case PM_TOKEN_GREATER_EQUAL: case PM_TOKEN_GREATER_GREATER: case PM_TOKEN_GREATER: \
16270 case PM_TOKEN_LESS_EQUAL_GREATER: case PM_TOKEN_LESS_EQUAL: case PM_TOKEN_LESS_LESS: case PM_TOKEN_LESS: \
16271 case PM_TOKEN_MINUS: case PM_TOKEN_PERCENT: case PM_TOKEN_PIPE: case PM_TOKEN_PLUS: case PM_TOKEN_SLASH: \
16272 case PM_TOKEN_STAR_STAR: case PM_TOKEN_STAR: case PM_TOKEN_TILDE: case PM_TOKEN_UAMPERSAND: case PM_TOKEN_UMINUS: \
16273 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_UPLUS: case PM_TOKEN_USTAR: case PM_TOKEN_USTAR_STAR
16274
16280#define PM_CASE_PRIMITIVE PM_TOKEN_INTEGER: case PM_TOKEN_INTEGER_IMAGINARY: case PM_TOKEN_INTEGER_RATIONAL: \
16281 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: case PM_TOKEN_FLOAT: case PM_TOKEN_FLOAT_IMAGINARY: \
16282 case PM_TOKEN_FLOAT_RATIONAL: case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: case PM_TOKEN_SYMBOL_BEGIN: \
16283 case PM_TOKEN_REGEXP_BEGIN: case PM_TOKEN_XSTRING_BEGIN: case PM_TOKEN_PERCENT_LOWER_X: case PM_TOKEN_PERCENT_LOWER_I: \
16284 case PM_TOKEN_PERCENT_LOWER_W: case PM_TOKEN_PERCENT_UPPER_I: case PM_TOKEN_PERCENT_UPPER_W: \
16285 case PM_TOKEN_STRING_BEGIN: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_TRUE: \
16286 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16287 case PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_MINUS_GREATER: case PM_TOKEN_HEREDOC_START: \
16288 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_CHARACTER_LITERAL
16289
16294#define PM_CASE_PARAMETER PM_TOKEN_UAMPERSAND: case PM_TOKEN_AMPERSAND: case PM_TOKEN_UDOT_DOT_DOT: \
16295 case PM_TOKEN_IDENTIFIER: case PM_TOKEN_LABEL: case PM_TOKEN_USTAR: case PM_TOKEN_STAR: case PM_TOKEN_STAR_STAR: \
16296 case PM_TOKEN_USTAR_STAR: case PM_TOKEN_CONSTANT: case PM_TOKEN_INSTANCE_VARIABLE: case PM_TOKEN_GLOBAL_VARIABLE: \
16297 case PM_TOKEN_CLASS_VARIABLE
16298
16303#define PM_CASE_WRITABLE PM_CLASS_VARIABLE_READ_NODE: case PM_CONSTANT_PATH_NODE: \
16304 case PM_CONSTANT_READ_NODE: case PM_GLOBAL_VARIABLE_READ_NODE: case PM_LOCAL_VARIABLE_READ_NODE: \
16305 case PM_INSTANCE_VARIABLE_READ_NODE: case PM_MULTI_TARGET_NODE: case PM_BACK_REFERENCE_READ_NODE: \
16306 case PM_NUMBERED_REFERENCE_READ_NODE: case PM_IT_LOCAL_VARIABLE_READ_NODE
16307
16308// Assert here that the flags are the same so that we can safely switch the type
16309// of the node without having to move the flags.
16310PM_STATIC_ASSERT(__LINE__, ((int) PM_STRING_FLAGS_FORCED_UTF8_ENCODING) == ((int) PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING), "Expected the flags to match.");
16311
16316static PRISM_INLINE pm_node_flags_t
16317parse_unescaped_encoding(const pm_parser_t *parser, const pm_encoding_t *explicit_encoding) {
16318 if (explicit_encoding != NULL) {
16319 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
16320 // If the there's an explicit encoding and it's using a UTF-8 escape
16321 // sequence, then mark the string as UTF-8.
16322 return PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
16323 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
16324 // If there's a non-UTF-8 escape sequence being used, then the
16325 // string uses the source encoding, unless the source is marked as
16326 // US-ASCII. In that case the string is forced as ASCII-8BIT in
16327 // order to keep the string valid.
16328 return PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
16329 }
16330 }
16331 return 0;
16332}
16333
16338static pm_node_t *
16339parse_string_part(pm_parser_t *parser, uint16_t depth) {
16340 switch (parser->current.type) {
16341 // Here the lexer has returned to us plain string content. In this case
16342 // we'll create a string node that has no opening or closing and return that
16343 // as the part. These kinds of parts look like:
16344 //
16345 // "aaa #{bbb} #@ccc ddd"
16346 // ^^^^ ^ ^^^^
16347 case PM_TOKEN_STRING_CONTENT: {
16348 pm_node_t *node = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16349 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
16350
16351 parser_lex(parser);
16352 return node;
16353 }
16354 // Here the lexer has returned the beginning of an embedded expression. In
16355 // that case we'll parse the inner statements and return that as the part.
16356 // These kinds of parts look like:
16357 //
16358 // "aaa #{bbb} #@ccc ddd"
16359 // ^^^^^^
16360 case PM_TOKEN_EMBEXPR_BEGIN: {
16361 // Ruby disallows seeing encoding around interpolation in strings,
16362 // even though it is known at parse time.
16363 parser->explicit_encoding = NULL;
16364
16365 pm_lex_state_t state = parser->lex_state;
16366 int brace_nesting = parser->brace_nesting;
16367
16368 parser->brace_nesting = 0;
16369 lex_state_set(parser, PM_LEX_STATE_BEG);
16370 parser_lex(parser);
16371
16372 pm_token_t opening = parser->previous;
16373 pm_statements_node_t *statements = NULL;
16374
16375 if (!match3(parser, PM_TOKEN_EMBEXPR_END, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
16376 statements = parse_statements(parser, PM_CONTEXT_EMBEXPR, (uint16_t) (depth + 1));
16377 }
16378
16379 parser->brace_nesting = brace_nesting;
16380 lex_state_set(parser, state);
16381 expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END);
16382
16383 // If this set of embedded statements only contains a single
16384 // statement, then Ruby does not consider it as a possible statement
16385 // that could emit a line event.
16386 if (statements != NULL && statements->body.size == 1) {
16387 pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE);
16388 }
16389
16390 return UP(pm_embedded_statements_node_create(parser, &opening, statements, &parser->previous));
16391 }
16392
16393 // Here the lexer has returned the beginning of an embedded variable.
16394 // In that case we'll parse the variable and create an appropriate node
16395 // for it and then return that node. These kinds of parts look like:
16396 //
16397 // "aaa #{bbb} #@ccc ddd"
16398 // ^^^^^
16399 case PM_TOKEN_EMBVAR: {
16400 // Ruby disallows seeing encoding around interpolation in strings,
16401 // even though it is known at parse time.
16402 parser->explicit_encoding = NULL;
16403
16404 lex_state_set(parser, PM_LEX_STATE_BEG);
16405 parser_lex(parser);
16406
16407 pm_token_t operator = parser->previous;
16408 pm_node_t *variable;
16409
16410 switch (parser->current.type) {
16411 // In this case a back reference is being interpolated. We'll
16412 // create a global variable read node.
16413 case PM_TOKEN_BACK_REFERENCE:
16414 parser_lex(parser);
16415 variable = UP(pm_back_reference_read_node_create(parser, &parser->previous));
16416 break;
16417 // In this case an nth reference is being interpolated. We'll
16418 // create a global variable read node.
16419 case PM_TOKEN_NUMBERED_REFERENCE:
16420 parser_lex(parser);
16421 variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16422 break;
16423 // In this case a global variable is being interpolated. We'll
16424 // create a global variable read node.
16425 case PM_TOKEN_GLOBAL_VARIABLE:
16426 parser_lex(parser);
16427 variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
16428 break;
16429 // In this case an instance variable is being interpolated.
16430 // We'll create an instance variable read node.
16431 case PM_TOKEN_INSTANCE_VARIABLE:
16432 parser_lex(parser);
16433 variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
16434 break;
16435 // In this case a class variable is being interpolated. We'll
16436 // create a class variable read node.
16437 case PM_TOKEN_CLASS_VARIABLE:
16438 parser_lex(parser);
16439 variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
16440 break;
16441 // We can hit here if we got an invalid token. In that case
16442 // we'll not attempt to lex this token and instead just return a
16443 // missing node.
16444 default:
16445 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EMBVAR_INVALID);
16446 variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16447 break;
16448 }
16449
16450 return UP(pm_embedded_variable_node_create(parser, &operator, variable));
16451 }
16452 default:
16453 parser_lex(parser);
16454 pm_parser_err_previous(parser, PM_ERR_CANNOT_PARSE_STRING_PART);
16455 return NULL;
16456 }
16457}
16458
16464static const uint8_t *
16465parse_operator_symbol_name(const pm_token_t *name) {
16466 switch (name->type) {
16467 case PM_TOKEN_TILDE:
16468 case PM_TOKEN_BANG:
16469 if (name->end[-1] == '@') return name->end - 1;
16471 default:
16472 return name->end;
16473 }
16474}
16475
16476static pm_node_t *
16477parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_state_t next_state) {
16478 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, opening, &parser->current, NULL);
16479 const uint8_t *end = parse_operator_symbol_name(&parser->current);
16480
16481 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16482 parser_lex(parser);
16483
16484 pm_string_shared_init(&symbol->unescaped, parser->previous.start, end);
16485 pm_node_flag_set(UP(symbol), PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING);
16486
16487 return UP(symbol);
16488}
16489
16495static pm_node_t *
16496parse_symbol(pm_parser_t *parser, pm_lex_state_t next_state, uint16_t depth) {
16497 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
16498 parser_lex(parser);
16499
16500 const pm_token_t opening = parser->previous;
16501
16502 if (lex_mode.mode != PM_LEX_STRING) {
16503 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16504
16505 switch (parser->current.type) {
16506 case PM_CASE_OPERATOR:
16507 return parse_operator_symbol(parser, &opening, next_state == PM_LEX_STATE_NONE ? PM_LEX_STATE_ENDFN : next_state);
16508 case PM_TOKEN_IDENTIFIER:
16509 case PM_TOKEN_CONSTANT:
16510 case PM_TOKEN_INSTANCE_VARIABLE:
16511 case PM_TOKEN_METHOD_NAME:
16512 case PM_TOKEN_CLASS_VARIABLE:
16513 case PM_TOKEN_GLOBAL_VARIABLE:
16514 case PM_TOKEN_NUMBERED_REFERENCE:
16515 case PM_TOKEN_BACK_REFERENCE:
16516 case PM_CASE_KEYWORD:
16517 parser_lex(parser);
16518 break;
16519 default:
16520 expect2(parser, PM_TOKEN_IDENTIFIER, PM_TOKEN_METHOD_NAME, PM_ERR_SYMBOL_INVALID);
16521 break;
16522 }
16523
16524 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, NULL);
16525 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16526 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16527
16528 return UP(symbol);
16529 }
16530
16531 if (lex_mode.as.string.interpolation) {
16532 // If we have the end of the symbol, then we can return an empty symbol.
16533 if (match1(parser, PM_TOKEN_STRING_END)) {
16534 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16535 parser_lex(parser);
16536 pm_token_t content = {
16537 .type = PM_TOKEN_STRING_CONTENT,
16538 .start = parser->previous.start,
16539 .end = parser->previous.start
16540 };
16541
16542 return UP(pm_symbol_node_create(parser, &opening, &content, &parser->previous));
16543 }
16544
16545 // Now we can parse the first part of the symbol.
16546 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
16547
16548 // If we got a string part, then it's possible that we could transform
16549 // what looks like an interpolated symbol into a regular symbol.
16550 if (part && PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16551 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16552 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16553
16554 return UP(pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous));
16555 }
16556
16557 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16558 if (part) pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16559
16560 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16561 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16562 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16563 }
16564 }
16565
16566 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16567 if (match1(parser, PM_TOKEN_EOF)) {
16568 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16569 } else {
16570 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16571 }
16572
16573 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16574 return UP(symbol);
16575 }
16576
16577 pm_token_t content;
16578 pm_string_t unescaped;
16579
16580 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16581 content = parser->current;
16582 unescaped = parser->current_string;
16583 parser_lex(parser);
16584
16585 // If we have two string contents in a row, then the content of this
16586 // symbol is split because of heredoc contents. This looks like:
16587 //
16588 // <<A; :'a
16589 // A
16590 // b'
16591 //
16592 // In this case, the best way we have to represent this is as an
16593 // interpolated string node, so that's what we'll do here.
16594 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16595 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16596 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16597 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16598
16599 part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->current, NULL, &parser->current_string));
16600 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16601
16602 if (next_state != PM_LEX_STATE_NONE) {
16603 lex_state_set(parser, next_state);
16604 }
16605
16606 parser_lex(parser);
16607 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16608
16609 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16610 return UP(symbol);
16611 }
16612 } else {
16613 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end };
16614 pm_string_shared_init(&unescaped, content.start, content.end);
16615 }
16616
16617 if (next_state != PM_LEX_STATE_NONE) {
16618 lex_state_set(parser, next_state);
16619 }
16620
16621 if (match1(parser, PM_TOKEN_EOF)) {
16622 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_DYNAMIC);
16623 } else {
16624 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16625 }
16626
16627 return UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, false)));
16628}
16629
16634static PRISM_INLINE pm_node_t *
16635parse_undef_argument(pm_parser_t *parser, uint16_t depth) {
16636 switch (parser->current.type) {
16637 case PM_CASE_OPERATOR:
16638 return parse_operator_symbol(parser, NULL, PM_LEX_STATE_NONE);
16639 case PM_CASE_KEYWORD:
16640 case PM_TOKEN_CONSTANT:
16641 case PM_TOKEN_IDENTIFIER:
16642 case PM_TOKEN_METHOD_NAME: {
16643 parser_lex(parser);
16644
16645 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16646 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16647 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16648
16649 return UP(symbol);
16650 }
16651 case PM_TOKEN_SYMBOL_BEGIN:
16652 return parse_symbol(parser, PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16653 default:
16654 pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT);
16655 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16656 }
16657}
16658
16665static PRISM_INLINE pm_node_t *
16666parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) {
16667 switch (parser->current.type) {
16668 case PM_CASE_OPERATOR:
16669 return parse_operator_symbol(parser, NULL, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE);
16670 case PM_CASE_KEYWORD:
16671 case PM_TOKEN_CONSTANT:
16672 case PM_TOKEN_IDENTIFIER:
16673 case PM_TOKEN_METHOD_NAME: {
16674 if (first) lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
16675 parser_lex(parser);
16676
16677 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16678 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16679 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16680
16681 return UP(symbol);
16682 }
16683 case PM_TOKEN_SYMBOL_BEGIN:
16684 return parse_symbol(parser, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16685 case PM_TOKEN_BACK_REFERENCE:
16686 parser_lex(parser);
16687 return UP(pm_back_reference_read_node_create(parser, &parser->previous));
16688 case PM_TOKEN_NUMBERED_REFERENCE:
16689 parser_lex(parser);
16690 return UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16691 case PM_TOKEN_GLOBAL_VARIABLE:
16692 parser_lex(parser);
16693 return UP(pm_global_variable_read_node_create(parser, &parser->previous));
16694 default:
16695 pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT);
16696 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16697 }
16698}
16699
16704static pm_node_t *
16705parse_variable(pm_parser_t *parser) {
16706 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &parser->previous);
16707 int depth;
16708 bool is_numbered_param = pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
16709
16710 if (!is_numbered_param && ((depth = pm_parser_local_depth_constant_id(parser, name_id)) != -1)) {
16711 return UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false));
16712 }
16713
16714 pm_scope_t *current_scope = parser->current_scope;
16715 if (!current_scope->closed && !(current_scope->parameters & PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED)) {
16716 if (is_numbered_param) {
16717 // When you use a numbered parameter, it implies the existence of
16718 // all of the locals that exist before it. For example, referencing
16719 // _2 means that _1 must exist. Therefore here we loop through all
16720 // of the possibilities and add them into the constant pool.
16721 uint8_t maximum = (uint8_t) (parser->previous.start[1] - '0');
16722 for (uint8_t number = 1; number <= maximum; number++) {
16723 pm_parser_local_add_constant(parser, pm_numbered_parameter_names[number - 1], 2);
16724 }
16725
16726 if (!match1(parser, PM_TOKEN_EQUAL)) {
16727 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_FOUND;
16728 }
16729
16730 pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false));
16731 pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16732
16733 return node;
16734 } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16735 pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous));
16736 pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16737
16738 return node;
16739 }
16740 }
16741
16742 return NULL;
16743}
16744
16748static pm_node_t *
16749parse_variable_call(pm_parser_t *parser) {
16750 pm_node_flags_t flags = 0;
16751
16752 if (!match1(parser, PM_TOKEN_PARENTHESIS_LEFT) && (parser->previous.end[-1] != '!') && (parser->previous.end[-1] != '?')) {
16753 pm_node_t *node = parse_variable(parser);
16754 if (node != NULL) return node;
16755 flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL;
16756 }
16757
16758 pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous);
16759 pm_node_flag_set(UP(node), flags);
16760
16761 return UP(node);
16762}
16763
16770parse_method_definition_name(pm_parser_t *parser) {
16771 switch (parser->current.type) {
16772 case PM_CASE_KEYWORD:
16773 case PM_TOKEN_CONSTANT:
16774 case PM_TOKEN_METHOD_NAME:
16775 parser_lex(parser);
16776 return parser->previous;
16777 case PM_TOKEN_IDENTIFIER:
16778 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current));
16779 parser_lex(parser);
16780 return parser->previous;
16781 case PM_CASE_OPERATOR:
16782 lex_state_set(parser, PM_LEX_STATE_ENDFN);
16783 parser_lex(parser);
16784 return parser->previous;
16785 default:
16786 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_NAME, pm_token_str(parser->current.type));
16787 return (pm_token_t) { .type = 0, .start = parser->current.start, .end = parser->current.end };
16788 }
16789}
16790
16791static void
16792parse_heredoc_dedent_string(pm_arena_t *arena, pm_string_t *string, size_t common_whitespace) {
16793 // Make a writable copy in the arena if the string isn't already writable.
16794 // We keep a mutable pointer to the arena memory so we can memmove into it
16795 // below without casting away const from the string's source field.
16796 uint8_t *writable;
16797
16798 if (string->type != PM_STRING_OWNED) {
16799 size_t length = pm_string_length(string);
16800 writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
16801 pm_string_constant_init(string, (const char *) writable, length);
16802 } else {
16803 writable = (uint8_t *) string->source;
16804 }
16805
16806 // Now get the bounds of the existing string. We'll use this as a
16807 // destination to move bytes into. We'll also use it for bounds checking
16808 // since we don't require that these strings be null terminated.
16809 size_t dest_length = pm_string_length(string);
16810 const uint8_t *source_cursor = writable;
16811 const uint8_t *source_end = source_cursor + dest_length;
16812
16813 // We're going to move bytes backward in the string when we get leading
16814 // whitespace, so we'll maintain a pointer to the current position in the
16815 // string that we're writing to.
16816 size_t trimmed_whitespace = 0;
16817
16818 // While we haven't reached the amount of common whitespace that we need to
16819 // trim and we haven't reached the end of the string, we'll keep trimming
16820 // whitespace. Trimming in this context means skipping over these bytes such
16821 // that they aren't copied into the new string.
16822 while ((source_cursor < source_end) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
16823 if (*source_cursor == '\t') {
16824 trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
16825 if (trimmed_whitespace > common_whitespace) break;
16826 } else {
16827 trimmed_whitespace++;
16828 }
16829
16830 source_cursor++;
16831 dest_length--;
16832 }
16833
16834 memmove(writable, source_cursor, (size_t) (source_end - source_cursor));
16835 string->length = dest_length;
16836}
16837
16842static PRISM_INLINE bool
16843heredoc_dedent_discard_string_node(pm_parser_t *parser, pm_string_node_t *string_node) {
16844 if (string_node->unescaped.length == 0) {
16845 const uint8_t *cursor = parser->start + PM_LOCATION_START(&string_node->content_loc);
16846 return pm_memchr(cursor, '\\', string_node->content_loc.length, parser->encoding_changed, parser->encoding) == NULL;
16847 }
16848 return false;
16849}
16850
16854static void
16855parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_whitespace) {
16856 // The next node should be dedented if it's the first node in the list or if
16857 // it follows a string node.
16858 bool dedent_next = true;
16859
16860 // Iterate over all nodes, and trim whitespace accordingly. We're going to
16861 // keep around two indices: a read and a write.
16862 size_t write_index = 0;
16863
16864 pm_node_t *node;
16865 PM_NODE_LIST_FOREACH(nodes, read_index, node) {
16866 // We're not manipulating child nodes that aren't strings. In this case
16867 // we'll skip past it and indicate that the subsequent node should not
16868 // be dedented.
16869 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE)) {
16870 nodes->nodes[write_index++] = node;
16871 dedent_next = false;
16872 continue;
16873 }
16874
16875 pm_string_node_t *string_node = ((pm_string_node_t *) node);
16876 if (dedent_next) {
16877 parse_heredoc_dedent_string(parser->arena, &string_node->unescaped, common_whitespace);
16878 }
16879
16880 if (heredoc_dedent_discard_string_node(parser, string_node)) {
16881 } else {
16882 nodes->nodes[write_index++] = node;
16883 }
16884
16885 // We always dedent the next node if it follows a string node.
16886 dedent_next = true;
16887 }
16888
16889 nodes->size = write_index;
16890}
16891
16895static pm_token_t
16896parse_strings_empty_content(const uint8_t *location) {
16897 return (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = location, .end = location };
16898}
16899
16903static PRISM_INLINE pm_node_t *
16904parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint16_t depth) {
16905 assert(parser->current.type == PM_TOKEN_STRING_BEGIN);
16906 bool concating = false;
16907
16908 while (match1(parser, PM_TOKEN_STRING_BEGIN)) {
16909 pm_node_t *node = NULL;
16910
16911 // Here we have found a string literal. We'll parse it and add it to
16912 // the list of strings.
16913 const pm_lex_mode_t *lex_mode = parser->lex_modes.current;
16914 assert(lex_mode->mode == PM_LEX_STRING);
16915 bool lex_interpolation = lex_mode->as.string.interpolation;
16916 bool label_allowed = lex_mode->as.string.label_allowed && accepts_label;
16917
16918 pm_token_t opening = parser->current;
16919 parser_lex(parser);
16920
16921 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16922 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16923 // If we get here, then we have an end immediately after a
16924 // start. In that case we'll create an empty content token and
16925 // return an uninterpolated string.
16926 pm_token_t content = parse_strings_empty_content(parser->previous.start);
16927 pm_string_node_t *string = pm_string_node_create(parser, &opening, &content, &parser->previous);
16928
16929 pm_string_shared_init(&string->unescaped, content.start, content.end);
16930 node = UP(string);
16931 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16932 // If we get here, then we have an end of a label immediately
16933 // after a start. In that case we'll create an empty symbol
16934 // node.
16935 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, NULL, &parser->previous);
16936 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.start);
16937 node = UP(symbol);
16938
16939 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16940 } else if (!lex_interpolation) {
16941 // If we don't accept interpolation then we expect the string to
16942 // start with a single string content node.
16943 pm_string_t unescaped;
16944 pm_token_t content;
16945
16946 if (match1(parser, PM_TOKEN_EOF)) {
16947 unescaped = PM_STRING_EMPTY;
16948 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start, .end = parser->start };
16949 } else {
16950 unescaped = parser->current_string;
16951 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_EXPECT_STRING_CONTENT);
16952 content = parser->previous;
16953 }
16954
16955 // It is unfortunately possible to have multiple string content
16956 // nodes in a row in the case that there's heredoc content in
16957 // the middle of the string, like this cursed example:
16958 //
16959 // <<-END+'b
16960 // a
16961 // END
16962 // c'+'d'
16963 //
16964 // In that case we need to switch to an interpolated string to
16965 // be able to contain all of the parts.
16966 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16967 pm_node_list_t parts = { 0 };
16968 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16969 pm_node_list_append(parser->arena, &parts, part);
16970
16971 do {
16972 part = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16973 pm_node_list_append(parser->arena, &parts, part);
16974 parser_lex(parser);
16975 } while (match1(parser, PM_TOKEN_STRING_CONTENT));
16976
16977 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16978 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16979 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16980 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, true)));
16981 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16982 } else if (match1(parser, PM_TOKEN_EOF)) {
16983 pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF);
16984 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16985 } else if (accept1(parser, PM_TOKEN_STRING_END)) {
16986 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16987 } else {
16988 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_str(parser->previous.type));
16989 parser->previous.start = parser->previous.end;
16990 parser->previous.type = 0;
16991 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16992 }
16993 } else if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16994 // In this case we've hit string content so we know the string
16995 // at least has something in it. We'll need to check if the
16996 // following token is the end (in which case we can return a
16997 // plain string) or if it's not then it has interpolation.
16998 pm_token_t content = parser->current;
16999 pm_string_t unescaped = parser->current_string;
17000 const pm_encoding_t *explicit_encoding = parser->explicit_encoding;
17001 parser_lex(parser);
17002
17003 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
17004 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
17005 pm_node_flag_set(node, parse_unescaped_encoding(parser, explicit_encoding));
17006
17007 // Kind of odd behavior, but basically if we have an
17008 // unterminated string and it ends in a newline, we back up one
17009 // character so that the error message is on the last line of
17010 // content in the string.
17011 if (!accept1(parser, PM_TOKEN_STRING_END)) {
17012 const uint8_t *location = parser->previous.end;
17013 if (location > parser->start && location[-1] == '\n') location--;
17014 pm_parser_err(parser, U32(location - parser->start), 0, PM_ERR_STRING_LITERAL_EOF);
17015
17016 parser->previous.start = parser->previous.end;
17017 parser->previous.type = 0;
17018 }
17019 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
17020 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, explicit_encoding, &content, &unescaped, true)));
17021 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
17022 } else {
17023 // If we get here, then we have interpolation so we'll need
17024 // to create a string or symbol node with interpolation.
17025 pm_node_list_t parts = { 0 };
17026 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
17027 pm_node_flag_set(part, parse_unescaped_encoding(parser, explicit_encoding));
17028 pm_node_list_append(parser->arena, &parts, part);
17029
17030 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
17031 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
17032 pm_node_list_append(parser->arena, &parts, part);
17033 }
17034 }
17035
17036 if (accept1(parser, PM_TOKEN_LABEL_END)) {
17037 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
17038 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
17039 } else if (match1(parser, PM_TOKEN_EOF)) {
17040 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
17041 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
17042 } else {
17043 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
17044 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
17045 }
17046 }
17047 } else {
17048 // If we get here, then the first part of the string is not plain
17049 // string content, in which case we need to parse the string as an
17050 // interpolated string.
17051 pm_node_list_t parts = { 0 };
17052 pm_node_t *part;
17053
17054 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
17055 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
17056 pm_node_list_append(parser->arena, &parts, part);
17057 }
17058 }
17059
17060 if (accept1(parser, PM_TOKEN_LABEL_END)) {
17061 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
17062 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
17063 } else if (match1(parser, PM_TOKEN_EOF)) {
17064 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
17065 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
17066 } else {
17067 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
17068 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
17069 }
17070 }
17071
17072 if (current == NULL) {
17073 // If the node we just parsed is a symbol node, then we can't
17074 // concatenate it with anything else, so we can now return that
17075 // node.
17076 if (PM_NODE_TYPE_P(node, PM_SYMBOL_NODE) || PM_NODE_TYPE_P(node, PM_INTERPOLATED_SYMBOL_NODE)) {
17077 return node;
17078 }
17079
17080 // If we don't already have a node, then it's fine and we can just
17081 // set the result to be the node we just parsed.
17082 current = node;
17083 } else {
17084 // Otherwise we need to check the type of the node we just parsed.
17085 // If it cannot be concatenated with the previous node, then we'll
17086 // need to add a syntax error.
17087 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE) && !PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
17088 pm_parser_err_node(parser, node, PM_ERR_STRING_CONCATENATION);
17089 }
17090
17091 // If we haven't already created our container for concatenation,
17092 // we'll do that now.
17093 if (!concating) {
17094 if (!PM_NODE_TYPE_P(current, PM_STRING_NODE) && !PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
17095 pm_parser_err_node(parser, current, PM_ERR_STRING_CONCATENATION);
17096 }
17097
17098 concating = true;
17099 pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
17100 pm_interpolated_string_node_append(parser, container, current);
17101 current = UP(container);
17102 }
17103
17104 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, node);
17105 }
17106 }
17107
17108 return current;
17109}
17110
17111#define PM_PARSE_PATTERN_SINGLE 0
17112#define PM_PARSE_PATTERN_TOP 1
17113#define PM_PARSE_PATTERN_MULTI 2
17114
17115static pm_node_t *
17116parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
17117
17123static void
17124parse_pattern_capture(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_constant_id_t capture, const pm_location_t *location) {
17125 // Skip this capture if it starts with an underscore.
17126 if (peek_at(parser, parser->start + location->start) == '_') return;
17127
17128 if (!pm_constant_id_set_insert(parser->arena, captures, capture)) {
17129 pm_parser_err(parser, location->start, location->length, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
17130 }
17131}
17132
17136static pm_node_t *
17137parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_t *node, uint16_t depth) {
17138 // Now, if there are any :: operators that follow, parse them as constant
17139 // path nodes.
17140 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
17141 pm_token_t delimiter = parser->previous;
17142 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
17143 node = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
17144 }
17145
17146 // If there is a [ or ( that follows, then this is part of a larger pattern
17147 // expression. We'll parse the inner pattern here, then modify the returned
17148 // inner pattern with our constant path attached.
17149 if (!match2(parser, PM_TOKEN_BRACKET_LEFT, PM_TOKEN_PARENTHESIS_LEFT)) {
17150 return node;
17151 }
17152
17153 pm_token_t opening;
17154 pm_token_t closing;
17155 pm_node_t *inner = NULL;
17156
17157 if (accept1(parser, PM_TOKEN_BRACKET_LEFT)) {
17158 opening = parser->previous;
17159 accept1(parser, PM_TOKEN_NEWLINE);
17160
17161 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17162 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
17163 accept1(parser, PM_TOKEN_NEWLINE);
17164 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
17165 }
17166
17167 closing = parser->previous;
17168 } else {
17169 parser_lex(parser);
17170 opening = parser->previous;
17171 accept1(parser, PM_TOKEN_NEWLINE);
17172
17173 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
17174 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17175 accept1(parser, PM_TOKEN_NEWLINE);
17176 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17177 }
17178
17179 closing = parser->previous;
17180 }
17181
17182 if (!inner) {
17183 // If there was no inner pattern, then we have something like Foo() or
17184 // Foo[]. In that case we'll create an array pattern with no requireds.
17185 return UP(pm_array_pattern_node_constant_create(parser, node, &opening, &closing));
17186 }
17187
17188 // Now that we have the inner pattern, check to see if it's an array, find,
17189 // or hash pattern. If it is, then we'll attach our constant path to it if
17190 // it doesn't already have a constant. If it's not one of those node types
17191 // or it does have a constant, then we'll create an array pattern.
17192 switch (PM_NODE_TYPE(inner)) {
17193 case PM_ARRAY_PATTERN_NODE: {
17194 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17195
17196 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17197 PM_NODE_START_SET_NODE(pattern_node, node);
17198 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17199
17200 pattern_node->constant = node;
17201 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17202 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17203
17204 return UP(pattern_node);
17205 }
17206
17207 break;
17208 }
17209 case PM_FIND_PATTERN_NODE: {
17210 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17211
17212 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17213 PM_NODE_START_SET_NODE(pattern_node, node);
17214 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17215
17216 pattern_node->constant = node;
17217 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17218 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17219
17220 return UP(pattern_node);
17221 }
17222
17223 break;
17224 }
17225 case PM_HASH_PATTERN_NODE: {
17226 pm_hash_pattern_node_t *pattern_node = (pm_hash_pattern_node_t *) inner;
17227
17228 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17229 PM_NODE_START_SET_NODE(pattern_node, node);
17230 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17231
17232 pattern_node->constant = node;
17233 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17234 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17235
17236 return UP(pattern_node);
17237 }
17238
17239 break;
17240 }
17241 default:
17242 break;
17243 }
17244
17245 // If we got here, then we didn't return one of the inner patterns by
17246 // attaching its constant. In this case we'll create an array pattern and
17247 // attach our constant to it.
17248 pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing);
17249 pm_array_pattern_node_requireds_append(parser->arena, pattern_node, inner);
17250 return UP(pattern_node);
17251}
17252
17256static pm_splat_node_t *
17257parse_pattern_rest(pm_parser_t *parser, pm_constant_id_set_t *captures) {
17258 assert(parser->previous.type == PM_TOKEN_USTAR);
17259 pm_token_t operator = parser->previous;
17260 pm_node_t *name = NULL;
17261
17262 // Rest patterns don't necessarily have a name associated with them. So we
17263 // will check for that here. If they do, then we'll add it to the local
17264 // table since this pattern will cause it to become a local variable.
17265 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17266 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17267
17268 int depth;
17269 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17270 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17271 }
17272
17273 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17274 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17275 name = UP(pm_local_variable_target_node_create(
17276 parser,
17277 &previous_loc,
17278 constant_id,
17279 (uint32_t) (depth == -1 ? 0 : depth)
17280 ));
17281 }
17282
17283 // Finally we can return the created node.
17284 return pm_splat_node_create(parser, &operator, name);
17285}
17286
17290static pm_node_t *
17291parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_set_t *captures) {
17292 assert(parser->current.type == PM_TOKEN_USTAR_STAR);
17293 parser_lex(parser);
17294
17295 pm_token_t operator = parser->previous;
17296 pm_node_t *value = NULL;
17297
17298 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
17299 return UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
17300 }
17301
17302 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17303 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17304
17305 int depth;
17306 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17307 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17308 }
17309
17310 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17311 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17312 value = UP(pm_local_variable_target_node_create(
17313 parser,
17314 &previous_loc,
17315 constant_id,
17316 (uint32_t) (depth == -1 ? 0 : depth)
17317 ));
17318 }
17319
17320 return UP(pm_assoc_splat_node_create(parser, value, &operator));
17321}
17322
17327static bool
17328pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
17329 ptrdiff_t length = end - start;
17330 if (length == 0) return false;
17331
17332 // First ensure that it starts with a valid identifier starting character.
17333 size_t width = char_is_identifier_start(parser, start, end - start);
17334 if (width == 0) return false;
17335
17336 // Next, ensure that it's not an uppercase character.
17337 if (parser->encoding_changed) {
17338 if (parser->encoding->isupper_char(start, length)) return false;
17339 } else {
17340 if (pm_encoding_utf_8_isupper_char(start, length)) return false;
17341 }
17342
17343 // Next, iterate through all of the bytes of the string to ensure that they
17344 // are all valid identifier characters.
17345 const uint8_t *cursor = start + width;
17346 while ((width = char_is_identifier(parser, cursor, end - cursor))) cursor += width;
17347 return cursor == end;
17348}
17349
17354static pm_node_t *
17355parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_symbol_node_t *key) {
17356 const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->content_loc;
17357 const uint8_t *start = parser->start + PM_LOCATION_START(value_loc);
17358 const uint8_t *end = parser->start + PM_LOCATION_END(value_loc);
17359
17360 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
17361 int depth = -1;
17362
17363 if (pm_slice_is_valid_local(parser, start, end)) {
17364 depth = pm_parser_local_depth_constant_id(parser, constant_id);
17365 } else {
17366 pm_parser_err(parser, PM_NODE_START(key), PM_NODE_LENGTH(key), PM_ERR_PATTERN_HASH_KEY_LOCALS);
17367
17368 if ((end > start) && ((end[-1] == '!') || (end[-1] == '?'))) {
17369 PM_PARSER_ERR_FORMAT(parser, value_loc->start, value_loc->length, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (end - start), (const char *) start);
17370 }
17371 }
17372
17373 if (depth == -1) {
17374 pm_parser_local_add(parser, constant_id, start, end, 0);
17375 }
17376
17377 parse_pattern_capture(parser, captures, constant_id, value_loc);
17378 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17379 parser,
17380 value_loc,
17381 constant_id,
17382 (uint32_t) (depth == -1 ? 0 : depth)
17383 );
17384
17385 return UP(pm_implicit_node_create(parser, UP(target)));
17386}
17387
17392static void
17393parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) {
17394 if (pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, keys, node, true) != NULL) {
17395 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE);
17396 }
17397}
17398
17403parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_t *first_node, uint16_t depth) {
17404 pm_node_list_t assocs = { 0 };
17405 pm_static_literals_t keys = { 0 };
17406 pm_node_t *rest = NULL;
17407
17408 switch (PM_NODE_TYPE(first_node)) {
17409 case PM_ASSOC_SPLAT_NODE:
17410 case PM_NO_KEYWORDS_PARAMETER_NODE:
17411 rest = first_node;
17412 break;
17413 case PM_INTERPOLATED_SYMBOL_NODE:
17414 case PM_SYMBOL_NODE: {
17415 if (pm_symbol_node_label_p(parser, first_node)) {
17416 if (PM_NODE_TYPE_P(first_node, PM_INTERPOLATED_SYMBOL_NODE)) {
17417 pm_parser_err_node(parser, first_node, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17418 } else {
17419 parse_pattern_hash_key(parser, &keys, first_node);
17420 }
17421
17422 pm_node_t *value;
17423
17424 /*
17425 * The label has an implicit value when the next token cannot
17426 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17427 * reduction.
17428 */
17429 if (!token_begins_pattern_p(parser->current.type)) {
17430 if (PM_NODE_TYPE_P(first_node, PM_SYMBOL_NODE)) {
17431 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
17432 } else {
17433 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(first_node), 0));
17434 }
17435 } else {
17436 // Here we have a value for the first assoc in the list, so
17437 // we will parse it now.
17438 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17439 }
17440
17441 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17442 pm_node_list_append(parser->arena, &assocs, assoc);
17443 break;
17444 }
17445 }
17447 default: {
17448 // If we get anything else, then this is an error. For this we'll
17449 // create a missing node for the value and create an assoc node for
17450 // the first node in the list.
17451 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;
17452 pm_parser_err_node(parser, first_node, diag_id);
17453
17454 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_START(first_node), PM_NODE_LENGTH(first_node)));
17455 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17456
17457 pm_node_list_append(parser->arena, &assocs, assoc);
17458 break;
17459 }
17460 }
17461
17462 // If there are any other assocs, then we'll parse them now.
17463 while (accept1(parser, PM_TOKEN_COMMA)) {
17464 /*
17465 * A trailing comma ends the pattern when the next token cannot begin
17466 * another element, mirroring the grammar's `p_kwargs: p_kwarg ','`
17467 * reduction.
17468 */
17469 if (!token_begins_pattern_p(parser->current.type)) {
17470 // Trailing commas are not allowed to follow a rest pattern.
17471 if (rest != NULL) {
17472 pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17473 }
17474
17475 break;
17476 }
17477
17478 if (match1(parser, PM_TOKEN_USTAR_STAR)) {
17479 pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures);
17480
17481 if (rest == NULL) {
17482 rest = assoc;
17483 } else {
17484 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17485 pm_node_list_append(parser->arena, &assocs, assoc);
17486 }
17487 } else {
17488 pm_node_t *key;
17489
17490 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
17491 key = parse_strings(parser, NULL, true, (uint16_t) (depth + 1));
17492
17493 if (PM_NODE_TYPE_P(key, PM_INTERPOLATED_SYMBOL_NODE)) {
17494 pm_parser_err_node(parser, key, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17495 } else if (!pm_symbol_node_label_p(parser, key)) {
17496 pm_parser_err_node(parser, key, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17497 }
17498 } else if (accept1(parser, PM_TOKEN_LABEL)) {
17499 key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17500 } else {
17501 expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17502
17503 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = parser->previous.end, .end = parser->previous.end };
17504 key = UP(pm_symbol_node_create(parser, NULL, &label, NULL));
17505 }
17506
17507 parse_pattern_hash_key(parser, &keys, key);
17508 pm_node_t *value = NULL;
17509
17510 /*
17511 * The label has an implicit value when the next token cannot
17512 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17513 * reduction.
17514 */
17515 if (!token_begins_pattern_p(parser->current.type)) {
17516 if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) {
17517 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key);
17518 } else {
17519 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(key), 0));
17520 }
17521 } else {
17522 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17523 }
17524
17525 pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, NULL, value));
17526
17527 if (rest != NULL) {
17528 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17529 }
17530
17531 pm_node_list_append(parser->arena, &assocs, assoc);
17532 }
17533 }
17534
17535 pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest);
17536 // assocs.nodes is arena-allocated; no explicit free needed.
17537
17538 pm_static_literals_free(&keys);
17539 return node;
17540}
17541
17545static pm_node_t *
17546parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_diagnostic_id_t diag_id, uint16_t depth) {
17547 switch (parser->current.type) {
17548 case PM_TOKEN_IDENTIFIER:
17549 case PM_TOKEN_METHOD_NAME: {
17550 parser_lex(parser);
17551 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17552
17553 int depth;
17554 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17555 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17556 }
17557
17558 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17559 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17560 return UP(pm_local_variable_target_node_create(
17561 parser,
17562 &previous_loc,
17563 constant_id,
17564 (uint32_t) (depth == -1 ? 0 : depth)
17565 ));
17566 }
17567 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
17568 pm_token_t opening = parser->current;
17569 parser_lex(parser);
17570
17571 if (accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17572 // If we have an empty array pattern, then we'll just return a new
17573 // array pattern node.
17574 return UP(pm_array_pattern_node_empty_create(parser, &opening, &parser->previous));
17575 }
17576
17577 // Otherwise, we'll parse the inner pattern, then deal with it depending
17578 // on the type it returns.
17579 pm_node_t *inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
17580
17581 accept1(parser, PM_TOKEN_NEWLINE);
17582 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
17583 pm_token_t closing = parser->previous;
17584
17585 switch (PM_NODE_TYPE(inner)) {
17586 case PM_ARRAY_PATTERN_NODE: {
17587 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17588 if (pattern_node->opening_loc.length == 0) {
17589 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17590 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17591
17592 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17593 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17594
17595 return UP(pattern_node);
17596 }
17597
17598 break;
17599 }
17600 case PM_FIND_PATTERN_NODE: {
17601 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17602 if (pattern_node->opening_loc.length == 0) {
17603 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17604 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17605
17606 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17607 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17608
17609 return UP(pattern_node);
17610 }
17611
17612 break;
17613 }
17614 default:
17615 break;
17616 }
17617
17618 pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing);
17619 pm_array_pattern_node_requireds_append(parser->arena, node, inner);
17620 return UP(node);
17621 }
17622 case PM_TOKEN_BRACE_LEFT_HASH: {
17623 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17624 parser->pattern_matching_newlines = false;
17625
17627 pm_token_t opening = parser->current;
17628 parser_lex(parser);
17629
17630 if (accept1(parser, PM_TOKEN_BRACE_RIGHT)) {
17631 // If we have an empty hash pattern, then we'll just return a new hash
17632 // pattern node.
17633 node = pm_hash_pattern_node_empty_create(parser, &opening, &parser->previous);
17634 } else {
17635 pm_node_t *first_node;
17636
17637 switch (parser->current.type) {
17638 case PM_TOKEN_LABEL:
17639 parser_lex(parser);
17640 first_node = UP(pm_symbol_node_label_create(parser, &parser->previous));
17641 break;
17642 case PM_TOKEN_USTAR_STAR:
17643 first_node = parse_pattern_keyword_rest(parser, captures);
17644 break;
17645 case PM_TOKEN_STRING_BEGIN:
17646 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));
17647 break;
17648 default: {
17649 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_str(parser->current.type));
17650 parser_lex(parser);
17651
17652 first_node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
17653 break;
17654 }
17655 }
17656
17657 node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1));
17658
17659 accept1(parser, PM_TOKEN_NEWLINE);
17660 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE, &opening);
17661 pm_token_t closing = parser->previous;
17662
17663 PM_NODE_START_SET_TOKEN(parser, node, &opening);
17664 PM_NODE_LENGTH_SET_TOKEN(parser, node, &closing);
17665
17666 node->opening_loc = TOK2LOC(parser, &opening);
17667 node->closing_loc = TOK2LOC(parser, &closing);
17668 }
17669
17670 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17671 return UP(node);
17672 }
17673 case PM_TOKEN_UDOT_DOT:
17674 case PM_TOKEN_UDOT_DOT_DOT: {
17675 pm_token_t operator = parser->current;
17676 parser_lex(parser);
17677
17678 // Since we have a unary range operator, we need to parse the subsequent
17679 // expression as the right side of the range.
17680 switch (parser->current.type) {
17681 case PM_CASE_PRIMITIVE: {
17682 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));
17683 return UP(pm_range_node_create(parser, NULL, &operator, right));
17684 }
17685 default: {
17686 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE);
17687 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17688 return UP(pm_range_node_create(parser, NULL, &operator, right));
17689 }
17690 }
17691 }
17692 case PM_CASE_PRIMITIVE: {
17693 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));
17694
17695 // If we found a label, we need to immediately return to the caller.
17696 if (pm_symbol_node_label_p(parser, node)) return node;
17697
17698 // Call nodes (arithmetic operations) are not allowed in patterns
17699 if (PM_NODE_TYPE(node) == PM_CALL_NODE) {
17700 pm_parser_err_node(parser, node, diag_id);
17701 return UP(pm_error_recovery_node_create_unexpected(parser, node));
17702 }
17703
17704 // Now that we have a primitive, we need to check if it's part of a range.
17705 if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
17706 pm_token_t operator = parser->previous;
17707
17708 // Now that we have the operator, we need to check if this is followed
17709 // by another expression. If it is, then we will create a full range
17710 // node. Otherwise, we'll create an endless range.
17711 switch (parser->current.type) {
17712 case PM_CASE_PRIMITIVE: {
17713 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));
17714 return UP(pm_range_node_create(parser, node, &operator, right));
17715 }
17716 default:
17717 return UP(pm_range_node_create(parser, node, &operator, NULL));
17718 }
17719 }
17720
17721 return node;
17722 }
17723 case PM_TOKEN_CARET: {
17724 parser_lex(parser);
17725 pm_token_t operator = parser->previous;
17726
17727 // At this point we have a pin operator. We need to check the subsequent
17728 // expression to determine if it's a variable or an expression.
17729 switch (parser->current.type) {
17730 case PM_TOKEN_IDENTIFIER: {
17731 parser_lex(parser);
17732 pm_node_t *variable = UP(parse_variable(parser));
17733
17734 if (variable == NULL) {
17735 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->previous, PM_ERR_NO_LOCAL_VARIABLE);
17736 variable = UP(pm_local_variable_read_node_missing_create(parser, &parser->previous, 0));
17737 }
17738
17739 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17740 }
17741 case PM_TOKEN_INSTANCE_VARIABLE: {
17742 parser_lex(parser);
17743 pm_node_t *variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
17744
17745 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17746 }
17747 case PM_TOKEN_CLASS_VARIABLE: {
17748 parser_lex(parser);
17749 pm_node_t *variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
17750
17751 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17752 }
17753 case PM_TOKEN_GLOBAL_VARIABLE: {
17754 parser_lex(parser);
17755 pm_node_t *variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
17756
17757 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17758 }
17759 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
17760 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17761 parser->pattern_matching_newlines = false;
17762
17763 pm_token_t lparen = parser->current;
17764 parser_lex(parser);
17765
17766 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));
17767 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17768
17769 accept1(parser, PM_TOKEN_NEWLINE);
17770 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &lparen);
17771 return UP(pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous));
17772 }
17773 default: {
17774 // If we get here, then we have a pin operator followed by something
17775 // not understood. We'll create a missing node and return that.
17776 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN);
17777 pm_node_t *variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17778 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17779 }
17780 }
17781 }
17782 case PM_TOKEN_UCOLON_COLON: {
17783 pm_token_t delimiter = parser->current;
17784 parser_lex(parser);
17785
17786 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
17787 pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous);
17788
17789 return parse_pattern_constant_path(parser, captures, UP(node), (uint16_t) (depth + 1));
17790 }
17791 case PM_TOKEN_CONSTANT: {
17792 pm_token_t constant = parser->current;
17793 parser_lex(parser);
17794
17795 pm_node_t *node = UP(pm_constant_read_node_create(parser, &constant));
17796 return parse_pattern_constant_path(parser, captures, node, (uint16_t) (depth + 1));
17797 }
17798 default:
17799 pm_parser_err_current(parser, diag_id);
17800 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17801 }
17802}
17803
17811static bool
17812parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
17813 pm_parser_t *parser = (pm_parser_t *) data;
17814
17815 switch (PM_NODE_TYPE(node)) {
17816 case PM_LOCAL_VARIABLE_TARGET_NODE:
17817 // Underscore-prefixed names are not captures, see
17818 // parse_pattern_capture.
17819 if (peek_at(parser, parser->start + PM_NODE_START(node)) != '_') {
17820 pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
17821 }
17822 return false;
17823 case PM_ARRAY_PATTERN_NODE:
17824 case PM_ASSOC_NODE:
17825 case PM_ASSOC_SPLAT_NODE:
17826 case PM_CAPTURE_PATTERN_NODE:
17827 case PM_FIND_PATTERN_NODE:
17828 case PM_HASH_PATTERN_NODE:
17829 case PM_IMPLICIT_NODE:
17830 case PM_PARENTHESES_NODE:
17831 case PM_SPLAT_NODE:
17832 return true;
17833 default:
17834 return false;
17835 }
17836}
17837
17843static void
17844parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) {
17845 pm_visit_node(node, parse_pattern_alternation_error_each, parser);
17846}
17847
17852static pm_node_t *
17853parse_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) {
17854 pm_node_t *node = first_node;
17855 bool alternation = false;
17856
17857 while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) {
17858 if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) {
17859 parse_pattern_alternation_error(parser, node);
17860 }
17861
17862 switch (parser->current.type) {
17863 case PM_TOKEN_IDENTIFIER:
17864 case PM_TOKEN_BRACKET_LEFT_ARRAY:
17865 case PM_TOKEN_BRACE_LEFT_HASH:
17866 case PM_TOKEN_CARET:
17867 case PM_TOKEN_CONSTANT:
17868 case PM_TOKEN_UCOLON_COLON:
17869 case PM_TOKEN_UDOT_DOT:
17870 case PM_TOKEN_UDOT_DOT_DOT:
17871 case PM_CASE_PRIMITIVE: {
17872 if (!alternation) {
17873 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17874 } else {
17875 pm_token_t operator = parser->previous;
17876 pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1));
17877
17878 if (captures->size) parse_pattern_alternation_error(parser, right);
17879 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17880 }
17881
17882 break;
17883 }
17884 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
17885 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
17886 pm_token_t operator = parser->previous;
17887 pm_token_t opening = parser->current;
17888 parser_lex(parser);
17889
17890 pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17891 accept1(parser, PM_TOKEN_NEWLINE);
17892 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17893 pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0));
17894
17895 if (!alternation) {
17896 node = right;
17897 } else {
17898 if (captures->size) parse_pattern_alternation_error(parser, right);
17899 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17900 }
17901
17902 break;
17903 }
17904 default: {
17905 pm_parser_err_current(parser, diag_id);
17906 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17907
17908 if (!alternation) {
17909 node = right;
17910 } else {
17911 if (captures->size) parse_pattern_alternation_error(parser, right);
17912 node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous));
17913 }
17914
17915 break;
17916 }
17917 }
17918 }
17919
17920 // If we have an =>, then we are assigning this pattern to a variable.
17921 // In this case we should create an assignment node.
17922 while (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
17923 pm_token_t operator = parser->previous;
17924 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET);
17925
17926 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17927 int depth;
17928
17929 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17930 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17931 }
17932
17933 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17934 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17935 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17936 parser,
17937 &previous_loc,
17938 constant_id,
17939 (uint32_t) (depth == -1 ? 0 : depth)
17940 );
17941
17942 node = UP(pm_capture_pattern_node_create(parser, node, target, &operator));
17943 }
17944
17945 return node;
17946}
17947
17951static pm_node_t *
17952parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
17953 /* Nested patterns recurse back into parse_pattern without passing through
17954 * parse_expression, so this cycle needs its own depth check. */
17955 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
17956 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
17957 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17958 }
17959
17960 pm_node_t *node = NULL;
17961
17962 bool leading_rest = false;
17963 bool trailing_rest = false;
17964
17965 switch (parser->current.type) {
17966 case PM_TOKEN_LABEL: {
17967 parser_lex(parser);
17968 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17969 node = UP(parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)));
17970
17971 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17972 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17973 }
17974
17975 return node;
17976 }
17977 case PM_TOKEN_USTAR_STAR: {
17978 node = parse_pattern_keyword_rest(parser, captures);
17979 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17980
17981 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17982 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17983 }
17984
17985 return node;
17986 }
17987 case PM_TOKEN_STRING_BEGIN: {
17988 // We need special handling for string beginnings because they could
17989 // be dynamic symbols leading to hash patterns.
17990 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17991
17992 if (pm_symbol_node_label_p(parser, node)) {
17993 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17994
17995 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17996 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17997 }
17998
17999 return node;
18000 }
18001
18002 node = parse_pattern_primitives(parser, captures, node, diag_id, (uint16_t) (depth + 1));
18003 break;
18004 }
18005 case PM_TOKEN_USTAR: {
18006 if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) {
18007 parser_lex(parser);
18008 node = UP(parse_pattern_rest(parser, captures));
18009 leading_rest = true;
18010 break;
18011 }
18012 }
18014 default:
18015 node = parse_pattern_primitives(parser, captures, NULL, diag_id, (uint16_t) (depth + 1));
18016 break;
18017 }
18018
18019 // If we got a dynamic label symbol, then we need to treat it like the
18020 // beginning of a hash pattern.
18021 if (pm_symbol_node_label_p(parser, node)) {
18022 return UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
18023 }
18024
18025 if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) {
18026 // If we have a comma, then we are now parsing either an array pattern
18027 // or a find pattern. We need to parse all of the patterns, put them
18028 // into a big list, and then determine which type of node we have.
18029 pm_node_list_t nodes = { 0 };
18030 pm_node_list_append(parser->arena, &nodes, node);
18031
18032 // Gather up all of the patterns into the list.
18033 while (accept1(parser, PM_TOKEN_COMMA)) {
18034 /*
18035 * A trailing comma ends the pattern when the next token cannot
18036 * begin another pattern element, leaving the token for the
18037 * enclosing context to accept or reject.
18038 */
18039 if (!token_begins_pattern_p(parser->current.type)) {
18040 // A trailing comma forms an implicit rest pattern (`[a,]` is
18041 // `[a, *]`). If a rest pattern has already been parsed, then
18042 // this is a second rest, which is not allowed (e.g. `[a, *b,]`
18043 // or `x => a, *b,`).
18044 if (trailing_rest) {
18045 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
18046 }
18047
18048 node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
18049 pm_node_list_append(parser->arena, &nodes, node);
18050 trailing_rest = true;
18051 break;
18052 }
18053
18054 if (accept1(parser, PM_TOKEN_USTAR)) {
18055 node = UP(parse_pattern_rest(parser, captures));
18056
18057 // If we have already parsed a splat pattern, then this is an
18058 // error. We will continue to parse the rest of the patterns,
18059 // but we will indicate it as an error.
18060 if (trailing_rest) {
18061 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
18062 }
18063
18064 trailing_rest = true;
18065 } else {
18066 node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
18067 }
18068
18069 pm_node_list_append(parser->arena, &nodes, node);
18070 }
18071
18072 // If the first pattern and the last pattern are rest patterns, then we
18073 // will call this a find pattern, regardless of how many rest patterns
18074 // are in between because we know we already added the appropriate
18075 // errors. Otherwise we will create an array pattern.
18076 if (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) {
18077 node = UP(pm_find_pattern_node_create(parser, &nodes));
18078
18079 if (nodes.size == 2) {
18080 pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER);
18081 }
18082 } else {
18083 node = UP(pm_array_pattern_node_node_list_create(parser, &nodes));
18084
18085 if (leading_rest && trailing_rest) {
18086 pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS);
18087 }
18088 }
18089
18090 // nodes.nodes is arena-allocated; no explicit free needed.
18091 } else if (leading_rest) {
18092 // Otherwise, if we parsed a single splat pattern, then we know we have
18093 // an array pattern, so we can go ahead and create that node.
18094 node = UP(pm_array_pattern_node_rest_create(parser, node));
18095 }
18096
18097 return node;
18098}
18099
18105static PRISM_INLINE void
18106parse_negative_numeric(pm_node_t *node) {
18107 switch (PM_NODE_TYPE(node)) {
18108 case PM_INTEGER_NODE: {
18109 pm_integer_node_t *cast = (pm_integer_node_t *) node;
18110 cast->base.location.start--;
18111 cast->base.location.length++;
18112 cast->value.negative = true;
18113 break;
18114 }
18115 case PM_FLOAT_NODE: {
18116 pm_float_node_t *cast = (pm_float_node_t *) node;
18117 cast->base.location.start--;
18118 cast->base.location.length++;
18119 cast->value = -cast->value;
18120 break;
18121 }
18122 case PM_RATIONAL_NODE: {
18123 pm_rational_node_t *cast = (pm_rational_node_t *) node;
18124 cast->base.location.start--;
18125 cast->base.location.length++;
18126 cast->numerator.negative = true;
18127 break;
18128 }
18129 case PM_IMAGINARY_NODE:
18130 node->location.start--;
18131 node->location.length++;
18132 parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric);
18133 break;
18134 default:
18135 assert(false && "unreachable");
18136 break;
18137 }
18138}
18139
18145static void
18146pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
18147 switch (diag_id) {
18148 case PM_ERR_HASH_KEY: {
18149 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, pm_token_str(parser->previous.type));
18150 break;
18151 }
18152 case PM_ERR_HASH_VALUE:
18153 case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: {
18154 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
18155 break;
18156 }
18157 case PM_ERR_UNARY_RECEIVER: {
18158 const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_str(parser->current.type));
18159 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, human, parser->previous.start[0]);
18160 break;
18161 }
18162 case PM_ERR_UNARY_DISALLOWED:
18163 case PM_ERR_EXPECT_ARGUMENT: {
18164 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
18165 break;
18166 }
18167 default:
18168 pm_parser_err_previous(parser, diag_id);
18169 break;
18170 }
18171}
18172
18176static void
18177ensure_valid_retry(pm_parser_t *parser, const pm_node_t *node) {
18178#define CONTEXT_NONE 0
18179#define CONTEXT_THROUGH_ENSURE 1
18180#define CONTEXT_THROUGH_ELSE 2
18181
18182 pm_context_node_t *context_node = parser->current_context;
18183 int context = CONTEXT_NONE;
18184
18185 while (context_node != NULL) {
18186 switch (context_node->context) {
18187 case PM_CONTEXT_BEGIN_RESCUE:
18188 case PM_CONTEXT_BLOCK_RESCUE:
18189 case PM_CONTEXT_CLASS_RESCUE:
18190 case PM_CONTEXT_DEF_RESCUE:
18191 case PM_CONTEXT_LAMBDA_RESCUE:
18192 case PM_CONTEXT_MODULE_RESCUE:
18193 case PM_CONTEXT_SCLASS_RESCUE:
18194 case PM_CONTEXT_DEFINED:
18195 case PM_CONTEXT_RESCUE_MODIFIER:
18196 // These are the good cases. We're allowed to have a retry here.
18197 return;
18198 case PM_CONTEXT_CLASS:
18199 case PM_CONTEXT_DEF:
18200 case PM_CONTEXT_DEF_PARAMS:
18201 case PM_CONTEXT_MAIN:
18202 case PM_CONTEXT_MODULE:
18203 case PM_CONTEXT_PREEXE:
18204 case PM_CONTEXT_SCLASS:
18205 // These are the bad cases. We're not allowed to have a retry in
18206 // these contexts.
18207 if (context == CONTEXT_NONE) {
18208 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE);
18209 } else if (context == CONTEXT_THROUGH_ENSURE) {
18210 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE);
18211 } else if (context == CONTEXT_THROUGH_ELSE) {
18212 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE);
18213 }
18214 return;
18215 case PM_CONTEXT_BEGIN_ELSE:
18216 case PM_CONTEXT_BLOCK_ELSE:
18217 case PM_CONTEXT_CLASS_ELSE:
18218 case PM_CONTEXT_DEF_ELSE:
18219 case PM_CONTEXT_LAMBDA_ELSE:
18220 case PM_CONTEXT_MODULE_ELSE:
18221 case PM_CONTEXT_SCLASS_ELSE:
18222 // These are also bad cases, but with a more specific error
18223 // message indicating the else.
18224 context = CONTEXT_THROUGH_ELSE;
18225 break;
18226 case PM_CONTEXT_BEGIN_ENSURE:
18227 case PM_CONTEXT_BLOCK_ENSURE:
18228 case PM_CONTEXT_CLASS_ENSURE:
18229 case PM_CONTEXT_DEF_ENSURE:
18230 case PM_CONTEXT_LAMBDA_ENSURE:
18231 case PM_CONTEXT_MODULE_ENSURE:
18232 case PM_CONTEXT_SCLASS_ENSURE:
18233 // These are also bad cases, but with a more specific error
18234 // message indicating the ensure.
18235 context = CONTEXT_THROUGH_ENSURE;
18236 break;
18237 case PM_CONTEXT_NONE:
18238 case PM_CONTEXT_MAXIMUM:
18239 // This case should never happen.
18240 assert(false && "unreachable");
18241 break;
18242 case PM_CONTEXT_BEGIN:
18243 case PM_CONTEXT_BLOCK_BRACES:
18244 case PM_CONTEXT_BLOCK_KEYWORDS:
18245 case PM_CONTEXT_BLOCK_PARAMETERS:
18246 case PM_CONTEXT_CASE_IN:
18247 case PM_CONTEXT_CASE_WHEN:
18248 case PM_CONTEXT_DEFAULT_PARAMS:
18249 case PM_CONTEXT_ELSE:
18250 case PM_CONTEXT_ELSIF:
18251 case PM_CONTEXT_EMBEXPR:
18252 case PM_CONTEXT_FOR_INDEX:
18253 case PM_CONTEXT_FOR:
18254 case PM_CONTEXT_IF:
18255 case PM_CONTEXT_LAMBDA_BRACES:
18256 case PM_CONTEXT_LAMBDA_DO_END:
18257 case PM_CONTEXT_LOOP_PREDICATE:
18258 case PM_CONTEXT_MULTI_TARGET:
18259 case PM_CONTEXT_PARENS:
18260 case PM_CONTEXT_POSTEXE:
18261 case PM_CONTEXT_PREDICATE:
18262 case PM_CONTEXT_TERNARY:
18263 case PM_CONTEXT_UNLESS:
18264 case PM_CONTEXT_UNTIL:
18265 case PM_CONTEXT_WHILE:
18266 // In these contexts we should continue walking up the list of
18267 // contexts.
18268 break;
18269 }
18270
18271 context_node = context_node->prev;
18272 }
18273
18274#undef CONTEXT_NONE
18275#undef CONTEXT_ENSURE
18276#undef CONTEXT_ELSE
18277}
18278
18282static void
18283ensure_valid_yield(pm_parser_t *parser, const pm_yield_node_t *node) {
18284 pm_context_node_t *context_node = parser->current_context;
18285
18286 while (context_node != NULL) {
18287 switch (context_node->context) {
18288 case PM_CONTEXT_DEF:
18289 case PM_CONTEXT_DEF_PARAMS:
18290 case PM_CONTEXT_DEFINED:
18291 case PM_CONTEXT_DEF_ENSURE:
18292 case PM_CONTEXT_DEF_RESCUE:
18293 case PM_CONTEXT_DEF_ELSE:
18294 // These are the good cases. We're allowed to have a block exit
18295 // in these contexts.
18296 return;
18297 case PM_CONTEXT_CLASS:
18298 case PM_CONTEXT_CLASS_ENSURE:
18299 case PM_CONTEXT_CLASS_RESCUE:
18300 case PM_CONTEXT_CLASS_ELSE:
18301 case PM_CONTEXT_MAIN:
18302 case PM_CONTEXT_MODULE:
18303 case PM_CONTEXT_MODULE_ENSURE:
18304 case PM_CONTEXT_MODULE_RESCUE:
18305 case PM_CONTEXT_MODULE_ELSE:
18306 case PM_CONTEXT_SCLASS:
18307 case PM_CONTEXT_SCLASS_RESCUE:
18308 case PM_CONTEXT_SCLASS_ENSURE:
18309 case PM_CONTEXT_SCLASS_ELSE:
18310 // These are the bad cases. We're not allowed to have a retry in
18311 // these contexts.
18312 pm_parser_err_node(parser, UP(node), PM_ERR_INVALID_YIELD);
18313 return;
18314 case PM_CONTEXT_NONE:
18315 case PM_CONTEXT_MAXIMUM:
18316 // This case should never happen.
18317 assert(false && "unreachable");
18318 break;
18319 case PM_CONTEXT_BEGIN:
18320 case PM_CONTEXT_BEGIN_ELSE:
18321 case PM_CONTEXT_BEGIN_ENSURE:
18322 case PM_CONTEXT_BEGIN_RESCUE:
18323 case PM_CONTEXT_BLOCK_BRACES:
18324 case PM_CONTEXT_BLOCK_KEYWORDS:
18325 case PM_CONTEXT_BLOCK_ELSE:
18326 case PM_CONTEXT_BLOCK_ENSURE:
18327 case PM_CONTEXT_BLOCK_PARAMETERS:
18328 case PM_CONTEXT_BLOCK_RESCUE:
18329 case PM_CONTEXT_CASE_IN:
18330 case PM_CONTEXT_CASE_WHEN:
18331 case PM_CONTEXT_DEFAULT_PARAMS:
18332 case PM_CONTEXT_ELSE:
18333 case PM_CONTEXT_ELSIF:
18334 case PM_CONTEXT_EMBEXPR:
18335 case PM_CONTEXT_FOR_INDEX:
18336 case PM_CONTEXT_FOR:
18337 case PM_CONTEXT_IF:
18338 case PM_CONTEXT_LAMBDA_BRACES:
18339 case PM_CONTEXT_LAMBDA_DO_END:
18340 case PM_CONTEXT_LAMBDA_ELSE:
18341 case PM_CONTEXT_LAMBDA_ENSURE:
18342 case PM_CONTEXT_LAMBDA_RESCUE:
18343 case PM_CONTEXT_LOOP_PREDICATE:
18344 case PM_CONTEXT_MULTI_TARGET:
18345 case PM_CONTEXT_PARENS:
18346 case PM_CONTEXT_POSTEXE:
18347 case PM_CONTEXT_PREDICATE:
18348 case PM_CONTEXT_PREEXE:
18349 case PM_CONTEXT_RESCUE_MODIFIER:
18350 case PM_CONTEXT_TERNARY:
18351 case PM_CONTEXT_UNLESS:
18352 case PM_CONTEXT_UNTIL:
18353 case PM_CONTEXT_WHILE:
18354 // In these contexts we should continue walking up the list of
18355 // contexts.
18356 break;
18357 }
18358
18359 context_node = context_node->prev;
18360 }
18361}
18362
18367static pm_node_t *
18368parse_case(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18369 size_t opening_newline_index = token_newline_index(parser);
18370 parser_lex(parser);
18371
18372 pm_token_t case_keyword = parser->previous;
18373 pm_node_t *predicate = NULL;
18374
18375 pm_node_list_t current_block_exits = { 0 };
18376 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18377
18378 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18379 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18380 predicate = NULL;
18381 } else if (match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_END)) {
18382 predicate = NULL;
18383 } else if (!token_begins_expression_p(parser->current.type)) {
18384 predicate = NULL;
18385 } else {
18386 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));
18387 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18388 }
18389
18390 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
18391 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18392 parser_lex(parser);
18393 pop_block_exits(parser, previous_block_exits);
18394 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18395 return UP(pm_case_node_create(parser, &case_keyword, predicate, &parser->previous));
18396 }
18397
18398 /* At this point we can create a case node, though we don't yet know if it
18399 * is a case-in or case-when node. */
18400 pm_node_t *node;
18401
18402 if (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18403 pm_case_node_t *case_node = pm_case_node_create(parser, &case_keyword, predicate, NULL);
18404 pm_static_literals_t literals = { 0 };
18405
18406 /* At this point we've seen a when keyword, so we know this is a
18407 * case-when node. We will continue to parse the when nodes until we hit
18408 * the end of the list. */
18409 while (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18410 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18411 parser_lex(parser);
18412
18413 pm_token_t when_keyword = parser->previous;
18414 pm_when_node_t *when_node = pm_when_node_create(parser, &when_keyword);
18415
18416 do {
18417 if (accept1(parser, PM_TOKEN_USTAR)) {
18418 pm_token_t operator = parser->previous;
18419 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));
18420
18421 pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression);
18422 pm_when_node_conditions_append(parser->arena, when_node, UP(splat_node));
18423
18424 if (PM_NODE_TYPE_P(expression, PM_ERROR_RECOVERY_NODE)) break;
18425 } else {
18426 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));
18427 pm_when_node_conditions_append(parser->arena, when_node, condition);
18428
18429 /* If we found a missing node, then this is a syntax error
18430 * and we should stop looping. */
18431 if (PM_NODE_TYPE_P(condition, PM_ERROR_RECOVERY_NODE)) break;
18432
18433 /* If this is a string node, then we need to mark it as
18434 * frozen because when clause strings are frozen. */
18435 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
18436 pm_node_flag_set(condition, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
18437 } else if (PM_NODE_TYPE_P(condition, PM_SOURCE_FILE_NODE) && parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
18438 pm_node_flag_set(condition, PM_NODE_FLAG_STATIC_LITERAL);
18439 }
18440
18441 pm_when_clause_static_literals_add(parser, &literals, condition);
18442 }
18443 } while (accept1(parser, PM_TOKEN_COMMA));
18444
18445 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18446 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18447 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18448 }
18449 } else {
18450 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER);
18451 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18452 }
18453
18454 if (!match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18455 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_CASE_WHEN, (uint16_t) (depth + 1));
18456 if (statements != NULL) {
18457 pm_when_node_statements_set(when_node, statements);
18458 }
18459 }
18460
18461 pm_case_node_condition_append(parser->arena, case_node, UP(when_node));
18462 }
18463
18464 /* If we didn't parse any conditions (in or when) then we need to
18465 * indicate that we have an error. */
18466 if (case_node->conditions.size == 0) {
18467 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18468 }
18469
18470 pm_static_literals_free(&literals);
18471 node = UP(case_node);
18472 } else {
18473 pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate);
18474
18475 /* If this is a case-match node (i.e., it is a pattern matching case
18476 * statement) then we must have a predicate. */
18477 if (predicate == NULL) {
18478 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MATCH_MISSING_PREDICATE);
18479 }
18480
18481 /* At this point we expect that we're parsing a case-in node. We will
18482 * continue to parse the in nodes until we hit the end of the list. */
18483 while (match1(parser, PM_TOKEN_KEYWORD_IN)) {
18484 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18485
18486 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
18487 parser->pattern_matching_newlines = true;
18488
18489 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
18490 parser->command_start = false;
18491 parser_lex(parser);
18492
18493 pm_token_t in_keyword = parser->previous;
18494
18495 pm_constant_id_set_t captures = { 0 };
18496 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));
18497
18498 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
18499
18500 /* Since we're in the top-level of the case-in node we need to
18501 * check for guard clauses in the form of `if` or `unless`
18502 * statements. */
18503 if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) {
18504 pm_token_t keyword = parser->previous;
18505 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));
18506 pattern = UP(pm_if_node_modifier_create(parser, pattern, &keyword, predicate));
18507 } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) {
18508 pm_token_t keyword = parser->previous;
18509 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));
18510 pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate));
18511 }
18512
18513 /* Now we need to check for the terminator of the in node's pattern.
18514 * It can be a newline or semicolon optionally followed by a `then`
18515 * keyword. */
18516 pm_token_t then_keyword = { 0 };
18517 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18518 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18519 then_keyword = parser->previous;
18520 }
18521 } else {
18522 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_IN_DELIMITER);
18523 then_keyword = parser->previous;
18524 }
18525
18526 /* Now we can actually parse the statements associated with the in
18527 * node. */
18528 pm_statements_node_t *statements;
18529 if (match3(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18530 statements = NULL;
18531 } else {
18532 statements = parse_statements(parser, PM_CONTEXT_CASE_IN, (uint16_t) (depth + 1));
18533 }
18534
18535 /* Now that we have the full pattern and statements, we can create
18536 * the node and attach it to the case node. */
18537 pm_node_t *condition = UP(pm_in_node_create(parser, pattern, statements, &in_keyword, NTOK2PTR(then_keyword)));
18538 pm_case_match_node_condition_append(parser->arena, case_node, condition);
18539 }
18540
18541 /* If we didn't parse any conditions (in or when) then we need to
18542 * indicate that we have an error. */
18543 if (case_node->conditions.size == 0) {
18544 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18545 }
18546
18547 node = UP(case_node);
18548 }
18549
18550 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18551 if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) {
18552 pm_token_t else_keyword = parser->previous;
18553 pm_else_node_t *else_node;
18554
18555 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
18556 else_node = pm_else_node_create(parser, &else_keyword, parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1)), &parser->current);
18557 } else {
18558 else_node = pm_else_node_create(parser, &else_keyword, NULL, &parser->current);
18559 }
18560
18561 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18562 pm_case_node_else_clause_set((pm_case_node_t *) node, else_node);
18563 } else {
18564 pm_case_match_node_else_clause_set((pm_case_match_node_t *) node, else_node);
18565 }
18566 }
18567
18568 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18569 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword);
18570
18571 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18572 pm_case_node_end_keyword_loc_set(parser, (pm_case_node_t *) node, &parser->previous);
18573 } else {
18574 pm_case_match_node_end_keyword_loc_set(parser, (pm_case_match_node_t *) node, &parser->previous);
18575 }
18576
18577 pop_block_exits(parser, previous_block_exits);
18578 return node;
18579}
18580
18585static pm_node_t *
18586parse_class(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18587 size_t opening_newline_index = token_newline_index(parser);
18588 parser_lex(parser);
18589
18590 pm_token_t class_keyword = parser->previous;
18591 pm_do_loop_stack_push(parser, false);
18592
18593 pm_node_list_t current_block_exits = { 0 };
18594 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18595
18596 if (accept1(parser, PM_TOKEN_LESS_LESS)) {
18597 pm_token_t operator = parser->previous;
18598 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));
18599
18600 pm_parser_scope_push(parser, true);
18601 if (!match2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18602 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_str(parser->current.type));
18603 }
18604
18605 pm_node_t *statements = NULL;
18606 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18607 pm_accepts_block_stack_push(parser, true);
18608 statements = UP(parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1)));
18609 pm_accepts_block_stack_pop(parser);
18610 }
18611
18612 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18613 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18614 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)));
18615 } else {
18616 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18617 }
18618
18619 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18620
18621 pm_constant_id_list_t locals;
18622 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18623
18624 pm_parser_scope_pop(parser);
18625 pm_do_loop_stack_pop(parser);
18626
18627 flush_block_exits(parser, previous_block_exits);
18628 return UP(pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous));
18629 }
18630
18631 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));
18632 pm_token_t name = parser->previous;
18633 if (name.type != PM_TOKEN_CONSTANT) {
18634 pm_parser_err_token(parser, &name, PM_ERR_CLASS_NAME);
18635 }
18636
18637 pm_token_t inheritance_operator = { 0 };
18638 pm_node_t *superclass;
18639
18640 if (match1(parser, PM_TOKEN_LESS)) {
18641 inheritance_operator = parser->current;
18642 lex_state_set(parser, PM_LEX_STATE_BEG);
18643
18644 parser->command_start = true;
18645 parser_lex(parser);
18646
18647 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));
18648 } else {
18649 superclass = NULL;
18650 }
18651
18652 pm_parser_scope_push(parser, true);
18653
18654 if (inheritance_operator.start != NULL) {
18655 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CLASS_UNEXPECTED_END);
18656 } else {
18657 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18658 }
18659 pm_node_t *statements = NULL;
18660
18661 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18662 pm_accepts_block_stack_push(parser, true);
18663 statements = UP(parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1)));
18664 pm_accepts_block_stack_pop(parser);
18665 }
18666
18667 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18668 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18669 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)));
18670 } else {
18671 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18672 }
18673
18674 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18675
18676 if (context_def_p(parser)) {
18677 pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD);
18678 }
18679
18680 pm_constant_id_list_t locals;
18681 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18682
18683 pm_parser_scope_pop(parser);
18684 pm_do_loop_stack_pop(parser);
18685
18686 if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) {
18687 pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME);
18688 if (!PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18689 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18690 }
18691 }
18692
18693 pop_block_exits(parser, previous_block_exits);
18694 return UP(pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, NTOK2PTR(inheritance_operator), superclass, statements, &parser->previous));
18695}
18696
18700static pm_node_t *
18701parse_def(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18702 pm_node_list_t current_block_exits = { 0 };
18703 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18704
18705 pm_token_t def_keyword = parser->current;
18706 size_t opening_newline_index = token_newline_index(parser);
18707
18708 pm_node_t *receiver = NULL;
18709 pm_token_t operator = { 0 };
18710 pm_token_t name;
18711
18712 /* This context is necessary for lexing `...` in a bare params correctly. It
18713 * must be pushed before lexing the first param, so it is here. */
18714 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18715 parser_lex(parser);
18716
18717 /* This will be false if the method name is not a valid identifier but could
18718 * be followed by an operator. */
18719 bool valid_name = true;
18720
18721 switch (parser->current.type) {
18722 case PM_CASE_OPERATOR:
18723 pm_parser_scope_push(parser, true);
18724 lex_state_set(parser, PM_LEX_STATE_ENDFN);
18725 parser_lex(parser);
18726
18727 name = parser->previous;
18728 break;
18729 case PM_TOKEN_IDENTIFIER: {
18730 parser_lex(parser);
18731
18732 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18733 receiver = parse_variable_call(parser);
18734
18735 pm_parser_scope_push(parser, true);
18736 lex_state_set(parser, PM_LEX_STATE_FNAME);
18737 parser_lex(parser);
18738
18739 operator = parser->previous;
18740 name = parse_method_definition_name(parser);
18741 } else {
18742 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
18743 pm_parser_scope_push(parser, true);
18744
18745 name = parser->previous;
18746 }
18747
18748 break;
18749 }
18750 case PM_TOKEN_INSTANCE_VARIABLE:
18751 case PM_TOKEN_CLASS_VARIABLE:
18752 case PM_TOKEN_GLOBAL_VARIABLE:
18753 valid_name = false;
18755 case PM_TOKEN_CONSTANT:
18756 case PM_TOKEN_KEYWORD_NIL:
18757 case PM_TOKEN_KEYWORD_SELF:
18758 case PM_TOKEN_KEYWORD_TRUE:
18759 case PM_TOKEN_KEYWORD_FALSE:
18760 case PM_TOKEN_KEYWORD___FILE__:
18761 case PM_TOKEN_KEYWORD___LINE__:
18762 case PM_TOKEN_KEYWORD___ENCODING__: {
18763 pm_parser_scope_push(parser, true);
18764 parser_lex(parser);
18765
18766 pm_token_t identifier = parser->previous;
18767
18768 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18769 lex_state_set(parser, PM_LEX_STATE_FNAME);
18770 parser_lex(parser);
18771 operator = parser->previous;
18772
18773 switch (identifier.type) {
18774 case PM_TOKEN_CONSTANT:
18775 receiver = UP(pm_constant_read_node_create(parser, &identifier));
18776 break;
18777 case PM_TOKEN_INSTANCE_VARIABLE:
18778 receiver = UP(pm_instance_variable_read_node_create(parser, &identifier));
18779 break;
18780 case PM_TOKEN_CLASS_VARIABLE:
18781 receiver = UP(pm_class_variable_read_node_create(parser, &identifier));
18782 break;
18783 case PM_TOKEN_GLOBAL_VARIABLE:
18784 receiver = UP(pm_global_variable_read_node_create(parser, &identifier));
18785 break;
18786 case PM_TOKEN_KEYWORD_NIL:
18787 receiver = UP(pm_nil_node_create(parser, &identifier));
18788 break;
18789 case PM_TOKEN_KEYWORD_SELF:
18790 receiver = UP(pm_self_node_create(parser, &identifier));
18791 break;
18792 case PM_TOKEN_KEYWORD_TRUE:
18793 receiver = UP(pm_true_node_create(parser, &identifier));
18794 break;
18795 case PM_TOKEN_KEYWORD_FALSE:
18796 receiver = UP(pm_false_node_create(parser, &identifier));
18797 break;
18798 case PM_TOKEN_KEYWORD___FILE__:
18799 receiver = UP(pm_source_file_node_create(parser, &identifier));
18800 break;
18801 case PM_TOKEN_KEYWORD___LINE__:
18802 receiver = UP(pm_source_line_node_create(parser, &identifier));
18803 break;
18804 case PM_TOKEN_KEYWORD___ENCODING__:
18805 receiver = UP(pm_source_encoding_node_create(parser, &identifier));
18806 break;
18807 default:
18808 break;
18809 }
18810
18811 name = parse_method_definition_name(parser);
18812 } else {
18813 if (!valid_name) {
18814 PM_PARSER_ERR_TOKEN_FORMAT(parser, &identifier, PM_ERR_DEF_NAME, pm_token_str(identifier.type));
18815 }
18816
18817 name = identifier;
18818 }
18819 break;
18820 }
18821 case PM_TOKEN_PARENTHESIS_LEFT: {
18822 /* The current context is `PM_CONTEXT_DEF_PARAMS`, however the inner
18823 * expression of this parenthesis should not be processed under this
18824 * context. Thus, the context is popped here. */
18825 context_pop(parser);
18826 parser_lex(parser);
18827
18828 pm_token_t lparen = parser->previous;
18829 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));
18830
18831 accept1(parser, PM_TOKEN_NEWLINE);
18832 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
18833 pm_token_t rparen = parser->previous;
18834
18835 lex_state_set(parser, PM_LEX_STATE_FNAME);
18836 expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM);
18837
18838 operator = parser->previous;
18839 receiver = UP(pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0));
18840
18841 /* To push `PM_CONTEXT_DEF_PARAMS` again is for the same reason as
18842 * described the above. */
18843 pm_parser_scope_push(parser, true);
18844 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18845 name = parse_method_definition_name(parser);
18846 break;
18847 }
18848 default:
18849 pm_parser_scope_push(parser, true);
18850 name = parse_method_definition_name(parser);
18851 break;
18852 }
18853
18854 pm_token_t lparen = { 0 };
18855 pm_token_t rparen = { 0 };
18856 pm_parameters_node_t *params;
18857
18858 bool accept_endless_def = true;
18859 switch (parser->current.type) {
18860 case PM_TOKEN_PARENTHESIS_LEFT: {
18861 parser_lex(parser);
18862 lparen = parser->previous;
18863
18864 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18865 params = NULL;
18866 } else {
18867 /* https://bugs.ruby-lang.org/issues/19107 */
18868 bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1;
18869 params = parse_parameters(
18870 parser,
18871 PM_BINDING_POWER_DEFINED,
18872 true,
18873 allow_trailing_comma,
18874 true,
18875 true,
18876 false,
18877 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18878 (uint16_t) (depth + 1)
18879 );
18880 }
18881
18882 lex_state_set(parser, PM_LEX_STATE_BEG);
18883 parser->command_start = true;
18884
18885 context_pop(parser);
18886 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18887 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_str(parser->current.type));
18888 parser->previous.start = parser->previous.end;
18889 parser->previous.type = 0;
18890 }
18891
18892 rparen = parser->previous;
18893 break;
18894 }
18895 case PM_CASE_PARAMETER: {
18896 /* If we're about to lex a label, we need to add the label state to
18897 * make sure the next newline is ignored. */
18898 if (parser->current.type == PM_TOKEN_LABEL) {
18899 lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL);
18900 }
18901
18902 params = parse_parameters(
18903 parser,
18904 PM_BINDING_POWER_DEFINED,
18905 false,
18906 false,
18907 true,
18908 true,
18909 false,
18910 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18911 (uint16_t) (depth + 1)
18912 );
18913
18914 /* Reject `def * = 1` and similar. We have to specifically check for
18915 * them because they create ambiguity with optional arguments. */
18916 accept_endless_def = false;
18917
18918 context_pop(parser);
18919 break;
18920 }
18921 default: {
18922 params = NULL;
18923 context_pop(parser);
18924 break;
18925 }
18926 }
18927
18928 pm_node_t *statements = NULL;
18929 pm_token_t equal = { 0 };
18930 pm_token_t end_keyword = { 0 };
18931
18932 if (accept1(parser, PM_TOKEN_EQUAL)) {
18933 if (token_is_setter_name(&name)) {
18934 pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER);
18935 }
18936 if (!accept_endless_def) {
18937 pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
18938 }
18939 if (
18940 parser->current_context->context == PM_CONTEXT_DEFAULT_PARAMS &&
18941 parser->current_context->prev->context == PM_CONTEXT_BLOCK_PARAMETERS
18942 ) {
18943 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");
18944 }
18945 equal = parser->previous;
18946
18947 context_push(parser, PM_CONTEXT_DEF);
18948 pm_do_loop_stack_push(parser, false);
18949 statements = UP(pm_statements_node_create(parser));
18950
18951 /* The body accepts a command call (`def f = foo 1`) only where the
18952 * caller accepts an endless method with a command body. */
18953 uint8_t allow_flags = (flags & PM_PARSE_ACCEPTS_COMMAND_DEF) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
18954
18955 /* Inside a def body, we push true onto the accepts_block_stack so that
18956 * `do` is lexed as PM_TOKEN_KEYWORD_DO (which can only start a block
18957 * for primary-level constructs, not commands). During command argument
18958 * parsing, the stack is pushed to false, causing `do` to be lexed as
18959 * PM_TOKEN_KEYWORD_DO_BLOCK, which is not consumed inside the endless
18960 * def body and instead left for the outer context. A method definition
18961 * opens a fresh context all the way through its rescue modifier, so
18962 * this frame spans the rescue modifier value as well: the `do` in
18963 * `baz def f = a rescue z do end` lexes as a plain keyword that
18964 * attaches to `z` rather than to `baz`. */
18965 pm_accepts_block_stack_push(parser, true);
18966 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));
18967
18968 /* If an unconsumed PM_TOKEN_KEYWORD_DO follows the body, it is an error
18969 * (e.g., `def f = 1 do end`). PM_TOKEN_KEYWORD_DO_BLOCK is
18970 * intentionally not caught here — it should bubble up to the outer
18971 * context (e.g., `private def f = puts "Hello" do end` where the block
18972 * attaches to `private`). */
18973 if (accept1(parser, PM_TOKEN_KEYWORD_DO)) {
18974 pm_block_node_t *block = parse_block(parser, (uint16_t) (depth + 1));
18975 pm_parser_err_node(parser, UP(block), PM_ERR_DEF_ENDLESS_DO_BLOCK);
18976 }
18977
18978 /* Any number of rescue modifiers chain onto the body within the method
18979 * definition itself, associating to the left: `def f = a rescue b
18980 * rescue c` defines a method whose body is `(a rescue b) rescue c`,
18981 * rather than a rescue modifier guarding the definition. */
18982 while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
18983 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
18984
18985 pm_token_t rescue_keyword = parser->previous;
18986
18987 /* In the Ruby grammar, the rescue value of an endless method
18988 * command excludes and/or and in/=>. */
18989 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));
18990 context_pop(parser);
18991
18992 statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value));
18993 }
18994
18995 pm_accepts_block_stack_pop(parser);
18996
18997 /* A nested endless def whose body is a command call (e.g.,
18998 * `def f = def g = foo bar`) is a command write and cannot appear
18999 * as a def body. */
19000 if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(parser, statement)) {
19001 PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19002 }
19003
19004 pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false);
19005 pm_do_loop_stack_pop(parser);
19006 context_pop(parser);
19007 } else {
19008 if (lparen.start == NULL) {
19009 lex_state_set(parser, PM_LEX_STATE_BEG);
19010 parser->command_start = true;
19011 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_DEF_PARAMS_TERM);
19012 } else {
19013 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19014 }
19015
19016 pm_accepts_block_stack_push(parser, true);
19017 pm_do_loop_stack_push(parser, false);
19018
19019 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
19020 pm_accepts_block_stack_push(parser, true);
19021 statements = UP(parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1)));
19022 pm_accepts_block_stack_pop(parser);
19023 }
19024
19025 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
19026 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
19027 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)));
19028 } else {
19029 parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false);
19030 }
19031
19032 pm_accepts_block_stack_pop(parser);
19033 pm_do_loop_stack_pop(parser);
19034
19035 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword);
19036 end_keyword = parser->previous;
19037 }
19038
19039 pm_constant_id_list_t locals;
19040 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
19041 pm_parser_scope_pop(parser);
19042
19043 /* If the final character is `@` as is the case when defining methods to
19044 * override the unary operators, we should ignore the @ in the same way we
19045 * do for symbols. */
19046 pm_constant_id_t name_id = pm_parser_constant_id_raw(parser, name.start, parse_operator_symbol_name(&name));
19047
19048 flush_block_exits(parser, previous_block_exits);
19049
19050 return UP(pm_def_node_create(
19051 parser,
19052 name_id,
19053 &name,
19054 receiver,
19055 params,
19056 statements,
19057 &locals,
19058 &def_keyword,
19059 NTOK2PTR(operator),
19060 NTOK2PTR(lparen),
19061 NTOK2PTR(rparen),
19062 NTOK2PTR(equal),
19063 NTOK2PTR(end_keyword)
19064 ));
19065}
19066
19070static pm_node_t *
19071parse_module(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19072 pm_node_list_t current_block_exits = { 0 };
19073 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19074
19075 size_t opening_newline_index = token_newline_index(parser);
19076 parser_lex(parser);
19077 pm_token_t module_keyword = parser->previous;
19078
19079 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));
19080 pm_token_t name;
19081
19082 /* If we can recover from a syntax error that occurred while parsing the
19083 * name of the module, then we'll handle that here. */
19084 if (PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
19085 pop_block_exits(parser, previous_block_exits);
19086
19087 pm_token_t missing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19088 return UP(pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing));
19089 }
19090
19091 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
19092 pm_token_t double_colon = parser->previous;
19093
19094 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
19095 constant_path = UP(pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous));
19096 }
19097
19098 /* Here we retrieve the name of the module. If it wasn't a constant, then
19099 * it's possible that `module foo` was passed, which is a syntax error. We
19100 * handle that here as well. */
19101 name = parser->previous;
19102 if (name.type != PM_TOKEN_CONSTANT) {
19103 pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME);
19104 }
19105
19106 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)) {
19107 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
19108 }
19109
19110 pm_parser_scope_push(parser, true);
19111 accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE);
19112 pm_node_t *statements = NULL;
19113
19114 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
19115 pm_accepts_block_stack_push(parser, true);
19116 statements = UP(parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1)));
19117 pm_accepts_block_stack_pop(parser);
19118 }
19119
19120 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
19121 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
19122 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)));
19123 } else {
19124 parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false);
19125 }
19126
19127 pm_constant_id_list_t locals;
19128 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
19129
19130 pm_parser_scope_pop(parser);
19131 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword);
19132
19133 if (context_def_p(parser)) {
19134 pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
19135 }
19136
19137 pop_block_exits(parser, previous_block_exits);
19138
19139 return UP(pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous));
19140}
19141
19146static pm_node_t *
19147parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
19148 pm_token_t opening = parser->current;
19149 pm_node_flags_t paren_flags = 0;
19150
19151 pm_node_list_t current_block_exits = { 0 };
19152 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19153
19154 parser_lex(parser);
19155 while (true) {
19156 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19157 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19158 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19159 break;
19160 }
19161 }
19162
19163 /* If this is the end of the file or we match a right parenthesis, then we
19164 * have an empty parentheses node, and we can immediately return. */
19165 if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
19166 /* A command argument group sets EXPR_ENDARG before its ')' is
19167 * consumed, even when the group is empty, so that a following '{' is
19168 * scanned as a block brace. */
19169 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT) && opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19170 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19171 }
19172
19173 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19174 pop_block_exits(parser, previous_block_exits);
19175 return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, paren_flags));
19176 }
19177
19178 /* Otherwise, we're going to parse the first statement in the list of
19179 * statements within the parentheses. */
19180 context_push(parser, PM_CONTEXT_PARENS);
19181 pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19182 context_pop(parser);
19183
19184 /* Determine if this statement is followed by a terminator. In the case of a
19185 * single statement, this is fine. But in the case of multiple statements
19186 * it's required. */
19187 bool terminator_found = false;
19188
19189 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19190 terminator_found = true;
19191 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19192 } else if (accept1(parser, PM_TOKEN_NEWLINE)) {
19193 terminator_found = true;
19194 }
19195
19196 if (terminator_found) {
19197 while (true) {
19198 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19199 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19200 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19201 break;
19202 }
19203 }
19204 }
19205
19206 /* If we hit a right parenthesis, then we're done parsing the parentheses
19207 * node, and we can check which kind of node we should return. */
19208 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19209 if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19210 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19211 }
19212
19213 parser_lex(parser);
19214 pop_block_exits(parser, previous_block_exits);
19215
19216 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19217 /* If we have a single statement and are ending on a right
19218 * parenthesis, then we need to check if this is possibly a multiple
19219 * target node. */
19220 pm_multi_target_node_t *multi_target;
19221
19222 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) && ((pm_multi_target_node_t *) statement)->lparen_loc.length == 0) {
19223 multi_target = (pm_multi_target_node_t *) statement;
19224 } else {
19225 multi_target = pm_multi_target_node_create(parser);
19226 pm_multi_target_node_targets_append(parser, multi_target, statement);
19227 }
19228
19229 multi_target->lparen_loc = TOK2LOC(parser, &opening);
19230 multi_target->rparen_loc = TOK2LOC(parser, &parser->previous);
19231 PM_NODE_START_SET_TOKEN(parser, multi_target, &opening);
19232 PM_NODE_LENGTH_SET_TOKEN(parser, multi_target, &parser->previous);
19233
19234 pm_node_t *result;
19235 if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) {
19236 result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19237 accept1(parser, PM_TOKEN_NEWLINE);
19238 } else {
19239 result = UP(multi_target);
19240 }
19241
19242 if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
19243 /* All set, this is explicitly allowed by the parent context. */
19244 } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match2(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_COMMA)) {
19245 /* All set, we're inside a for loop and we're parsing multiple
19246 * targets. A comma continues the index target list, as in
19247 * `for (a, b), c in ...`. */
19248 } else if (flags & PM_PARSE_ACCEPTS_STATEMENT) {
19249 /* The rescue-modifier value parser promotes this target on a
19250 * following `=` or comma. Reject any other binary operator that
19251 * would otherwise consume the target list (e.g. `(a, b) + c`). */
19252 if (pm_binding_powers[parser->current.type].binary && !match1(parser, PM_TOKEN_EQUAL)) {
19253 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19254 }
19255 } else if (binding_power != PM_BINDING_POWER_STATEMENT) {
19256 /* Multi targets are not allowed when it's not a statement
19257 * level. */
19258 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19259 } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
19260 /* Multi targets must be followed by an equal sign in order to
19261 * be valid (or a right parenthesis if they are nested). */
19262 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19263 }
19264
19265 return result;
19266 }
19267
19268 /* If we have a single statement and are ending on a right parenthesis
19269 * and we didn't return a multiple assignment node, then we can return a
19270 * regular parentheses node now. */
19271 pm_statements_node_t *statements = pm_statements_node_create(parser);
19272 pm_statements_node_body_append(parser, statements, statement, true);
19273
19274 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19275 }
19276
19277 /* If we have more than one statement in the set of parentheses, then we are
19278 * going to parse all of them as a list of statements. We'll do that here.
19279 */
19280 context_push(parser, PM_CONTEXT_PARENS);
19281 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19282
19283 pm_statements_node_t *statements = pm_statements_node_create(parser);
19284 pm_statements_node_body_append(parser, statements, statement, true);
19285
19286 /* If we didn't find a terminator and we didn't find a right parenthesis,
19287 * then this is a syntax error. */
19288 if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) {
19289 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19290 }
19291
19292 /* Parse each statement within the parentheses. */
19293 while (true) {
19294 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19295 pm_statements_node_body_append(parser, statements, node, true);
19296
19297 /* If we're recovering from a syntax error, then we need to stop parsing
19298 * the statements now. */
19299 if (parser->recovering) {
19300 /* If this is the level of context where the recovery has happened,
19301 * then we can mark the parser as done recovering. */
19302 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false;
19303 break;
19304 }
19305
19306 /* If we couldn't parse an expression at all, then we need to bail out
19307 * of the loop. */
19308 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) break;
19309
19310 /* If we successfully parsed a statement, then we are going to need a
19311 * terminator to delimit them. */
19312 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
19313 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
19314 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break;
19315 } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19316 break;
19317 } else if (!match1(parser, PM_TOKEN_EOF)) {
19318 /* If we're at the end of the file, then we're going to add an error
19319 * after this for the ) anyway. */
19320 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19321 }
19322 }
19323
19324 context_pop(parser);
19325 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19326
19327 /* When we're parsing multi targets, we allow them to be followed by a right
19328 * parenthesis if they are at the statement level. This is only possible if
19329 * they are the final statement in a parentheses. We need to explicitly
19330 * reject that here. */
19331 {
19332 pm_node_t *statement = statements->body.nodes[statements->body.size - 1];
19333
19334 if (PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19335 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
19336 pm_multi_target_node_targets_append(parser, multi_target, statement);
19337
19338 statement = UP(multi_target);
19339 statements->body.nodes[statements->body.size - 1] = statement;
19340 }
19341
19342 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) {
19343 const uint8_t *offset = parser->start + PM_NODE_END(statement);
19344 pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset };
19345 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(statement), 0));
19346
19347 statement = UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value));
19348 statements->body.nodes[statements->body.size - 1] = statement;
19349
19350 pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED);
19351 }
19352 }
19353
19354 pop_block_exits(parser, previous_block_exits);
19355 pm_void_statements_check(parser, statements, true);
19356 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19357}
19358
19364static pm_node_t *
19365parse_splat(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19366 pm_token_t operator = parser->previous;
19367 pm_node_t *name = NULL;
19368
19369 if (token_begins_expression_p(parser->current.type)) {
19370 name = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
19371 }
19372
19373 return UP(pm_splat_node_create(parser, &operator, name));
19374}
19375
19377parse_array_keyword_hash(pm_parser_t *parser, pm_node_t *key, uint8_t flags, uint16_t depth) {
19378 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
19379
19380 pm_static_literals_t hash_keys = { 0 };
19381 pm_hash_key_static_literals_add(parser, &hash_keys, key);
19382
19383 pm_token_t operator = { 0 };
19384 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
19385 operator = parser->previous;
19386 }
19387
19388 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));
19389 pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, NTOK2PTR(operator), value));
19390 pm_keyword_hash_node_elements_append(parser->arena, hash, assoc);
19391
19392 if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19393 parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1));
19394 }
19395
19396 pm_static_literals_free(&hash_keys);
19397 return hash;
19398}
19399
19400static pm_array_node_t *
19401parse_array(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19402 pm_array_node_t *array = pm_array_node_create(parser, &parser->current);
19403 parser_lex(parser);
19404
19405 bool parsed_bare_hash = false;
19406 while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
19407 bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
19408
19409 // Handle the case where we don't have a comma and we have a
19410 // newline followed by a right bracket.
19411 if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19412 break;
19413 }
19414
19415 // Ensure that we have a comma between elements in the array.
19416 if (array->elements.size > 0) {
19417 if (accept1(parser, PM_TOKEN_COMMA)) {
19418 // If there was a comma but we also accepts a newline,
19419 // then this is a syntax error.
19420 if (accepted_newline) {
19421 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
19422 }
19423 } else {
19424 // If there was no comma, then we need to add a syntax
19425 // error.
19426 PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_END(parser, &parser->previous), 0, PM_ERR_ARRAY_SEPARATOR, pm_token_str(parser->current.type));
19427 parser->previous.start = parser->previous.end;
19428 parser->previous.type = 0;
19429 }
19430 }
19431
19432 // If we have a right bracket immediately following a comma,
19433 // this is allowed since it's a trailing comma. In this case we
19434 // can break out of the loop.
19435 if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break;
19436
19437 pm_node_t *element;
19438
19439 if (accept1(parser, PM_TOKEN_USTAR)) {
19440 pm_token_t operator = parser->previous;
19441 pm_node_t *expression = NULL;
19442
19443 if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) {
19444 pm_parser_scope_forwarding_positionals_check(parser, &operator);
19445 } else {
19446 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));
19447 }
19448
19449 element = UP(pm_splat_node_create(parser, &operator, expression));
19450 } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) {
19451 if (parsed_bare_hash) {
19452 pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH);
19453 }
19454
19455 element = UP(pm_keyword_hash_node_create(parser));
19456 pm_static_literals_t hash_keys = { 0 };
19457
19458 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)) {
19459 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
19460 }
19461
19462 pm_static_literals_free(&hash_keys);
19463 parsed_bare_hash = true;
19464 } else {
19465 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));
19466
19467 if (pm_symbol_node_label_p(parser, element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
19468 if (parsed_bare_hash) {
19469 pm_parser_err_previous(parser, PM_ERR_EXPRESSION_BARE_HASH);
19470 }
19471
19472 element = UP(parse_array_keyword_hash(parser, element, flags, depth));
19473 parsed_bare_hash = true;
19474 }
19475 }
19476
19477 pm_array_node_elements_append(parser->arena, array, element);
19478 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
19479 }
19480
19481 accept1(parser, PM_TOKEN_NEWLINE);
19482
19483 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19484 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
19485 parser->previous.start = parser->previous.end;
19486 parser->previous.type = 0;
19487 }
19488
19489 pm_array_node_close_set(parser, array, &parser->previous);
19490 return array;
19491}
19492
19493static pm_hash_node_t *
19494parse_hash(pm_parser_t *parser, uint16_t depth) {
19495 pm_token_t opening = parser->current;
19496 pm_hash_node_t *node = pm_hash_node_create(parser, &opening);
19497 parser_lex(parser);
19498
19499 if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
19500 pm_static_literals_t hash_keys = { 0 };
19501 parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1));
19502 pm_static_literals_free(&hash_keys);
19503
19504 accept1(parser, PM_TOKEN_NEWLINE);
19505 }
19506
19507 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening);
19508 pm_hash_node_closing_loc_set(parser, node, &parser->previous);
19509 return node;
19510}
19511
19512static pm_node_t *
19513parse_heredoc(pm_parser_t *parser, uint16_t depth) {
19514 assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC);
19515 pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base;
19516
19517 size_t common_whitespace = (size_t) -1;
19518 parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace;
19519
19520 parser_lex(parser);
19521 pm_token_t opening = parser->previous;
19522
19523 pm_node_t *node;
19524 pm_node_t *part;
19525
19526 if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19527 // If we get here, then we have an empty heredoc. We'll create
19528 // an empty content token and return an empty string node.
19529 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19530 pm_token_t content = parse_strings_empty_content(parser->previous.start);
19531
19532 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19533 node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19534 } else {
19535 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19536 }
19537
19538 PM_NODE_LENGTH_SET_TOKEN(parser, node, &opening);
19539 } else if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) == NULL) {
19540 // If we get here, then we tried to find something in the
19541 // heredoc but couldn't actually parse anything, so we'll just
19542 // return a missing node.
19543 //
19544 // parse_string_part handles its own errors, so there is no need
19545 // for us to add one here.
19546 node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
19547 } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19548 // If we get here, then the part that we parsed was plain string
19549 // content and we're at the end of the heredoc, so we can return
19550 // just a string node with the heredoc opening and closing as
19551 // its opening and closing.
19552 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
19553 pm_string_node_t *cast = (pm_string_node_t *) part;
19554
19555 cast->opening_loc = TOK2LOC(parser, &opening);
19556 cast->closing_loc = TOK2LOC(parser, &parser->current);
19557 cast->base.location = cast->opening_loc;
19558
19559 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19560 assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
19561 cast->base.type = PM_X_STRING_NODE;
19562 }
19563
19564 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19565 parse_heredoc_dedent_string(parser->arena, &cast->unescaped, common_whitespace);
19566 }
19567
19568 node = UP(cast);
19569 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19570 } else {
19571 // If we get here, then we have multiple parts in the heredoc,
19572 // so we'll need to create an interpolated string node to hold
19573 // them all.
19574 pm_node_list_t parts = { 0 };
19575 pm_node_list_append(parser->arena, &parts, part);
19576
19577 while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19578 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19579 pm_node_list_append(parser->arena, &parts, part);
19580 }
19581 }
19582
19583 // Now that we have all of the parts, create the correct type of
19584 // interpolated node.
19585 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19586 pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19587 cast->parts = parts;
19588
19589 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19590 pm_interpolated_xstring_node_closing_set(parser, cast, &parser->previous);
19591
19592 cast->base.location = cast->opening_loc;
19593 node = UP(cast);
19594 } else {
19595 pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
19596
19597 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19598 pm_interpolated_string_node_closing_set(parser, cast, &parser->previous);
19599
19600 cast->base.location = cast->opening_loc;
19601 node = UP(cast);
19602 }
19603
19604 // If this is a heredoc that is indented with a ~, then we need
19605 // to dedent each line by the common leading whitespace.
19606 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19607 pm_node_list_t *nodes;
19608 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19609 nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
19610 } else {
19611 nodes = &((pm_interpolated_string_node_t *) node)->parts;
19612 }
19613
19614 parse_heredoc_dedent(parser, nodes, common_whitespace);
19615 }
19616 }
19617
19618 /* If a missing terminator left this heredoc's lex mode on the stack, it
19619 * still points at our stack-local common_whitespace. Clear the pointer so
19620 * that subsequent lexing cannot read from this function's dead stack frame.
19621 */
19622 pm_lex_mode_t *whitespace_mode = parser->lex_modes.current;
19623 do {
19624 if (whitespace_mode->mode == PM_LEX_HEREDOC && whitespace_mode->as.heredoc.common_whitespace == &common_whitespace) {
19625 whitespace_mode->as.heredoc.common_whitespace = NULL;
19626 }
19627 whitespace_mode = whitespace_mode->prev;
19628 } while (whitespace_mode != NULL);
19629
19630 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
19631 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
19632 }
19633
19634 return node;
19635}
19636
19637static pm_begin_node_t *
19638parse_begin(pm_parser_t *parser, uint16_t depth) {
19639 pm_token_t begin_keyword = parser->current;
19640 size_t opening_newline_index = token_newline_index(parser);
19641 parser_lex(parser);
19642
19643 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19644
19645 pm_node_list_t current_block_exits = { 0 };
19646 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19647 pm_statements_node_t *begin_statements = NULL;
19648
19649 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
19650 pm_accepts_block_stack_push(parser, true);
19651 begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN, (uint16_t) (depth + 1));
19652 pm_accepts_block_stack_pop(parser);
19653 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19654 }
19655
19656 pm_begin_node_t *node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
19657 parse_rescues(parser, opening_newline_index, &begin_keyword, node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1));
19658 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword);
19659
19660 PM_NODE_LENGTH_SET_TOKEN(parser, node, &parser->previous);
19661 pm_begin_node_end_keyword_set(parser, node, &parser->previous);
19662 pop_block_exits(parser, previous_block_exits);
19663 return node;
19664}
19665
19666static pm_defined_node_t *
19667parse_defined(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19668 pm_token_t keyword = parser->current;
19669 parser_lex(parser);
19670
19671 pm_token_t lparen = { 0 };
19672 pm_token_t rparen = { 0 };
19673 pm_node_t *expression;
19674
19675 context_push(parser, PM_CONTEXT_DEFINED);
19676 bool newline = accept1(parser, PM_TOKEN_NEWLINE);
19677
19678 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
19679 lparen = parser->previous;
19680
19681 if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19682 expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
19683 lparen = (pm_token_t) { 0 };
19684 } else {
19685 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));
19686
19687 if (!parser->recovering) {
19688 accept1(parser, PM_TOKEN_NEWLINE);
19689 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19690 rparen = parser->previous;
19691 }
19692 }
19693 } else {
19694 expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19695 }
19696
19697 context_pop(parser);
19698 return pm_defined_node_create(parser, NTOK2PTR(lparen), expression, NTOK2PTR(rparen), &keyword);
19699}
19700
19701static pm_for_node_t *
19702parse_for(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19703 pm_token_t for_keyword = parser->current;
19704 size_t opening_newline_index = token_newline_index(parser);
19705 parser_lex(parser);
19706
19707 pm_node_t *index;
19708 context_push(parser, PM_CONTEXT_FOR_INDEX);
19709
19710 // First, parse out the first index expression.
19711 if (accept1(parser, PM_TOKEN_USTAR)) {
19712 index = parse_splat(parser, flags, depth);
19713 } else if (token_begins_expression_p(parser->current.type)) {
19714 index = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
19715 } else {
19716 pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX);
19717 index = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &for_keyword), PM_TOKEN_LENGTH(&for_keyword)));
19718 }
19719
19720 // Now, if there are multiple index expressions, parse them out.
19721 if (match1(parser, PM_TOKEN_COMMA)) {
19722 index = parse_targets(parser, index, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19723 } else {
19724 index = parse_target(parser, index, false, false);
19725 }
19726
19727 context_pop(parser);
19728 pm_do_loop_stack_push(parser, true);
19729
19730 expect1(parser, PM_TOKEN_KEYWORD_IN, PM_ERR_FOR_IN);
19731 pm_token_t in_keyword = parser->previous;
19732
19733 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));
19734 pm_do_loop_stack_pop(parser);
19735
19736 pm_token_t do_keyword = { 0 };
19737 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
19738 do_keyword = parser->previous;
19739 } else {
19740 if (!match2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)) {
19741 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_str(parser->current.type));
19742 }
19743 }
19744
19745 pm_statements_node_t *statements = NULL;
19746 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
19747 statements = parse_statements(parser, PM_CONTEXT_FOR, (uint16_t) (depth + 1));
19748 }
19749
19750 parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false);
19751 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword);
19752
19753 return pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, NTOK2PTR(do_keyword), &parser->previous);
19754}
19755
19756static pm_lambda_node_t *
19757parse_lambda(pm_parser_t *parser, uint16_t depth) {
19758 int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting;
19759 parser->lambda_enclosure_nesting = parser->enclosure_nesting;
19760
19761 pm_token_t operator = parser->current;
19762 size_t opening_newline_index = token_newline_index(parser);
19763 parser_lex(parser);
19764
19765 pm_parser_scope_push(parser, false);
19766 pm_block_parameters_node_t *block_parameters;
19767
19768 switch (parser->current.type) {
19769 case PM_TOKEN_PARENTHESIS_LEFT: {
19770 pm_token_t opening = parser->current;
19771 parser_lex(parser);
19772
19773 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19774 block_parameters = pm_block_parameters_node_create(parser, NULL, &opening);
19775 } else {
19776 block_parameters = parse_block_parameters(parser, false, &opening, true, true, (uint16_t) (depth + 1));
19777 }
19778
19779 accept1(parser, PM_TOKEN_NEWLINE);
19780 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19781
19782 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
19783 break;
19784 }
19785 case PM_CASE_PARAMETER: {
19786 block_parameters = parse_block_parameters(parser, false, NULL, true, false, (uint16_t) (depth + 1));
19787 break;
19788 }
19789 default: {
19790 block_parameters = NULL;
19791 break;
19792 }
19793 }
19794
19795 pm_token_t opening;
19796 pm_node_t *body = NULL;
19797
19798 if (accept1(parser, PM_TOKEN_LAMBDA_BEGIN)) {
19799 opening = parser->previous;
19800
19801 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
19802 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1)));
19803 }
19804
19805 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
19806
19807 /* Restore the enclosing lambda's nesting now that the body has been
19808 * parsed, so that the token following the closing `}` is lexed in the
19809 * enclosing context. During the body the nesting held this lambda's own
19810 * level, which every token inside the braces sits above. */
19811 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
19812 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
19813 } else {
19814 /* A `-> { }` body is delimited by `{`/`}`, whose enclosure frame the
19815 * lexer manages. A `-> do end` body is delimited by keywords, so push
19816 * the frame here and pop it before `end`. The frame isolates the body
19817 * from an enclosing `while`/`until`/`for` condition, so a `do` inside
19818 * it opens a block rather than the loop body. The push must precede
19819 * consuming the `do`, which lexes the first token of the body. */
19820 pm_enclosure_frame_push(parser);
19821 expect1(parser, PM_TOKEN_KEYWORD_DO_LAMBDA, PM_ERR_LAMBDA_OPEN);
19822 opening = parser->previous;
19823
19824 /* The lexer cleared the nesting when it produced the `do`. If it was
19825 * missing entirely, clear it here so that the body is recovered the
19826 * same way it would have been parsed: no token within it sits at the
19827 * beginning of a lambda. */
19828 parser->lambda_enclosure_nesting = -1;
19829
19830 if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
19831 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1)));
19832 }
19833
19834 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
19835 assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE));
19836 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)));
19837 } else {
19838 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
19839 }
19840
19841 pm_enclosure_frame_pop(parser);
19842
19843 /* As with the brace branch above, restore the nesting before consuming
19844 * the closing `end`, which lexes the token that follows it. */
19845 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
19846 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator);
19847 }
19848
19849 pm_constant_id_list_t locals;
19850 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
19851 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &operator, &parser->previous);
19852
19853 pm_parser_scope_pop(parser);
19854 return pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body);
19855}
19856
19857static pm_node_t *
19858parse_regexp(pm_parser_t *parser, uint16_t depth) {
19859 pm_token_t opening = parser->current;
19860 parser_lex(parser);
19861
19862 if (match1(parser, PM_TOKEN_REGEXP_END)) {
19863 // If we get here, then we have an end immediately after a start. In
19864 // that case we'll create an empty content token and return an
19865 // uninterpolated regular expression.
19866 pm_token_t content = (pm_token_t) {
19867 .type = PM_TOKEN_STRING_CONTENT,
19868 .start = parser->previous.end,
19869 .end = parser->previous.end
19870 };
19871
19872 parser_lex(parser);
19873
19874 pm_regular_expression_node_t *node = pm_regular_expression_node_create(parser, &opening, &content, &parser->previous);
19875 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
19876 return UP(node);
19877 }
19878
19880
19881 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
19882 // In this case we've hit string content so we know the regular
19883 // expression at least has something in it. We'll need to check if the
19884 // following token is the end (in which case we can return a plain
19885 // regular expression) or if it's not then it has interpolation.
19886 pm_string_t unescaped = parser->current_string;
19887 pm_token_t content = parser->current;
19888 parser_lex(parser);
19889
19890 // If we hit an end, then we can create a regular expression
19891 // node without interpolation, which can be represented more
19892 // succinctly and more easily compiled.
19893 if (accept1(parser, PM_TOKEN_REGEXP_END)) {
19894 pm_regular_expression_node_t *node = (pm_regular_expression_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
19895
19896 // If we're not immediately followed by a =~, then we
19897 // parse and validate now. If it is followed by a =~,
19898 // then it will get parsed in the =~ handler where
19899 // named captures can also be extracted.
19900 if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) {
19901 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
19902 }
19903
19904 return UP(node);
19905 }
19906
19907 // If we get here, then we have interpolation so we'll need to create
19908 // a regular expression node with interpolation.
19909 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
19910
19911 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
19912 if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
19913 // This is extremely strange, but the first string part of a
19914 // regular expression will always be tagged as binary if we
19915 // are in a US-ASCII file, no matter its contents.
19916 pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING);
19917 }
19918
19919 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
19920 } else {
19921 // If the first part of the body of the regular expression is not a
19922 // string content, then we have interpolation and we need to create an
19923 // interpolated regular expression node.
19924 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
19925 }
19926
19927 // Now that we're here and we have interpolation, we'll parse all of the
19928 // parts into the list.
19929 pm_node_t *part;
19930 while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) {
19931 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19932 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
19933 }
19934 }
19935
19936 pm_token_t closing = parser->current;
19937 if (match1(parser, PM_TOKEN_EOF)) {
19938 pm_parser_err_token(parser, &opening, PM_ERR_REGEXP_TERM);
19939 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19940 } else {
19941 expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM);
19942 }
19943
19944 pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing);
19945 return UP(interpolated);
19946}
19947
19948static pm_node_t *
19949parse_alias(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
19950 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
19951 pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS);
19952 }
19953
19954 pm_token_t keyword = parser->current;
19955 parser_lex(parser);
19956
19957 pm_node_t *new_name = parse_alias_argument(parser, true, (uint16_t) (depth + 1));
19958 pm_node_t *old_name = parse_alias_argument(parser, false, (uint16_t) (depth + 1));
19959
19960 switch (PM_NODE_TYPE(new_name)) {
19961 case PM_BACK_REFERENCE_READ_NODE:
19962 case PM_NUMBERED_REFERENCE_READ_NODE:
19963 case PM_GLOBAL_VARIABLE_READ_NODE: {
19964 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)) {
19965 if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) {
19966 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE);
19967 }
19968 } else if (!PM_NODE_TYPE_P(old_name, PM_ERROR_RECOVERY_NODE)) {
19969 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
19970 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
19971 }
19972
19973 return UP(pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name));
19974 }
19975 case PM_SYMBOL_NODE:
19976 case PM_INTERPOLATED_SYMBOL_NODE: {
19977 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)) {
19978 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
19979 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
19980 }
19981 }
19983 default:
19984 return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name));
19985 }
19986}
19987
19988static pm_until_node_t *
19989parse_until(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19990 pm_token_t keyword = parser->current;
19991 size_t opening_newline_index = token_newline_index(parser);
19992
19993 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
19994 pm_do_loop_stack_push(parser, true);
19995
19996 parser_lex(parser);
19997 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));
19998
19999 pm_do_loop_stack_pop(parser);
20000 context_pop(parser);
20001
20002 pm_token_t do_keyword = { 0 };
20003 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20004 do_keyword = parser->previous;
20005 } else {
20006 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
20007 }
20008
20009 pm_statements_node_t *statements = NULL;
20010 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20011 pm_accepts_block_stack_push(parser, true);
20012 statements = parse_statements(parser, PM_CONTEXT_UNTIL, (uint16_t) (depth + 1));
20013 pm_accepts_block_stack_pop(parser);
20014 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20015 }
20016
20017 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
20018 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword);
20019
20020 return pm_until_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0);
20021}
20022
20023static pm_while_node_t *
20024parse_while(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20025 pm_token_t keyword = parser->current;
20026 size_t opening_newline_index = token_newline_index(parser);
20027
20028 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
20029 pm_do_loop_stack_push(parser, true);
20030
20031 parser_lex(parser);
20032 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));
20033
20034 pm_do_loop_stack_pop(parser);
20035 context_pop(parser);
20036
20037 pm_token_t do_keyword = { 0 };
20038 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20039 do_keyword = parser->previous;
20040 } else {
20041 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
20042 }
20043
20044 pm_statements_node_t *statements = NULL;
20045 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20046 pm_accepts_block_stack_push(parser, true);
20047 statements = parse_statements(parser, PM_CONTEXT_WHILE, (uint16_t) (depth + 1));
20048 pm_accepts_block_stack_pop(parser);
20049 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20050 }
20051
20052 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
20053 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword);
20054
20055 return pm_while_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0);
20056}
20057
20058static pm_node_t *
20059parse_xstring(pm_parser_t *parser, uint16_t depth) {
20060 pm_token_t opening = parser->current;
20061 parser_lex(parser);
20062
20063 // When we get here, we don't know if this string is going to have
20064 // interpolation or not, even though it is allowed. Still, we want to be
20065 // able to return a string node without interpolation if we can since
20066 // it'll be faster.
20067 if (match1(parser, PM_TOKEN_STRING_END)) {
20068 // If we get here, then we have an end immediately after a start. In
20069 // that case we'll create an empty content token and return an
20070 // uninterpolated string.
20071 pm_token_t content = (pm_token_t) {
20072 .type = PM_TOKEN_STRING_CONTENT,
20073 .start = parser->previous.end,
20074 .end = parser->previous.end
20075 };
20076
20077 parser_lex(parser);
20078 return UP(pm_xstring_node_create(parser, &opening, &content, &parser->previous));
20079 }
20080
20082
20083 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20084 // In this case we've hit string content so we know the string
20085 // at least has something in it. We'll need to check if the
20086 // following token is the end (in which case we can return a
20087 // plain string) or if it's not then it has interpolation.
20088 pm_string_t unescaped = parser->current_string;
20089 pm_token_t content = parser->current;
20090 parser_lex(parser);
20091
20092 if (match1(parser, PM_TOKEN_STRING_END)) {
20093 pm_node_t *node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
20094 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
20095 parser_lex(parser);
20096 return node;
20097 }
20098
20099 // If we get here, then we have interpolation so we'll need to
20100 // create a string node with interpolation.
20101 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20102
20103 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
20104 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
20105
20106 pm_interpolated_xstring_node_append(parser->arena, node, part);
20107 } else {
20108 // If the first part of the body of the string is not a string
20109 // content, then we have interpolation and we need to create an
20110 // interpolated string node.
20111 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20112 }
20113
20114 pm_node_t *part;
20115 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20116 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
20117 pm_interpolated_xstring_node_append(parser->arena, node, part);
20118 }
20119 }
20120
20121 pm_token_t closing = parser->current;
20122 if (match1(parser, PM_TOKEN_EOF)) {
20123 pm_parser_err_token(parser, &opening, PM_ERR_XSTRING_TERM);
20124 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20125 } else {
20126 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM);
20127 }
20128 pm_interpolated_xstring_node_closing_set(parser, node, &closing);
20129
20130 return UP(node);
20131}
20132
20133static pm_node_t *
20134parse_multi_target(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_power, uint16_t depth) {
20135 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
20136 /* If we get here, then we have a comma immediately following something
20137 * that could have been a target in a multiple assignment. */
20138 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20139 }
20140 return node;
20141}
20142
20143static pm_node_t *
20144parse_identifier(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20145 pm_token_t identifier = parser->current;
20146 parser_lex(parser);
20147
20148 pm_node_t *node = parse_variable_call(parser);
20149
20150 if (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
20151 // If parse_variable_call returned with a call node, then we
20152 // know the identifier is not in the local table. In that case
20153 // we need to check if there are arguments following the
20154 // identifier.
20155 pm_call_node_t *call = (pm_call_node_t *) node;
20156 pm_arguments_t arguments = { 0 };
20157
20158 if (parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1))) {
20159 // Since we found arguments, we need to turn off the
20160 // variable call bit in the flags.
20161 pm_node_flag_unset(UP(call), PM_CALL_NODE_FLAGS_VARIABLE_CALL);
20162
20163 call->opening_loc = arguments.opening_loc;
20164 call->arguments = arguments.arguments;
20165 call->closing_loc = arguments.closing_loc;
20166 call->block = arguments.block;
20167
20168 const pm_location_t *end = pm_arguments_end(&arguments);
20169 if (end == NULL) {
20170 PM_NODE_LENGTH_SET_LOCATION(call, &call->message_loc);
20171 } else {
20172 PM_NODE_LENGTH_SET_LOCATION(call, end);
20173 }
20174 }
20175 } else {
20176 // Otherwise, we know the identifier is in the local table. This
20177 // can still be a method call if it is followed by arguments or
20178 // a block, so we need to check for that here.
20179 if (
20180 ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
20181 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
20182 match1(parser, PM_TOKEN_BRACE_LEFT)
20183 ) {
20184 pm_arguments_t arguments = { 0 };
20185 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20186 pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments);
20187
20188 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
20189 // If we're about to convert an 'it' implicit local
20190 // variable read into a method call, we need to remove
20191 // it from the list of implicit local variables.
20192 pm_node_unreference(parser, node);
20193 } else {
20194 // Otherwise, we're about to convert a regular local
20195 // variable read into a method call, in which case we
20196 // need to indicate that this was not a read for the
20197 // purposes of warnings.
20198 assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE));
20199
20200 if (pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &identifier), PM_TOKEN_LENGTH(&identifier))) {
20201 pm_node_unreference(parser, node);
20202 } else {
20204 pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
20205 }
20206 }
20207
20208 return UP(fcall);
20209 }
20210 }
20211
20212 return parse_multi_target(parser, node, binding_power, depth);
20213}
20214
20215static pm_array_node_t *
20216parse_uninterpolated_symbol_array(pm_parser_t *parser) {
20217 pm_token_t opening = parser->current;
20218 parser_lex(parser);
20219
20220 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20221 pm_node_t *current = NULL;
20222
20223 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20224 accept1(parser, PM_TOKEN_WORDS_SEP);
20225 if (match1(parser, PM_TOKEN_STRING_END)) break;
20226
20227 // Interpolation is not possible but nested heredocs can still lead to
20228 // consecutive (disjoint) string tokens when the final newline is escaped.
20229 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20230 // Record the string node, moving to interpolation if needed.
20231 if (current == NULL) {
20232 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
20233 parser_lex(parser);
20234 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20235 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20236 parser_lex(parser);
20237 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
20238 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20239 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
20240 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 };
20241 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
20242 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
20243 parser_lex(parser);
20244
20245 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20246 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
20247 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
20248
20249 // current is arena-allocated so no explicit free is needed.
20250 current = UP(interpolated);
20251 } else {
20252 assert(false && "unreachable");
20253 }
20254 }
20255
20256 if (current) {
20257 pm_array_node_elements_append(parser->arena, array, current);
20258 current = NULL;
20259 } else {
20260 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT);
20261 }
20262 }
20263
20264 pm_token_t closing = parser->current;
20265 if (match1(parser, PM_TOKEN_EOF)) {
20266 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_LOWER_TERM);
20267 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20268 } else {
20269 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM);
20270 }
20271 pm_array_node_close_set(parser, array, &closing);
20272
20273 return array;
20274}
20275
20276static pm_array_node_t *
20277parse_interpolated_symbol_array(pm_parser_t *parser, uint16_t depth) {
20278 parser_lex(parser);
20279 pm_token_t opening = parser->previous;
20280 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20281
20282 /* This is the current node that we are parsing that will be added to the
20283 * list of elements. */
20284 pm_node_t *current = NULL;
20285
20286 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20287 switch (parser->current.type) {
20288 case PM_TOKEN_WORDS_SEP: {
20289 /* Reset the explicit encoding if we hit a separator since each
20290 * element can have its own encoding. */
20291 parser->explicit_encoding = NULL;
20292
20293 if (current == NULL) {
20294 /* If we hit a separator before we have any content, then we
20295 * don't need to do anything. */
20296 } else {
20297 /* If we hit a separator after we've hit content, then we
20298 * need to append that content to the list and reset the
20299 * current node. */
20300 pm_array_node_elements_append(parser->arena, array, current);
20301 current = NULL;
20302 }
20303
20304 parser_lex(parser);
20305 break;
20306 }
20307 case PM_TOKEN_STRING_CONTENT: {
20308 if (current == NULL) {
20309 /* If we hit content and the current node is NULL, then this
20310 * is the first string content we've seen. In that case
20311 * we're going to create a new string node and set that to
20312 * the current. */
20313 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
20314 parser_lex(parser);
20315 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20316 /* If we hit string content and the current node is an
20317 * interpolated string, then we need to append the string
20318 * content to the list of child nodes. */
20319 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20320 parser_lex(parser);
20321
20322 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
20323 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20324 /* If we hit string content and the current node is a symbol
20325 * node, then we need to convert the current node into an
20326 * interpolated string and add the string content to the
20327 * list of child nodes. */
20328 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
20329 pm_token_t content = {
20330 .type = PM_TOKEN_STRING_CONTENT,
20331 .start = parser->start + cast->content_loc.start,
20332 .end = parser->start + cast->content_loc.start + cast->content_loc.length
20333 };
20334
20335 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
20336 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
20337 parser_lex(parser);
20338
20339 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20340 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
20341 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
20342
20343 current = UP(interpolated);
20344 } else {
20345 assert(false && "unreachable");
20346 }
20347
20348 break;
20349 }
20350 case PM_TOKEN_EMBVAR: {
20351 bool start_location_set = false;
20352 if (current == NULL) {
20353 /* If we hit an embedded variable and the current node is
20354 * NULL, then this is the start of a new string. We'll set
20355 * the current node to a new interpolated string. */
20356 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
20357 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20358 /* If we hit an embedded variable and the current node is a
20359 * string node, then we'll convert the current into an
20360 * interpolated string and add the string node to the list
20361 * of parts. */
20362 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20363
20364 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
20365 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
20366 PM_NODE_START_SET_NODE(interpolated, current);
20367 start_location_set = true;
20368 current = UP(interpolated);
20369 } else {
20370 /* If we hit an embedded variable and the current node is an
20371 * interpolated string, then we'll just add the embedded
20372 * variable. */
20373 }
20374
20375 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20376 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
20377 if (!start_location_set) {
20378 PM_NODE_START_SET_NODE(current, part);
20379 }
20380 break;
20381 }
20382 case PM_TOKEN_EMBEXPR_BEGIN: {
20383 bool start_location_set = false;
20384 if (current == NULL) {
20385 /* If we hit an embedded expression and the current node is
20386 * NULL, then this is the start of a new string. We'll set
20387 * the current node to a new interpolated string. */
20388 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
20389 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20390 /* If we hit an embedded expression and the current node is
20391 * a string node, then we'll convert the current into an
20392 * interpolated string and add the string node to the list
20393 * of parts. */
20394 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20395
20396 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
20397 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
20398 PM_NODE_START_SET_NODE(interpolated, current);
20399 start_location_set = true;
20400 current = UP(interpolated);
20401 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20402 /* If we hit an embedded expression and the current node is
20403 * an interpolated string, then we'll just continue on. */
20404 } else {
20405 assert(false && "unreachable");
20406 }
20407
20408 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20409 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
20410 if (!start_location_set) {
20411 PM_NODE_START_SET_NODE(current, part);
20412 }
20413 break;
20414 }
20415 default:
20416 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_UPPER_ELEMENT);
20417 parser_lex(parser);
20418 break;
20419 }
20420 }
20421
20422 /* If we have a current node, then we need to append it to the list. */
20423 if (current) {
20424 pm_array_node_elements_append(parser->arena, array, current);
20425 }
20426
20427 pm_token_t closing = parser->current;
20428 if (match1(parser, PM_TOKEN_EOF)) {
20429 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_UPPER_TERM);
20430 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20431 } else {
20432 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM);
20433 }
20434 pm_array_node_close_set(parser, array, &closing);
20435
20436 return array;
20437}
20438
20439static pm_array_node_t *
20440parse_uninterpolated_string_array(pm_parser_t *parser) {
20441 pm_token_t opening = parser->current;
20442 parser_lex(parser);
20443
20444 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20445 pm_node_t *current = NULL;
20446
20447 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20448 accept1(parser, PM_TOKEN_WORDS_SEP);
20449 if (match1(parser, PM_TOKEN_STRING_END)) break;
20450
20451 // Interpolation is not possible but nested heredocs can still lead to
20452 // consecutive (disjoint) string tokens when the final newline is escaped.
20453 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20454 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20455
20456 // Record the string node, moving to interpolation if needed.
20457 if (current == NULL) {
20458 current = string;
20459 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20460 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
20461 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20462 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20463 pm_interpolated_string_node_append(parser, interpolated, current);
20464 pm_interpolated_string_node_append(parser, interpolated, string);
20465 current = UP(interpolated);
20466 } else {
20467 assert(false && "unreachable");
20468 }
20469 parser_lex(parser);
20470 }
20471
20472 if (current) {
20473 pm_array_node_elements_append(parser->arena, array, current);
20474 current = NULL;
20475 } else {
20476 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT);
20477 }
20478 }
20479
20480 pm_token_t closing = parser->current;
20481 if (match1(parser, PM_TOKEN_EOF)) {
20482 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_LOWER_TERM);
20483 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20484 } else {
20485 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM);
20486 }
20487
20488 pm_array_node_close_set(parser, array, &closing);
20489 return array;
20490}
20491
20492static pm_array_node_t *
20493parse_interpolated_string_array(pm_parser_t *parser, uint16_t depth) {
20494 pm_token_t opening = parser->current;
20495 parser_lex(parser);
20496
20497 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20498 pm_node_t *current = NULL;
20499
20500 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20501 switch (parser->current.type) {
20502 case PM_TOKEN_WORDS_SEP: {
20503 /* Reset the explicit encoding if we hit a separator since each
20504 * element can have its own encoding. */
20505 parser->explicit_encoding = NULL;
20506
20507 if (current == NULL) {
20508 /* If we hit a separator before we have any content, then we
20509 * don't need to do anything. */
20510 } else {
20511 /* If we hit a separator after we've hit content, then we
20512 * need to append that content to the list and reset the
20513 * current node. */
20514 pm_array_node_elements_append(parser->arena, array, current);
20515 current = NULL;
20516 }
20517
20518 parser_lex(parser);
20519 break;
20520 }
20521 case PM_TOKEN_STRING_CONTENT: {
20522 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20523 pm_node_flag_set(string, parse_unescaped_encoding(parser, parser->explicit_encoding));
20524 parser_lex(parser);
20525
20526 if (current == NULL) {
20527 /* If we hit content and the current node is NULL, then this
20528 * is the first string content we've seen. In that case
20529 * we're going to create a new string node and set that to
20530 * the current. */
20531 current = string;
20532 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20533 /* If we hit string content and the current node is an
20534 * interpolated string, then we need to append the string
20535 * content to the list of child nodes. */
20536 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
20537 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20538 /* If we hit string content and the current node is a string
20539 * node, then we need to convert the current node into an
20540 * interpolated string and add the string content to the
20541 * list of child nodes. */
20542 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20543 pm_interpolated_string_node_append(parser, interpolated, current);
20544 pm_interpolated_string_node_append(parser, interpolated, string);
20545 current = UP(interpolated);
20546 } else {
20547 assert(false && "unreachable");
20548 }
20549
20550 break;
20551 }
20552 case PM_TOKEN_EMBVAR: {
20553 if (current == NULL) {
20554 /* If we hit an embedded variable and the current node is
20555 * NULL, then this is the start of a new string. We'll set
20556 * the current node to a new interpolated string. */
20557 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
20558 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20559 /* If we hit an embedded variable and the current node is a
20560 * string node, then we'll convert the current into an
20561 * interpolated string and add the string node to the list
20562 * of parts. */
20563 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20564 pm_interpolated_string_node_append(parser, interpolated, current);
20565 current = UP(interpolated);
20566 } else {
20567 /* If we hit an embedded variable and the current node is an
20568 * interpolated string, then we'll just add the embedded
20569 * variable. */
20570 }
20571
20572 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20573 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
20574 break;
20575 }
20576 case PM_TOKEN_EMBEXPR_BEGIN: {
20577 if (current == NULL) {
20578 /* If we hit an embedded expression and the current node is
20579 * NULL, then this is the start of a new string. We'll set
20580 * the current node to a new interpolated string. */
20581 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
20582 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20583 /* If we hit an embedded expression and the current node is
20584 * a string node, then we'll convert the current into an
20585 * interpolated string and add the string node to the list
20586 * of parts. */
20587 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20588 pm_interpolated_string_node_append(parser, interpolated, current);
20589 current = UP(interpolated);
20590 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20591 /* If we hit an embedded expression and the current node is
20592 * an interpolated string, then we'll just continue on. */
20593 } else {
20594 assert(false && "unreachable");
20595 }
20596
20597 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20598 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
20599 break;
20600 }
20601 default:
20602 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_UPPER_ELEMENT);
20603 parser_lex(parser);
20604 break;
20605 }
20606 }
20607
20608 /* If we have a current node, then we need to append it to the list. */
20609 if (current) {
20610 pm_array_node_elements_append(parser->arena, array, current);
20611 }
20612
20613 pm_token_t closing = parser->current;
20614 if (match1(parser, PM_TOKEN_EOF)) {
20615 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_UPPER_TERM);
20616 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20617 } else {
20618 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM);
20619 }
20620
20621 pm_array_node_close_set(parser, array, &closing);
20622 return array;
20623}
20624
20625static pm_node_t *
20626parse_jump(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20627 parser_lex(parser);
20628
20629 pm_token_t keyword = parser->previous;
20630 pm_arguments_t arguments = { 0 };
20631
20632 if (token_begins_expression_p(parser->current.type) || match2(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)) {
20633 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
20634
20635 if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
20636 pm_token_t next = parser->current;
20637 parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
20638
20639 // Reject `foo && return bar`.
20640 if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) {
20641 PM_PARSER_ERR_TOKEN_FORMAT(parser, &next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(next.type));
20642 }
20643
20644 // Reject a trailing comma, e.g. `return a,`. The arguments
20645 // parser silently accepts a trailing comma only when it is
20646 // immediately followed by the EOF terminator; in every other
20647 // case (e.g. `return a,;`) it reports the dangling comma
20648 // itself. We reject the accepted case here to stay in line
20649 // with the command call argument parsing above.
20650 if (parser->previous.type == PM_TOKEN_COMMA && match1(parser, PM_TOKEN_EOF)) {
20651 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
20652 }
20653 }
20654
20655 // It's possible that we've parsed a block argument through our
20656 // call to parse_arguments. If we found one, we should mark it
20657 // as invalid and destroy it, as we don't have a place for it.
20658 if (arguments.block != NULL) {
20659 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20660 pm_node_unreference(parser, arguments.block);
20661 arguments.block = NULL;
20662 }
20663 }
20664
20665 switch (keyword.type) {
20666 case PM_TOKEN_KEYWORD_BREAK: {
20667 pm_node_t *node = UP(pm_break_node_create(parser, &keyword, arguments.arguments));
20668 if (!parser->partial_script) ensure_valid_jump(parser, node);
20669 return node;
20670 }
20671 case PM_TOKEN_KEYWORD_NEXT: {
20672 pm_node_t *node = UP(pm_next_node_create(parser, &keyword, arguments.arguments));
20673 if (!parser->partial_script) ensure_valid_jump(parser, node);
20674 return node;
20675 }
20676 case PM_TOKEN_KEYWORD_RETURN: {
20677 pm_node_t *node = UP(pm_return_node_create(parser, &keyword, arguments.arguments));
20678 ensure_valid_return(parser, node);
20679 return node;
20680 }
20681 default:
20682 assert(false && "unreachable");
20683 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20684 }
20685}
20686
20687static pm_node_t *
20688parse_not(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20689 pm_token_t message = parser->current;
20690 parser_lex(parser);
20691
20692 pm_arguments_t arguments = { 0 };
20693 pm_node_t *receiver = NULL;
20694
20695 /* The `not` keyword without parentheses is only valid in contexts where it
20696 * would be parsed as an expression (i.e., at or below the `not` binding
20697 * power level). In other contexts (e.g., method arguments, array elements,
20698 * assignment right-hand sides), parentheses are required: `not(x)`. An
20699 * exception is made for endless def bodies, where `not` is valid as both
20700 * `arg` and `command` (e.g., `def f = not 1`, `def f = not foo bar`). */
20701 if (binding_power > PM_BINDING_POWER_NOT && !(flags & PM_PARSE_IN_ENDLESS_DEF) && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
20702 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) {
20703 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->previous), 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN);
20704 } else {
20705 accept1(parser, PM_TOKEN_NEWLINE);
20706 pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER);
20707 }
20708
20709 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
20710 }
20711
20712 accept1(parser, PM_TOKEN_NEWLINE);
20713
20714 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
20715 pm_token_t lparen = parser->previous;
20716
20717 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20718 receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
20719 } else {
20720 arguments.opening_loc = TOK2LOC(parser, &lparen);
20721 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));
20722
20723 if (!parser->recovering) {
20724 accept1(parser, PM_TOKEN_NEWLINE);
20725 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20726 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
20727 }
20728 }
20729 } else {
20730 /* The operand of `not` may be a command call, except at the start of
20731 * an endless method body, where it may be one only if the body may
20732 * be: `def f = not foo 1` has a command body, so it is rejected where
20733 * that is not accepted (`if def f = not foo 1; end`). A nested `not`
20734 * is still at the start of the body, so the operand carries the
20735 * endless method context on (`def f = not not foo 1`). */
20736 uint8_t operand_flags = (flags & PM_PARSE_IN_ENDLESS_DEF)
20737 ? (uint8_t) (flags & (PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_IN_ENDLESS_DEF))
20738 : PM_PARSE_ACCEPTS_COMMAND_CALL;
20739 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));
20740 }
20741
20742 return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
20743}
20744
20746parse_preexe(pm_parser_t *parser, pm_binding_power_t binding_power, uint16_t depth) {
20747 pm_node_list_t current_block_exits = { 0 };
20748 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
20749
20750 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20751 pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
20752 }
20753
20754 pm_token_t keyword = parser->current;
20755 parser_lex(parser);
20756
20757 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_BEGIN_UPCASE_BRACE);
20758 pm_token_t opening = parser->previous;
20759 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE, (uint16_t) (depth + 1));
20760
20761 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening);
20762 pm_context_t context = parser->current_context->context;
20763 if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
20764 pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
20765 }
20766
20767 flush_block_exits(parser, previous_block_exits);
20768 return pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
20769}
20770
20772parse_postexe(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_POSTEXE_END);
20775 }
20776
20777 pm_token_t keyword = parser->current;
20778 parser_lex(parser);
20779
20780 if (context_def_p(parser)) {
20781 pm_parser_warn_token(parser, &keyword, PM_WARN_END_IN_METHOD);
20782 }
20783
20784 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_END_UPCASE_BRACE);
20785 pm_token_t opening = parser->previous;
20786 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE, (uint16_t) (depth + 1));
20787
20788 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening);
20789 return pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
20790}
20791
20792static pm_undef_node_t *
20793parse_undef(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20794 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20795 pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF);
20796 }
20797
20798 parser_lex(parser);
20799 pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous);
20800 pm_node_t *name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20801
20802 if (!PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20803 pm_undef_node_append(parser->arena, undef, name);
20804
20805 while (match1(parser, PM_TOKEN_COMMA)) {
20806 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
20807 parser_lex(parser);
20808
20809 name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20810 if (PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20811 break;
20812 }
20813
20814 pm_undef_node_append(parser->arena, undef, name);
20815 }
20816 }
20817
20818 return undef;
20819}
20820
20821static pm_yield_node_t *
20822parse_yield(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20823 pm_token_t keyword = parser->current;
20824 parser_lex(parser);
20825
20826 pm_arguments_t arguments = { 0 };
20827 parse_arguments_list(parser, &arguments, false, flags, (uint16_t) (depth + 1));
20828
20829 /* It's possible that we've parsed a block argument through our call to
20830 * parse_arguments_list. If we found one, we should mark it as invalid and
20831 * destroy it, as we don't have a place for it on the yield node. */
20832 if (arguments.block != NULL) {
20833 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20834 pm_node_unreference(parser, arguments.block);
20835 arguments.block = NULL;
20836 }
20837
20838 pm_yield_node_t *node = pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc);
20839 if (!parser->parsing_eval && !parser->partial_script) ensure_valid_yield(parser, node);
20840
20841 return node;
20842}
20843
20844static pm_node_t *
20845parse_char(pm_parser_t *parser, uint16_t depth) {
20846 pm_node_t *node = UP(pm_string_node_create_current_string(
20847 parser,
20848 &(pm_token_t) {
20849 .type = PM_TOKEN_STRING_BEGIN,
20850 .start = parser->current.start,
20851 .end = parser->current.start + 1
20852 },
20853 &(pm_token_t) {
20854 .type = PM_TOKEN_STRING_CONTENT,
20855 .start = parser->current.start + 1,
20856 .end = parser->current.end
20857 },
20858 NULL
20859 ));
20860
20861 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
20862
20863 /* Skip past the character literal here, since now we have handled
20864 * parser->explicit_encoding correctly. */
20865 parser_lex(parser);
20866
20867 /* Characters can be followed by strings in which case they are
20868 * automatically concatenated. */
20869 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
20870 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
20871 }
20872
20873 return node;
20874}
20875
20876static pm_node_t *
20877parse_constant(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20878 pm_token_t constant = parser->current;
20879 parser_lex(parser);
20880
20881 /* If a constant is immediately followed by parentheses, then this is in
20882 * fact a method call, not a constant read. */
20883 if (
20884 match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
20885 ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type)) ||
20886 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
20887 match1(parser, PM_TOKEN_BRACE_LEFT)
20888 ) {
20889 pm_arguments_t arguments = { 0 };
20890 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20891 return UP(pm_call_node_fcall_create(parser, &constant, &arguments));
20892 }
20893
20894 return parse_multi_target(parser, UP(pm_constant_read_node_create(parser, &parser->previous)), binding_power, depth);
20895}
20896
20897static pm_node_t *
20898parse_constant_path_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint16_t depth) {
20899 pm_token_t delimiter = parser->current;
20900 parser_lex(parser);
20901
20902 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
20903 return parse_multi_target(parser, UP(pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous)), binding_power, depth);
20904}
20905
20906static pm_call_node_t *
20907parse_unary_plus(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20908 if (binding_power > PM_BINDING_POWER_UNARY) {
20909 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20910 }
20911 parser_lex(parser);
20912
20913 pm_token_t operator = parser->previous;
20914 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));
20915 return pm_call_node_unary_create(parser, &operator, receiver, "+@");
20916}
20917
20918static pm_call_node_t *
20919parse_unary_minus(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20920 if (binding_power > PM_BINDING_POWER_UNARY) {
20921 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20922 }
20923 parser_lex(parser);
20924
20925 pm_token_t operator = parser->previous;
20926 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));
20927 return pm_call_node_unary_create(parser, &operator, receiver, "-@");
20928}
20929
20930static pm_node_t *
20931parse_unary_minus_number(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20932 pm_token_t operator = parser->current;
20933 parser_lex(parser);
20934
20935 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));
20936
20937 if (accept1(parser, PM_TOKEN_STAR_STAR)) {
20938 pm_token_t exponent_operator = parser->previous;
20939 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));
20940 node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0));
20941 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20942 } else {
20943 switch (PM_NODE_TYPE(node)) {
20944 case PM_INTEGER_NODE:
20945 case PM_FLOAT_NODE:
20946 case PM_RATIONAL_NODE:
20947 case PM_IMAGINARY_NODE:
20948 parse_negative_numeric(node);
20949 break;
20950 default:
20951 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20952 break;
20953 }
20954 }
20955
20956 return node;
20957}
20958
20959static pm_node_t *
20960parse_unary_star(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
20961 parser_lex(parser);
20962
20963 /* * operators at the beginning of expressions are only valid in the context
20964 * of a multiple assignment. We enforce that here. We'll still lex past it
20965 * though and create an error recovery node. */
20966 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20967 pm_parser_err_prefix(parser, diag_id);
20968 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20969 }
20970
20971 pm_node_t *splat = parse_splat(parser, flags, depth);
20972
20973 if (match1(parser, PM_TOKEN_COMMA)) {
20974 return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20975 } else {
20976 return parse_target_validate(parser, splat, true);
20977 }
20978}
20979
20980static pm_node_t *
20981parse_super(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20982 pm_token_t keyword = parser->current;
20983 parser_lex(parser);
20984
20985 pm_arguments_t arguments = { 0 };
20986 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20987
20988 if (
20989 arguments.opening_loc.length == 0 &&
20990 arguments.arguments == NULL &&
20991 ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE))
20992 ) {
20993 return UP(pm_forwarding_super_node_create(parser, &keyword, &arguments));
20994 }
20995
20996 return UP(pm_super_node_create(parser, &keyword, &arguments));
20997}
20998
20999static pm_range_node_t *
21000parse_range(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
21001 pm_token_t operator = parser->current;
21002 parser_lex(parser);
21003
21004 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));
21005
21006 /* Unary .. and ... are special because these are non-associative operators
21007 * that can also be unary operators. In this case we need to explicitly
21008 * reject code that has a .. or ... that follows this expression. */
21009 if (match2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
21010 pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
21011 }
21012
21013 return pm_range_node_create(parser, NULL, &operator, right);
21014}
21015
21016static pm_call_node_t *
21017parse_bang(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
21018 if (binding_power > PM_BINDING_POWER_UNARY) {
21019 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
21020 }
21021
21022 parser_lex(parser);
21023
21024 pm_token_t operator = parser->previous;
21025 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));
21026 pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
21027 return pm_call_node_unary_create(parser, &operator, receiver, "!");
21028}
21029
21030static pm_call_node_t *
21031parse_tilde(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
21032 if (binding_power > PM_BINDING_POWER_UNARY) {
21033 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
21034 }
21035
21036 parser_lex(parser);
21037
21038 pm_token_t operator = parser->previous;
21039 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));
21040 return pm_call_node_unary_create(parser, &operator, receiver, "~");
21041}
21042
21046static PRISM_INLINE pm_node_t *
21047parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
21048 switch (parser->current.type) {
21049 case PM_TOKEN_BRACKET_LEFT_ARRAY:
21050 return UP(parse_array(parser, flags, depth));
21051 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
21052 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
21053 return parse_parentheses(parser, binding_power, flags, depth);
21054 case PM_TOKEN_BRACE_LEFT_HASH:
21055 return UP(parse_hash(parser, depth));
21056 case PM_TOKEN_CHARACTER_LITERAL:
21057 return parse_char(parser, depth);
21058 case PM_TOKEN_CONSTANT:
21059 return parse_constant(parser, binding_power, flags, depth);
21060 case PM_TOKEN_UCOLON_COLON:
21061 return parse_constant_path_prefix(parser, binding_power, depth);
21062 case PM_TOKEN_UDOT_DOT:
21063 case PM_TOKEN_UDOT_DOT_DOT:
21064 return UP(parse_range(parser, flags, depth));
21065 case PM_TOKEN_FLOAT:
21066 parser_lex(parser);
21067 return UP(pm_float_node_create(parser, &parser->previous));
21068 case PM_TOKEN_FLOAT_IMAGINARY:
21069 parser_lex(parser);
21070 return UP(pm_float_node_imaginary_create(parser, &parser->previous));
21071 case PM_TOKEN_FLOAT_RATIONAL:
21072 parser_lex(parser);
21073 return UP(pm_float_node_rational_create(parser, &parser->previous));
21074 case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY:
21075 parser_lex(parser);
21076 return UP(pm_float_node_rational_imaginary_create(parser, &parser->previous));
21077 case PM_TOKEN_IDENTIFIER:
21078 case PM_TOKEN_METHOD_NAME:
21079 return parse_identifier(parser, binding_power, flags, depth);
21080 case PM_TOKEN_HEREDOC_START:
21081 return parse_heredoc(parser, depth);
21082 case PM_TOKEN_CLASS_VARIABLE:
21083 parser_lex(parser);
21084 return parse_multi_target(parser, UP(pm_class_variable_read_node_create(parser, &parser->previous)), binding_power, depth);
21085 case PM_TOKEN_NUMBERED_REFERENCE:
21086 parser_lex(parser);
21087 return parse_multi_target(parser, UP(pm_numbered_reference_read_node_create(parser, &parser->previous)), binding_power, depth);
21088 case PM_TOKEN_GLOBAL_VARIABLE:
21089 parser_lex(parser);
21090 return parse_multi_target(parser, UP(pm_global_variable_read_node_create(parser, &parser->previous)), binding_power, depth);
21091 case PM_TOKEN_BACK_REFERENCE:
21092 parser_lex(parser);
21093 return parse_multi_target(parser, UP(pm_back_reference_read_node_create(parser, &parser->previous)), binding_power, depth);
21094 case PM_TOKEN_INSTANCE_VARIABLE:
21095 parser_lex(parser);
21096 return parse_multi_target(parser, UP(pm_instance_variable_read_node_create(parser, &parser->previous)), binding_power, depth);
21097 case PM_TOKEN_INTEGER: {
21098 pm_node_flags_t base = parser->integer.base;
21099 parser_lex(parser);
21100 return UP(pm_integer_node_create(parser, base, &parser->previous));
21101 }
21102 case PM_TOKEN_INTEGER_IMAGINARY: {
21103 pm_node_flags_t base = parser->integer.base;
21104 parser_lex(parser);
21105 return UP(pm_integer_node_imaginary_create(parser, base, &parser->previous));
21106 }
21107 case PM_TOKEN_INTEGER_RATIONAL: {
21108 pm_node_flags_t base = parser->integer.base;
21109 parser_lex(parser);
21110 return UP(pm_integer_node_rational_create(parser, base, &parser->previous));
21111 }
21112 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: {
21113 pm_node_flags_t base = parser->integer.base;
21114 parser_lex(parser);
21115 return UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous));
21116 }
21117 case PM_TOKEN_KEYWORD___ENCODING__:
21118 parser_lex(parser);
21119 return UP(pm_source_encoding_node_create(parser, &parser->previous));
21120 case PM_TOKEN_KEYWORD___FILE__:
21121 parser_lex(parser);
21122 return UP(pm_source_file_node_create(parser, &parser->previous));
21123 case PM_TOKEN_KEYWORD___LINE__:
21124 parser_lex(parser);
21125 return UP(pm_source_line_node_create(parser, &parser->previous));
21126 case PM_TOKEN_KEYWORD_ALIAS:
21127 return parse_alias(parser, binding_power, flags, depth);
21128 case PM_TOKEN_KEYWORD_CASE:
21129 return parse_case(parser, flags, depth);
21130 case PM_TOKEN_KEYWORD_BEGIN:
21131 return UP(parse_begin(parser, depth));
21132 case PM_TOKEN_KEYWORD_BEGIN_UPCASE:
21133 return UP(parse_preexe(parser, binding_power, depth));
21134 case PM_TOKEN_KEYWORD_BREAK:
21135 case PM_TOKEN_KEYWORD_NEXT:
21136 case PM_TOKEN_KEYWORD_RETURN:
21137 return parse_jump(parser, flags, depth);
21138 case PM_TOKEN_KEYWORD_SUPER:
21139 return parse_super(parser, flags, depth);
21140 case PM_TOKEN_KEYWORD_YIELD:
21141 return UP(parse_yield(parser, flags, depth));
21142 case PM_TOKEN_KEYWORD_CLASS:
21143 return parse_class(parser, flags, depth);
21144 case PM_TOKEN_KEYWORD_DEF:
21145 return parse_def(parser, flags, depth);
21146 case PM_TOKEN_KEYWORD_DEFINED:
21147 return UP(parse_defined(parser, flags, depth));
21148 case PM_TOKEN_KEYWORD_END_UPCASE:
21149 return UP(parse_postexe(parser, binding_power, flags, depth));
21150 case PM_TOKEN_KEYWORD_FALSE:
21151 parser_lex(parser);
21152 return UP(pm_false_node_create(parser, &parser->previous));
21153 case PM_TOKEN_KEYWORD_FOR:
21154 return UP(parse_for(parser, flags, depth));
21155 case PM_TOKEN_KEYWORD_IF:
21156 if (parser_end_of_line_p(parser)) {
21157 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
21158 }
21159
21160 size_t opening_newline_index = token_newline_index(parser);
21161 bool if_after_else = parser->previous.type == PM_TOKEN_KEYWORD_ELSE;
21162 parser_lex(parser);
21163
21164 return parse_conditional(parser, PM_CONTEXT_IF, opening_newline_index, if_after_else, (uint16_t) (depth + 1));
21165 case PM_TOKEN_KEYWORD_UNDEF:
21166 return UP(parse_undef(parser, binding_power, flags, depth));
21167 case PM_TOKEN_KEYWORD_NOT:
21168 return parse_not(parser, binding_power, flags, depth);
21169 case PM_TOKEN_KEYWORD_UNLESS: {
21170 size_t opening_newline_index = token_newline_index(parser);
21171 parser_lex(parser);
21172
21173 return parse_conditional(parser, PM_CONTEXT_UNLESS, opening_newline_index, false, (uint16_t) (depth + 1));
21174 }
21175 case PM_TOKEN_KEYWORD_MODULE:
21176 return parse_module(parser, flags, depth);
21177 case PM_TOKEN_KEYWORD_NIL:
21178 parser_lex(parser);
21179 return UP(pm_nil_node_create(parser, &parser->previous));
21180 case PM_TOKEN_KEYWORD_REDO: {
21181 parser_lex(parser);
21182
21183 pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous));
21184 if (!parser->partial_script) ensure_valid_jump(parser, node);
21185
21186 return node;
21187 }
21188 case PM_TOKEN_KEYWORD_RETRY: {
21189 parser_lex(parser);
21190
21191 pm_node_t *node = UP(pm_retry_node_create(parser, &parser->previous));
21192 ensure_valid_retry(parser, node);
21193
21194 return node;
21195 }
21196 case PM_TOKEN_KEYWORD_SELF:
21197 parser_lex(parser);
21198 return UP(pm_self_node_create(parser, &parser->previous));
21199 case PM_TOKEN_KEYWORD_TRUE:
21200 parser_lex(parser);
21201 return UP(pm_true_node_create(parser, &parser->previous));
21202 case PM_TOKEN_KEYWORD_UNTIL:
21203 return UP(parse_until(parser, flags, depth));
21204 case PM_TOKEN_KEYWORD_WHILE:
21205 return UP(parse_while(parser, flags, depth));
21206 case PM_TOKEN_PERCENT_LOWER_I:
21207 return UP(parse_uninterpolated_symbol_array(parser));
21208 case PM_TOKEN_PERCENT_UPPER_I:
21209 return UP(parse_interpolated_symbol_array(parser, depth));
21210 case PM_TOKEN_PERCENT_LOWER_W:
21211 return UP(parse_uninterpolated_string_array(parser));
21212 case PM_TOKEN_PERCENT_UPPER_W:
21213 return UP(parse_interpolated_string_array(parser, depth));
21214 case PM_TOKEN_REGEXP_BEGIN:
21215 return parse_regexp(parser, depth);
21216 case PM_TOKEN_XSTRING_BEGIN:
21217 case PM_TOKEN_PERCENT_LOWER_X:
21218 return parse_xstring(parser, depth);
21219 case PM_TOKEN_USTAR:
21220 return parse_unary_star(parser, binding_power, flags, diag_id, depth);
21221 case PM_TOKEN_BANG:
21222 return UP(parse_bang(parser, binding_power, flags, depth));
21223 case PM_TOKEN_TILDE:
21224 return UP(parse_tilde(parser, binding_power, flags, depth));
21225 case PM_TOKEN_UMINUS:
21226 return UP(parse_unary_minus(parser, binding_power, flags, depth));
21227 case PM_TOKEN_UMINUS_NUM:
21228 return UP(parse_unary_minus_number(parser, flags, depth));
21229 case PM_TOKEN_MINUS_GREATER:
21230 return UP(parse_lambda(parser, depth));
21231 case PM_TOKEN_UPLUS:
21232 return UP(parse_unary_plus(parser, binding_power, flags, depth));
21233 case PM_TOKEN_STRING_BEGIN:
21234 return parse_strings(parser, NULL, flags & PM_PARSE_ACCEPTS_LABEL, (uint16_t) (depth + 1));
21235 case PM_TOKEN_SYMBOL_BEGIN:
21236 return parse_symbol(parser, PM_LEX_STATE_END, (uint16_t) (depth + 1));
21237 default: {
21238 pm_context_t recoverable = context_recoverable(parser, &parser->current);
21239
21240 if (recoverable != PM_CONTEXT_NONE) {
21241 parser->recovering = true;
21242
21243 // If the given error is not the generic one, then we'll add it
21244 // here because it will provide more context in addition to the
21245 // recoverable error that we will also add.
21246 if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) {
21247 pm_parser_err_prefix(parser, diag_id);
21248 }
21249
21250 // If we get here, then we are assuming this token is closing a
21251 // parent context, so we'll indicate that to the user so that
21252 // they know how we behaved.
21253 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_str(parser->current.type), context_human(recoverable));
21254 } else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) {
21255 // We're going to make a special case here, because "cannot
21256 // parse expression" is pretty generic, and we know here that we
21257 // have an unexpected token.
21258 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_str(parser->current.type));
21259 } else {
21260 pm_parser_err_prefix(parser, diag_id);
21261 }
21262
21263 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
21264 }
21265 }
21266}
21267
21268static pm_node_t *
21269parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth);
21270
21278static void
21279parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
21280 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21281 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21282 parser_lex(parser);
21283 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));
21284 }
21285}
21286
21292static void
21293parse_write_value_comma_check(pm_parser_t *parser, const pm_node_t *value) {
21294 if (match1(parser, PM_TOKEN_COMMA) && pm_command_before_comma_p(parser, value)) {
21295 pm_parser_err_current(parser, PM_ERR_INVALID_COMMA);
21296 }
21297}
21298
21308static pm_node_t *
21309parse_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) {
21310 /* Where an operator write accepts a command call it also accepts an
21311 * endless method with a command body (`x += def f = foo 1`). */
21312 uint8_t command_flags = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
21313 ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21314 : (previous_binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21315 if (command_flags) command_flags |= PM_PARSE_ACCEPTS_COMMAND_DEF;
21316
21317 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));
21318
21319 /* An operator write never takes a multi-value right-hand side, so a comma
21320 * after a statement-level one is already reported by the statement that
21321 * contains it. */
21322 if (previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
21323 parse_write_value_comma_check(parser, value);
21324 }
21325
21326 // Assignments whose value is a command call (e.g., a = b c) can only
21327 // be followed by modifiers (if/unless/while/until/rescue) and not by
21328 // operators with higher binding power. If we find one, emit an error
21329 // and skip the operator and its right-hand side.
21330 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER && (pm_command_call_value_p(parser, value) || pm_block_call_p(value))) {
21331 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21332 parser_lex(parser);
21333 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));
21334 }
21335
21336 // Contradicting binding powers, the right-hand-side value of the assignment
21337 // allows the `rescue` modifier.
21338 if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21339 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21340
21341 pm_token_t rescue = parser->current;
21342 parser_lex(parser);
21343
21344 // As in parse_assignment_values, the resbody is a `stmt` (permitting a
21345 // multiple assignment / command call) when the rescued value is itself a
21346 // command call, and a plain `arg` otherwise.
21347 bool statement_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21348 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21349
21350 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21351 context_pop(parser);
21352
21353 // A pattern-match resbody is a statement, but here the rescue is nested
21354 // in an assignment value where parse_expression_terminator cannot see
21355 // it, so reject a trailing operator above the modifier level directly.
21356 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21357 parse_rescue_modifier_terminator(parser, flags, depth);
21358 }
21359
21360 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21361 }
21362
21363 return value;
21364}
21365
21370static void
21371parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) {
21372 switch (PM_NODE_TYPE(node)) {
21373 case PM_BEGIN_NODE: {
21374 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
21375 if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements);
21376 break;
21377 }
21378 case PM_LOCAL_VARIABLE_WRITE_NODE: {
21380 pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
21381 break;
21382 }
21383 case PM_PARENTHESES_NODE: {
21384 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
21385 if (cast->body != NULL) parse_assignment_value_local(parser, cast->body);
21386 break;
21387 }
21388 case PM_STATEMENTS_NODE: {
21389 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
21390 const pm_node_t *statement;
21391
21392 PM_NODE_LIST_FOREACH(&cast->body, index, statement) {
21393 parse_assignment_value_local(parser, statement);
21394 }
21395 break;
21396 }
21397 default:
21398 break;
21399 }
21400}
21401
21414static pm_node_t *
21415parse_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) {
21416 bool statement_level = (previous_binding_power == PM_BINDING_POWER_STATEMENT) || (flags & PM_PARSE_ACCEPTS_STATEMENT);
21417
21418 bool permitted = true;
21419 if (!statement_level && match1(parser, PM_TOKEN_USTAR)) permitted = false;
21420
21421 // A command call (e.g. `x = y z`) is permitted as the value when assigning
21422 // directly (carrying the caller's flag), or in any statement-level context
21423 // — which includes a rescue modifier value via the flag.
21424 uint8_t command_call_flag = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
21425 ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21426 : ((previous_binding_power < PM_BINDING_POWER_MODIFIER || statement_level) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21427
21428 /* Where a single-target write accepts a command call it also accepts an
21429 * endless method with a command body (`x = def f = foo 1`), but a
21430 * multi-write accepts only the command call. */
21431 if (command_call_flag && binding_power != PM_BINDING_POWER_MULTI_ASSIGNMENT + 1) {
21432 command_call_flag |= PM_PARSE_ACCEPTS_COMMAND_DEF;
21433 }
21434
21435 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));
21436 if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
21437
21438 parse_assignment_value_local(parser, value);
21439 bool single_value = true;
21440
21441 parse_write_value_comma_check(parser, value);
21442
21443 // Block calls (command call + do block, e.g., `foo bar do end`) cannot
21444 // be followed by a comma to form a multi-value RHS because each element
21445 // of a multi-value assignment must be an `arg`, not a `block_call`.
21446 if (statement_level && !pm_block_call_p(value) && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) {
21447 single_value = false;
21448
21449 pm_array_node_t *array = pm_array_node_create(parser, NULL);
21450 pm_array_node_elements_append(parser->arena, array, value);
21451 value = UP(array);
21452
21453 while (accept1(parser, PM_TOKEN_COMMA)) {
21454 pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1));
21455
21456 pm_array_node_elements_append(parser->arena, array, element);
21457 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
21458
21459 parse_assignment_value_local(parser, element);
21460 }
21461 }
21462
21463 // Assignments whose value is a command call (e.g., a = b c) can only
21464 // be followed by modifiers (if/unless/while/until/rescue) and not by
21465 // operators with higher binding power. If we find one, emit an error
21466 // and skip the operator and its right-hand side.
21467 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))) {
21468 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21469 parser_lex(parser);
21470 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));
21471 }
21472
21473 // Contradicting binding powers, the right-hand-side value of the assignment
21474 // allows the `rescue` modifier.
21475 bool multiple_assignment = (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1));
21476 if ((single_value || multiple_assignment) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21477 bool command_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21478
21479 // A multiple assignment whose value is a command call (`x, y = foo
21480 // bar`) is a complete statement (parse.y: `mlhs '='
21481 // command_call_value`, which has no rescue), so a trailing `rescue`
21482 // modifies the whole assignment rather than the value. Leave it for the
21483 // statement-level rescue instead of binding it to the value here. For a
21484 // non-command value the rescue does bind to the value (parse.y:
21485 // `mlhs '=' mrhs_arg modifier_rescue stmt`).
21486 if (multiple_assignment && command_value) return value;
21487
21488 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21489
21490 pm_token_t rescue = parser->current;
21491 parser_lex(parser);
21492
21493 // The resbody is a `stmt` (parse.y: `command_rhs`/`mlhs '=' mrhs_arg`),
21494 // which permits a multiple assignment and a command call, when this is a
21495 // multiple assignment or the rescued value is itself a command call.
21496 // Otherwise it is a plain `arg` (parse.y: `arg_rhs`).
21497 bool statement_value = multiple_assignment || command_value;
21498 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21499
21500 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21501 context_pop(parser);
21502
21503 // A pattern-match resbody is a statement, but here the rescue is nested
21504 // in an assignment value where parse_expression_terminator cannot see
21505 // it, so reject a trailing operator above the modifier level directly.
21506 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21507 parse_rescue_modifier_terminator(parser, flags, depth);
21508 }
21509
21510 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21511 }
21512
21513 return value;
21514}
21515
21528static pm_node_t *
21529parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth) {
21530 if (statement) {
21531 pm_node_t *value;
21532 bool multiple;
21533
21534 if (match1(parser, PM_TOKEN_USTAR)) {
21535 // A leading splat can only begin a multiple assignment target list.
21536 parser_lex(parser);
21537 value = parse_splat(parser, flags, depth);
21538 multiple = true;
21539 } else {
21540 // The flag lets a single-target assignment take a multiple-value or
21541 // splat right-hand side (`b = c, d` / `b = *c`); a comma _before_ an
21542 // `=`, or a parenthesized target list (`(b, c), d = 1`), instead
21543 // promotes to a multiple assignment target list below.
21544 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));
21545 multiple = match1(parser, PM_TOKEN_COMMA) || PM_NODE_TYPE_P(value, PM_MULTI_TARGET_NODE);
21546 }
21547
21548 if (multiple) {
21549 pm_node_t *target = parse_targets_validate(parser, value, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21550
21551 // A promoted target list is only a valid rescue value as part of a
21552 // complete `targets = values`. parse_targets_validate already
21553 // reports a missing `=` for every terminator except `)` (which it
21554 // permits for an enclosing mlhs paren that does not apply here), so
21555 // reject that case.
21556 if (!match1(parser, PM_TOKEN_EQUAL)) {
21557 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
21558 return target;
21559 }
21560
21561 pm_token_t operator = parser->current;
21562 parser_lex(parser);
21563
21564 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));
21565 value = parse_write(parser, target, &operator, values);
21566 }
21567
21568 // Reject a trailing operator that cannot follow a statement resbody.
21569 // Pattern-match handlers are statements too, but for the bare statement
21570 // form they are reported by parse_expression_terminator instead (which
21571 // keeps its existing error-recovery), so they are excluded here.
21572 if (!PM_NODE_TYPE_P(value, PM_MATCH_REQUIRED_NODE) && !PM_NODE_TYPE_P(value, PM_MATCH_PREDICATE_NODE)) {
21573 parse_rescue_modifier_terminator(parser, flags, depth);
21574 }
21575
21576 return value;
21577 }
21578
21579 // Otherwise the resbody is a plain `arg` (parse.y: `arg modifier_rescue
21580 // arg`), parsed above the `and`/`or`/`not` level so those stay outside it.
21581 return parse_expression(parser, PM_BINDING_POWER_DEFINED, flags, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21582}
21583
21591static void
21592parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) {
21593 if (call_node->arguments != NULL) {
21594 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS);
21595 pm_node_unreference(parser, UP(call_node->arguments));
21596 call_node->arguments = NULL;
21597 }
21598
21599 if (call_node->block != NULL) {
21600 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK);
21601 pm_node_unreference(parser, UP(call_node->block));
21602 call_node->block = NULL;
21603 }
21604}
21605
21606static PRISM_INLINE const uint8_t *
21607pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21608 cursor++;
21609
21610 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21611 uint8_t value = escape_hexadecimal_digit(*cursor);
21612 cursor++;
21613
21614 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21615 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(*cursor));
21616 cursor++;
21617 }
21618
21619 pm_buffer_append_byte(unescaped, value);
21620 } else {
21621 pm_buffer_append_string(unescaped, "\\x", 2);
21622 }
21623
21624 return cursor;
21625}
21626
21627static PRISM_INLINE const uint8_t *
21628pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21629 uint8_t value = (uint8_t) (*cursor - '0');
21630 cursor++;
21631
21632 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21633 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21634 cursor++;
21635
21636 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21637 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21638 cursor++;
21639 }
21640 }
21641
21642 pm_buffer_append_byte(unescaped, value);
21643 return cursor;
21644}
21645
21646static PRISM_INLINE const uint8_t *
21647pm_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) {
21648 const uint8_t *start = cursor - 1;
21649 cursor++;
21650
21651 if (cursor >= end) {
21652 pm_buffer_append_string(unescaped, "\\u", 2);
21653 return cursor;
21654 }
21655
21656 if (*cursor != '{') {
21657 size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4));
21658 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21659
21660 if (!pm_buffer_append_unicode_codepoint(unescaped, value)) {
21661 pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start));
21662 }
21663
21664 return cursor + length;
21665 }
21666
21667 cursor++;
21668 for (;;) {
21669 while (cursor < end && *cursor == ' ') cursor++;
21670
21671 if (cursor >= end) break;
21672 if (*cursor == '}') {
21673 cursor++;
21674 break;
21675 }
21676
21677 size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor);
21678 if (length == 0) {
21679 break;
21680 }
21681 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21682
21683 (void) pm_buffer_append_unicode_codepoint(unescaped, value);
21684 cursor += length;
21685 }
21686
21687 return cursor;
21688}
21689
21690static void
21691pm_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) {
21692 const uint8_t *end = source + length;
21693 pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source));
21694
21695 for (;;) {
21696 if (++cursor >= end) {
21697 pm_buffer_append_byte(unescaped, '\\');
21698 return;
21699 }
21700
21701 switch (*cursor) {
21702 case 'x':
21703 cursor = pm_named_capture_escape_hex(unescaped, cursor, end);
21704 break;
21705 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
21706 cursor = pm_named_capture_escape_octal(unescaped, cursor, end);
21707 break;
21708 case 'u':
21709 cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location);
21710 break;
21711 default:
21712 pm_buffer_append_byte(unescaped, '\\');
21713 break;
21714 }
21715
21716 const uint8_t *next_cursor = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, parser->encoding);
21717 if (next_cursor == NULL) break;
21718
21719 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (next_cursor - cursor));
21720 cursor = next_cursor;
21721 }
21722
21723 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (end - cursor));
21724}
21725
21744
21749static void
21750parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *capture, void *data) {
21751 pm_regexp_name_data_t *name_data = (pm_regexp_name_data_t *) data;
21752 pm_call_node_t *call = name_data->call;
21753 pm_constant_id_set_t *names = &name_data->names;
21754
21755 const uint8_t *source = pm_string_source(capture);
21756 size_t length = pm_string_length(capture);
21757 pm_buffer_t unescaped = { 0 };
21758
21759 // First, we need to handle escapes within the name of the capture group.
21760 // This is because regular expressions have three different representations
21761 // in prism. The first is the plain source code. The second is the
21762 // representation that will be sent to the regular expression engine, which
21763 // is the value of the "unescaped" field. This is poorly named, because it
21764 // actually still contains escapes, just a subset of them that the regular
21765 // expression engine knows how to handle. The third representation is fully
21766 // unescaped, which is what we need.
21767 const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding);
21768 if (PRISM_UNLIKELY(cursor != NULL)) {
21769 pm_named_capture_escape(parser, &unescaped, source, length, cursor, name_data->shared ? NULL : &call->receiver->location);
21770
21771 /*
21772 * Escapes like \u{} write nothing, so the unescaped name can be empty.
21773 * An empty name is never a valid local, and the buffer is unallocated
21774 * (NULL) in that case, so there is no slice to check.
21775 */
21776 if (pm_buffer_length(&unescaped) == 0) return;
21777
21778 source = (const uint8_t *) pm_buffer_value(&unescaped);
21779 length = pm_buffer_length(&unescaped);
21780 }
21781
21782 const uint8_t *start;
21783 const uint8_t *end;
21784 pm_constant_id_t name;
21785
21786 // If the name of the capture group isn't a valid identifier, we do
21787 // not add it to the local table.
21788 if (!pm_slice_is_valid_local(parser, source, source + length)) {
21789 pm_buffer_cleanup(&unescaped);
21790 return;
21791 }
21792
21793 if (name_data->shared) {
21794 // If the unescaped string is a slice of the source, then we can
21795 // copy the names directly. The pointers will line up.
21796 start = source;
21797 end = source + length;
21798 name = pm_parser_constant_id_raw(parser, start, end);
21799 } else {
21800 // Otherwise, the name is a slice of the malloc-ed owned string,
21801 // in which case we need to copy it out into a new string.
21802 start = parser->start + PM_NODE_START(call->receiver);
21803 end = parser->start + PM_NODE_END(call->receiver);
21804
21805 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
21806 memcpy(memory, source, length);
21807 name = pm_parser_constant_id_owned(parser, memory, length);
21808 }
21809
21810 /*
21811 * Add this name to the set of constants if it is valid, not duplicated,
21812 * and not a keyword.
21813 */
21814 if (name != 0 && pm_constant_id_set_insert(parser->arena, names, name)) {
21815
21816 int depth;
21817 if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
21818 // If the local is not already a local but it is a keyword, then we
21819 // do not want to add a capture for this.
21820 if (pm_local_is_keyword((const char *) source, length)) {
21821 pm_buffer_cleanup(&unescaped);
21822 return;
21823 }
21824
21825 // If the identifier is not already a local, then we will add it to
21826 // the local table.
21827 pm_parser_local_add(parser, name, start, end, 0);
21828 }
21829
21830 // Here we lazily create the MatchWriteNode since we know we're
21831 // about to add a target.
21832 if (name_data->match == NULL) {
21833 name_data->match = pm_match_write_node_create(parser, call);
21834 }
21835
21836 // Next, create the local variable target and add it to the list of
21837 // targets for the match.
21838 pm_token_t token = { .type = 0, .start = start, .end = end };
21839 pm_location_t token_loc = TOK2LOC(parser, &token);
21840 pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &token_loc, name, depth == -1 ? 0 : (uint32_t) depth));
21841 pm_node_list_append(parser->arena, &name_data->match->targets, target);
21842 }
21843
21844 pm_buffer_cleanup(&unescaped);
21845}
21846
21852static pm_node_t *
21853parse_interpolated_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call, bool extended_mode) {
21854 pm_regexp_name_data_t name_data = {
21855 .call = call,
21856 .match = NULL,
21857 .names = { 0 },
21858 .shared = false,
21859 };
21860
21861 pm_regexp_parse_named_captures(parser, pm_string_source(content), pm_string_length(content), extended_mode, parse_regular_expression_named_capture, &name_data);
21862
21863 if (name_data.match != NULL) {
21864 return UP(name_data.match);
21865 } else {
21866 return UP(call);
21867 }
21868}
21869
21870static pm_if_node_t *
21871parse_ternary(pm_parser_t *parser, pm_node_t *node, uint8_t flags, uint16_t depth) {
21872 context_push(parser, PM_CONTEXT_TERNARY);
21873 pm_node_list_t current_block_exits = { 0 };
21874 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
21875
21876 pm_token_t qmark = parser->current;
21877 parser_lex(parser);
21878
21879 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));
21880
21881 if (parser->recovering) {
21882 // If parsing the true expression of this ternary resulted in a syntax
21883 // error that we can recover from, then we're going to put missing nodes
21884 // and tokens into the remaining places. We want to be sure to do this
21885 // before the `expect` function call to make sure it doesn't
21886 // accidentally move past a ':' token that occurs after the syntax
21887 // error.
21888 pm_token_t colon = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
21889 pm_node_t *false_expression = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &colon), PM_TOKEN_LENGTH(&colon)));
21890
21891 context_pop(parser);
21892 pop_block_exits(parser, previous_block_exits);
21893 return pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
21894 }
21895
21896 accept1(parser, PM_TOKEN_NEWLINE);
21897 expect1(parser, PM_TOKEN_COLON, PM_ERR_TERNARY_COLON);
21898
21899 pm_token_t colon = parser->previous;
21900 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));
21901
21902 context_pop(parser);
21903 pop_block_exits(parser, previous_block_exits);
21904 return pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
21905}
21906
21907static pm_node_t *
21908parse_constant_path_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, uint8_t flags, uint16_t depth) {
21909 pm_token_t delimiter = parser->current;
21910 parser_lex(parser);
21911
21912 switch (parser->current.type) {
21913 case PM_TOKEN_CONSTANT: {
21914 parser_lex(parser);
21915 pm_node_t *path;
21916
21917 if (
21918 (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
21919 ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && token_begins_argument_p(parser->current.type))
21920 ) {
21921 /* If we have a constant immediately following a '::' operator,
21922 * then this can either be a constant path or a method call,
21923 * depending on what follows the constant.
21924 *
21925 * If we have parentheses, then this is a method call. That
21926 * would look like Foo::Bar(). */
21927 pm_token_t message = parser->previous;
21928 pm_arguments_t arguments = { 0 };
21929
21930 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
21931 path = UP(pm_call_node_call_create(parser, node, &delimiter, &message, &arguments));
21932 } else {
21933 /* Otherwise, this is a constant path. That would look like
21934 * Foo::Bar. */
21935 path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
21936 }
21937
21938 return parse_multi_target(parser, path, previous_binding_power, depth);
21939 }
21940 case PM_CASE_OPERATOR:
21941 case PM_CASE_KEYWORD:
21942 case PM_TOKEN_IDENTIFIER:
21943 case PM_TOKEN_METHOD_NAME: {
21944 parser_lex(parser);
21945 pm_token_t message = parser->previous;
21946
21947 /* If we have an identifier following a '::' operator, then it is
21948 * for sure a method call. */
21949 pm_arguments_t arguments = { 0 };
21950 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
21951
21952 pm_call_node_t *call = pm_call_node_call_create(parser, node, &delimiter, &message, &arguments);
21953 return parse_multi_target(parser, UP(call), previous_binding_power, depth);
21954 }
21955 case PM_TOKEN_PARENTHESIS_LEFT: {
21956 /* If we have a parenthesis following a '::' operator, then it is the
21957 * method call shorthand. That would look like Foo::(bar). */
21958 pm_arguments_t arguments = { 0 };
21959 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
21960
21961 return UP(pm_call_node_shorthand_create(parser, node, &delimiter, &arguments));
21962 }
21963 default:
21964 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
21965 return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
21966 }
21967}
21968
21969static pm_node_t *
21970parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
21971 /* Note that we _must_ parse the value before adding the local variables in
21972 * order to properly mirror the behavior of Ruby. For example,
21973 *
21974 * /(?<foo>bar)/ =~ foo
21975 *
21976 * In this case, `foo` should be a method call and not a local yet.
21977 */
21978 pm_token_t operator = parser->current;
21979 parser_lex(parser);
21980
21981 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));
21982
21983 // By default, we're going to create a call node and then return it.
21984 pm_call_node_t *call = pm_call_node_binary_create(parser, node, &operator, argument, 0);
21985 pm_node_t *result = UP(call);
21986
21987 /* If the receiver of this =~ is a regular expression node, then we need to
21988 * introduce local variables for it based on its named capture groups. */
21989 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE)) {
21990 /* It's possible to have an interpolated regular expression node that
21991 * only contains strings. This is because it can be split up by a
21992 * heredoc. In this case we need to concat the unescaped strings
21993 * together and then parse them as a regular expression. */
21995
21996 bool interpolated = false;
21997 size_t total_length = 0;
21998
21999 pm_node_t *part;
22000 PM_NODE_LIST_FOREACH(parts, index, part) {
22001 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
22002 total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped);
22003 } else {
22004 interpolated = true;
22005 break;
22006 }
22007 }
22008
22009 if (!interpolated && total_length > 0) {
22010 void *memory = xmalloc(total_length);
22011 if (!memory) abort();
22012
22013 uint8_t *cursor = memory;
22014 PM_NODE_LIST_FOREACH(parts, index, part) {
22015 pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
22016 size_t length = pm_string_length(unescaped);
22017
22018 memcpy(cursor, pm_string_source(unescaped), length);
22019 cursor += length;
22020 }
22021
22022 pm_string_t owned;
22023 pm_string_owned_init(&owned, (uint8_t *) memory, total_length);
22024
22025 result = parse_interpolated_regular_expression_named_captures(parser, &owned, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
22026 pm_string_cleanup(&owned);
22027 }
22028 } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
22029 /* If we have a regular expression node, then we can parse the named
22030 * captures and validate encoding in one pass. */
22032 pm_regexp_name_data_t name_data = {
22033 .call = call,
22034 .match = NULL,
22035 .names = { 0 },
22036 .shared = regexp->unescaped.type == PM_STRING_SHARED,
22037 };
22038
22039 pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data));
22040
22041 if (name_data.match != NULL) {
22042 result = UP(name_data.match);
22043 }
22044 }
22045
22046 return result;
22047}
22048
22049static pm_node_t *
22050parse_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) {
22051 pm_token_t operator = parser->current;
22052
22053 switch (PM_NODE_TYPE(node)) {
22054 case PM_CALL_NODE: {
22055 // If we have no arguments to the call node and we need this
22056 // to be a target then this is either a method call or a
22057 // local variable write. This _must_ happen before the value
22058 // is parsed because it could be referenced in the value.
22059 pm_call_node_t *call_node = (pm_call_node_t *) node;
22060 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22061 pm_parser_local_add_location(parser, &call_node->message_loc, 0);
22062 }
22063 }
22065 case PM_CASE_WRITABLE: {
22066 // When we have `it = value`, we need to add `it` as a local
22067 // variable before parsing the value, in case the value
22068 // references the variable.
22069 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
22070 pm_parser_local_add_location(parser, &node->location, 0);
22071 }
22072
22073 parser_lex(parser);
22074 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));
22075
22076 if (PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) && previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
22077 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_MULTI_WRITE);
22078 }
22079
22080 return parse_write(parser, node, &operator, value);
22081 }
22082 case PM_SPLAT_NODE: {
22083 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
22084 pm_multi_target_node_targets_append(parser, multi_target, node);
22085
22086 parser_lex(parser);
22087 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));
22088 return parse_write(parser, UP(multi_target), &operator, value);
22089 }
22090 case PM_SOURCE_ENCODING_NODE:
22091 case PM_FALSE_NODE:
22092 case PM_SOURCE_FILE_NODE:
22093 case PM_SOURCE_LINE_NODE:
22094 case PM_NIL_NODE:
22095 case PM_SELF_NODE:
22096 case PM_TRUE_NODE: {
22097 // In these special cases, we have specific error messages
22098 // and we will replace them with local variable writes.
22099 parser_lex(parser);
22100 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
22101 return parse_unwriteable_write(parser, node, &operator, value);
22102 }
22103 default:
22104 // In this case we have an = sign, but we don't know what
22105 // it's for. We need to treat it as an error. We'll mark it
22106 // as an error and skip past it.
22107 parser_lex(parser);
22108 pm_parser_err_token(parser, &operator, PM_ERR_EXPRESSION_NOT_WRITABLE);
22109 return node;
22110 }
22111}
22112
22113static pm_node_t *
22114parse_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) {
22115 pm_token_t operator = parser->current;
22116
22117 switch (PM_NODE_TYPE(node)) {
22118 case PM_BACK_REFERENCE_READ_NODE:
22119 case PM_NUMBERED_REFERENCE_READ_NODE:
22120 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
22122 case PM_GLOBAL_VARIABLE_READ_NODE: {
22123 parser_lex(parser);
22124
22125 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22126 pm_node_t *result = UP(pm_global_variable_and_write_node_create(parser, node, &operator, value));
22127
22128 return result;
22129 }
22130 case PM_CLASS_VARIABLE_READ_NODE: {
22131 parser_lex(parser);
22132
22133 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22134 pm_node_t *result = UP(pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &operator, value));
22135
22136 return result;
22137 }
22138 case PM_CONSTANT_PATH_NODE: {
22139 parser_lex(parser);
22140
22141 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22142 pm_node_t *write = UP(pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &operator, value));
22143
22144 return parse_shareable_constant_write(parser, write);
22145 }
22146 case PM_CONSTANT_READ_NODE: {
22147 parser_lex(parser);
22148
22149 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22150 pm_node_t *write = UP(pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &operator, value));
22151
22152 if (context_def_p(parser)) {
22153 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
22154 }
22155
22156 return parse_shareable_constant_write(parser, write);
22157 }
22158 case PM_INSTANCE_VARIABLE_READ_NODE: {
22159 parser_lex(parser);
22160
22161 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22162 pm_node_t *result = UP(pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &operator, value));
22163
22164 return result;
22165 }
22166 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
22167 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
22168 parser_lex(parser);
22169
22170 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22171 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &operator, value, name, 0));
22172
22173 pm_node_unreference(parser, node);
22174 return result;
22175 }
22176 case PM_LOCAL_VARIABLE_READ_NODE: {
22177 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
22178 PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + node->location.start);
22179 pm_node_unreference(parser, node);
22180 }
22181
22183 parser_lex(parser);
22184
22185 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22186 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &operator, value, cast->name, cast->depth));
22187
22188 return result;
22189 }
22190 case PM_CALL_NODE: {
22191 pm_call_node_t *cast = (pm_call_node_t *) node;
22192
22193 // If we have a vcall (a method with no arguments and no
22194 // receiver that could have been a local variable) then we
22195 // will transform it into a local variable write.
22196 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22197 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
22198 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
22199 parser_lex(parser);
22200
22201 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22202 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &operator, value, constant_id, 0));
22203
22204 return result;
22205 }
22206
22207 // Move past the token here so that we have already added
22208 // the local variable by this point.
22209 parser_lex(parser);
22210
22211 // If there is no call operator and the message is "[]" then
22212 // this is an aref expression, and we can transform it into
22213 // an aset expression.
22214 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
22215 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22216 return UP(pm_index_and_write_node_create(parser, cast, &operator, value));
22217 }
22218
22219 // If this node cannot be writable, then we have an error.
22220 if (pm_call_node_writable_p(parser, cast)) {
22221 parse_write_name(parser, &cast->name);
22222 } else {
22223 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22224 }
22225
22226 parse_call_operator_write(parser, cast, &operator);
22227 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22228 return UP(pm_call_and_write_node_create(parser, cast, &operator, value));
22229 }
22230 case PM_MULTI_WRITE_NODE: {
22231 parser_lex(parser);
22232
22233 pm_parser_err_token(parser, &operator, PM_ERR_AMPAMPEQ_MULTI_ASSIGN);
22234 return node;
22235 }
22236 default:
22237 parser_lex(parser);
22238
22239 // In this case we have an &&= sign, but we don't know what it's for.
22240 // We need to treat it as an error. For now, we'll mark it as an error
22241 // and just skip right past it.
22242 pm_parser_err_token(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
22243 return node;
22244 }
22245}
22246
22247static pm_node_t *
22248parse_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) {
22249 pm_token_t operator = parser->current;
22250
22251 switch (PM_NODE_TYPE(node)) {
22252 case PM_BACK_REFERENCE_READ_NODE:
22253 case PM_NUMBERED_REFERENCE_READ_NODE:
22254 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
22256 case PM_GLOBAL_VARIABLE_READ_NODE: {
22257 parser_lex(parser);
22258
22259 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22260 pm_node_t *result = UP(pm_global_variable_or_write_node_create(parser, node, &operator, value));
22261
22262 return result;
22263 }
22264 case PM_CLASS_VARIABLE_READ_NODE: {
22265 parser_lex(parser);
22266
22267 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22268 pm_node_t *result = UP(pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &operator, value));
22269
22270 return result;
22271 }
22272 case PM_CONSTANT_PATH_NODE: {
22273 parser_lex(parser);
22274
22275 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22276 pm_node_t *write = UP(pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &operator, value));
22277
22278 return parse_shareable_constant_write(parser, write);
22279 }
22280 case PM_CONSTANT_READ_NODE: {
22281 parser_lex(parser);
22282
22283 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22284 pm_node_t *write = UP(pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &operator, value));
22285
22286 if (context_def_p(parser)) {
22287 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
22288 }
22289
22290 return parse_shareable_constant_write(parser, write);
22291 }
22292 case PM_INSTANCE_VARIABLE_READ_NODE: {
22293 parser_lex(parser);
22294
22295 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22296 pm_node_t *result = UP(pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &operator, value));
22297
22298 return result;
22299 }
22300 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
22301 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
22302 parser_lex(parser);
22303
22304 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22305 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &operator, value, name, 0));
22306
22307 pm_node_unreference(parser, node);
22308 return result;
22309 }
22310 case PM_LOCAL_VARIABLE_READ_NODE: {
22311 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
22312 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
22313 pm_node_unreference(parser, node);
22314 }
22315
22317 parser_lex(parser);
22318
22319 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22320 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &operator, value, cast->name, cast->depth));
22321
22322 return result;
22323 }
22324 case PM_CALL_NODE: {
22325 pm_call_node_t *cast = (pm_call_node_t *) node;
22326
22327 // If we have a vcall (a method with no arguments and no
22328 // receiver that could have been a local variable) then we
22329 // will transform it into a local variable write.
22330 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22331 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
22332 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
22333 parser_lex(parser);
22334
22335 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22336 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &operator, value, constant_id, 0));
22337
22338 return result;
22339 }
22340
22341 // Move past the token here so that we have already added
22342 // the local variable by this point.
22343 parser_lex(parser);
22344
22345 // If there is no call operator and the message is "[]" then
22346 // this is an aref expression, and we can transform it into
22347 // an aset expression.
22348 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
22349 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22350 return UP(pm_index_or_write_node_create(parser, cast, &operator, value));
22351 }
22352
22353 // If this node cannot be writable, then we have an error.
22354 if (pm_call_node_writable_p(parser, cast)) {
22355 parse_write_name(parser, &cast->name);
22356 } else {
22357 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22358 }
22359
22360 parse_call_operator_write(parser, cast, &operator);
22361 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22362 return UP(pm_call_or_write_node_create(parser, cast, &operator, value));
22363 }
22364 case PM_MULTI_WRITE_NODE: {
22365 parser_lex(parser);
22366 pm_parser_err_token(parser, &operator, PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN);
22367 return node;
22368 }
22369 default:
22370 parser_lex(parser);
22371
22372 // In this case we have an ||= sign, but we don't know what it's for.
22373 // We need to treat it as an error. For now, we'll mark it as an error
22374 // and just skip right past it.
22375 pm_parser_err_token(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
22376 return node;
22377 }
22378}
22379
22380static pm_node_t *
22381parse_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) {
22382 pm_token_t operator = parser->current;
22383
22384 switch (PM_NODE_TYPE(node)) {
22385 case PM_BACK_REFERENCE_READ_NODE:
22386 case PM_NUMBERED_REFERENCE_READ_NODE:
22387 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
22389 case PM_GLOBAL_VARIABLE_READ_NODE: {
22390 parser_lex(parser);
22391
22392 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22393 pm_node_t *result = UP(pm_global_variable_operator_write_node_create(parser, node, &operator, value));
22394
22395 return result;
22396 }
22397 case PM_CLASS_VARIABLE_READ_NODE: {
22398 parser_lex(parser);
22399
22400 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22401 pm_node_t *result = UP(pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &operator, value));
22402
22403 return result;
22404 }
22405 case PM_CONSTANT_PATH_NODE: {
22406 parser_lex(parser);
22407
22408 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22409 pm_node_t *write = UP(pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &operator, value));
22410
22411 return parse_shareable_constant_write(parser, write);
22412 }
22413 case PM_CONSTANT_READ_NODE: {
22414 parser_lex(parser);
22415
22416 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22417 pm_node_t *write = UP(pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &operator, value));
22418
22419 if (context_def_p(parser)) {
22420 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
22421 }
22422
22423 return parse_shareable_constant_write(parser, write);
22424 }
22425 case PM_INSTANCE_VARIABLE_READ_NODE: {
22426 parser_lex(parser);
22427
22428 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22429 pm_node_t *result = UP(pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &operator, value));
22430
22431 return result;
22432 }
22433 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
22434 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
22435 parser_lex(parser);
22436
22437 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22438 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &operator, value, name, 0));
22439
22440 pm_node_unreference(parser, node);
22441 return result;
22442 }
22443 case PM_LOCAL_VARIABLE_READ_NODE: {
22444 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
22445 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
22446 pm_node_unreference(parser, node);
22447 }
22448
22450 parser_lex(parser);
22451
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 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &operator, value, cast->name, cast->depth));
22454
22455 return result;
22456 }
22457 case PM_CALL_NODE: {
22458 parser_lex(parser);
22459 pm_call_node_t *cast = (pm_call_node_t *) node;
22460
22461 // If we have a vcall (a method with no arguments and no
22462 // receiver that could have been a local variable) then we
22463 // will transform it into a local variable write.
22464 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22465 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
22466 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
22467 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22468 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &operator, value, constant_id, 0));
22469
22470 return result;
22471 }
22472
22473 // If there is no call operator and the message is "[]" then
22474 // this is an aref expression, and we can transform it into
22475 // an aset expression.
22476 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
22477 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22478 return UP(pm_index_operator_write_node_create(parser, cast, &operator, value));
22479 }
22480
22481 // If this node cannot be writable, then we have an error.
22482 if (pm_call_node_writable_p(parser, cast)) {
22483 parse_write_name(parser, &cast->name);
22484 } else {
22485 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22486 }
22487
22488 parse_call_operator_write(parser, cast, &operator);
22489 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22490 return UP(pm_call_operator_write_node_create(parser, cast, &operator, value));
22491 }
22492 case PM_MULTI_WRITE_NODE: {
22493 parser_lex(parser);
22494 pm_parser_err_token(parser, &operator, PM_ERR_OPERATOR_MULTI_ASSIGN);
22495 return node;
22496 }
22497 default:
22498 parser_lex(parser);
22499
22500 // In this case we have an operator but we don't know what it's for.
22501 // We need to treat it as an error. For now, we'll mark it as an error
22502 // and just skip right past it.
22503 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_str(parser->current.type));
22504 return node;
22505 }
22506}
22507
22508static pm_node_t *
22509parse_call(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, uint8_t flags, uint16_t depth) {
22510 pm_token_t operator = parser->current;
22511 parser_lex(parser);
22512
22513 pm_arguments_t arguments = { 0 };
22514
22515 // This if statement handles the foo.() syntax.
22516 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
22517 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
22518 return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments));
22519 }
22520
22521 switch (PM_NODE_TYPE(node)) {
22522 case PM_RESCUE_MODIFIER_NODE: {
22524 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22525 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22526 }
22527 break;
22528 }
22529 case PM_AND_NODE: {
22530 pm_and_node_t *cast = (pm_and_node_t *) node;
22531 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22532 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22533 }
22534 break;
22535 }
22536 case PM_OR_NODE: {
22537 pm_or_node_t *cast = (pm_or_node_t *) node;
22538 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22539 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22540 }
22541 break;
22542 }
22543 default:
22544 break;
22545 }
22546
22547 pm_token_t message;
22548
22549 switch (parser->current.type) {
22550 case PM_CASE_OPERATOR:
22551 case PM_CASE_KEYWORD:
22552 case PM_TOKEN_CONSTANT:
22553 case PM_TOKEN_IDENTIFIER:
22554 case PM_TOKEN_METHOD_NAME: {
22555 parser_lex(parser);
22556 message = parser->previous;
22557 break;
22558 }
22559 default: {
22560 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_str(parser->current.type));
22561 message = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
22562 }
22563 }
22564
22565 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22566 pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments);
22567
22568 if (
22569 (previous_binding_power == PM_BINDING_POWER_STATEMENT) &&
22570 arguments.arguments == NULL &&
22571 arguments.opening_loc.length == 0 &&
22572 match1(parser, PM_TOKEN_COMMA)
22573 ) {
22574 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22575 } else {
22576 return UP(call);
22577 }
22578}
22579
22580static pm_node_t *
22581parse_aref(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, uint8_t flags, uint16_t depth) {
22582 parser_lex(parser);
22583
22584 pm_arguments_t arguments = { 0 };
22585 arguments.opening_loc = TOK2LOC(parser, &parser->previous);
22586
22587 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
22588 parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
22589 expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET);
22590 }
22591
22592 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
22593
22594 // If we have a comma after the closing bracket then this is a multiple
22595 // assignment and we should parse the targets.
22596 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22597 pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments);
22598 return parse_targets_validate(parser, UP(aref), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22599 }
22600
22601 // If we're at the end of the arguments, we can now check if there is a
22602 // block node that starts with a {. If there is, then we can parse it and
22603 // add it to the arguments.
22604 pm_block_node_t *block = NULL;
22605 if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
22606 block = parse_block(parser, (uint16_t) (depth + 1));
22607 pm_arguments_validate_block(parser, &arguments, block);
22608 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
22609 block = parse_block(parser, (uint16_t) (depth + 1));
22610 }
22611
22612 if (block != NULL) {
22613 if (arguments.block != NULL) {
22614 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_AFTER_BLOCK);
22615 if (arguments.arguments == NULL) {
22616 arguments.arguments = pm_arguments_node_create(parser);
22617 }
22618 pm_arguments_node_arguments_append(parser->arena, arguments.arguments, arguments.block);
22619 }
22620
22621 arguments.block = UP(block);
22622 }
22623
22624 return UP(pm_call_node_aref_create(parser, node, &arguments));
22625}
22626
22627static PRISM_INLINE pm_node_t *
22628parse_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) {
22629 pm_token_t token = parser->current;
22630
22631 switch (token.type) {
22632 case PM_TOKEN_EQUAL:
22633 return parse_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22634 case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL:
22635 return parse_and_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22636 case PM_TOKEN_PIPE_PIPE_EQUAL:
22637 return parse_or_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22638 case PM_TOKEN_AMPERSAND_EQUAL:
22639 case PM_TOKEN_CARET_EQUAL:
22640 case PM_TOKEN_GREATER_GREATER_EQUAL:
22641 case PM_TOKEN_LESS_LESS_EQUAL:
22642 case PM_TOKEN_MINUS_EQUAL:
22643 case PM_TOKEN_PERCENT_EQUAL:
22644 case PM_TOKEN_PIPE_EQUAL:
22645 case PM_TOKEN_PLUS_EQUAL:
22646 case PM_TOKEN_SLASH_EQUAL:
22647 case PM_TOKEN_STAR_EQUAL:
22648 case PM_TOKEN_STAR_STAR_EQUAL:
22649 return parse_operator_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22650 case PM_TOKEN_AMPERSAND_AMPERSAND:
22651 case PM_TOKEN_KEYWORD_AND: {
22652 parser_lex(parser);
22653
22654 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));
22655 return UP(pm_and_node_create(parser, node, &token, right));
22656 }
22657 case PM_TOKEN_KEYWORD_OR:
22658 case PM_TOKEN_PIPE_PIPE: {
22659 parser_lex(parser);
22660
22661 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));
22662 return UP(pm_or_node_create(parser, node, &token, right));
22663 }
22664 case PM_TOKEN_EQUAL_TILDE:
22665 return parse_match(parser, node, binding_power, flags, depth);
22666 case PM_TOKEN_UAMPERSAND:
22667 case PM_TOKEN_USTAR:
22668 case PM_TOKEN_USTAR_STAR:
22669 // The only times this will occur are when we are in an error state,
22670 // but we'll put them in here so that errors can propagate.
22671 case PM_TOKEN_BANG_EQUAL:
22672 case PM_TOKEN_BANG_TILDE:
22673 case PM_TOKEN_EQUAL_EQUAL:
22674 case PM_TOKEN_EQUAL_EQUAL_EQUAL:
22675 case PM_TOKEN_LESS_EQUAL_GREATER:
22676 case PM_TOKEN_CARET:
22677 case PM_TOKEN_PIPE:
22678 case PM_TOKEN_AMPERSAND:
22679 case PM_TOKEN_GREATER_GREATER:
22680 case PM_TOKEN_LESS_LESS:
22681 case PM_TOKEN_MINUS:
22682 case PM_TOKEN_PLUS:
22683 case PM_TOKEN_PERCENT:
22684 case PM_TOKEN_SLASH:
22685 case PM_TOKEN_STAR:
22686 case PM_TOKEN_STAR_STAR: {
22687 parser_lex(parser);
22688 pm_token_t operator = parser->previous;
22689 switch (PM_NODE_TYPE(node)) {
22690 case PM_RESCUE_MODIFIER_NODE: {
22692 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22693 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22694 }
22695 break;
22696 }
22697 case PM_AND_NODE: {
22698 pm_and_node_t *cast = (pm_and_node_t *) node;
22699 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22700 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22701 }
22702 break;
22703 }
22704 case PM_OR_NODE: {
22705 pm_or_node_t *cast = (pm_or_node_t *) node;
22706 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22707 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22708 }
22709 break;
22710 }
22711 default:
22712 break;
22713 }
22714
22715 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));
22716 return UP(pm_call_node_binary_create(parser, node, &token, argument, 0));
22717 }
22718 case PM_TOKEN_GREATER:
22719 case PM_TOKEN_GREATER_EQUAL:
22720 case PM_TOKEN_LESS:
22721 case PM_TOKEN_LESS_EQUAL: {
22722 if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) {
22723 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON);
22724 }
22725
22726 parser_lex(parser);
22727 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));
22728 return UP(pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON));
22729 }
22730 case PM_TOKEN_AMPERSAND_DOT:
22731 case PM_TOKEN_DOT:
22732 return parse_call(parser, node, previous_binding_power, flags, depth);
22733 case PM_TOKEN_DOT_DOT:
22734 case PM_TOKEN_DOT_DOT_DOT: {
22735 parser_lex(parser);
22736
22737 pm_node_t *right = NULL;
22738 if (token_begins_expression_p(parser->current.type)) {
22739 right = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22740 }
22741
22742 return UP(pm_range_node_create(parser, node, &token, right));
22743 }
22744 case PM_TOKEN_KEYWORD_IF_MODIFIER: {
22745 pm_token_t keyword = parser->current;
22746 parser_lex(parser);
22747
22748 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));
22749 return UP(pm_if_node_modifier_create(parser, node, &keyword, predicate));
22750 }
22751 case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: {
22752 pm_token_t keyword = parser->current;
22753 parser_lex(parser);
22754
22755 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));
22756 return UP(pm_unless_node_modifier_create(parser, node, &keyword, predicate));
22757 }
22758 case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: {
22759 parser_lex(parser);
22760 pm_statements_node_t *statements = pm_statements_node_create(parser);
22761 pm_statements_node_body_append(parser, statements, node, true);
22762
22763 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));
22764 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));
22765 }
22766 case PM_TOKEN_KEYWORD_WHILE_MODIFIER: {
22767 parser_lex(parser);
22768 pm_statements_node_t *statements = pm_statements_node_create(parser);
22769 pm_statements_node_body_append(parser, statements, node, true);
22770
22771 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));
22772 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));
22773 }
22774 case PM_TOKEN_QUESTION_MARK:
22775 return UP(parse_ternary(parser, node, flags, depth));
22776 case PM_TOKEN_COLON_COLON:
22777 return parse_constant_path_infix(parser, node, previous_binding_power, flags, depth);
22778 case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: {
22779 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
22780 parser_lex(parser);
22781 accept1(parser, PM_TOKEN_NEWLINE);
22782
22783 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));
22784 context_pop(parser);
22785
22786 return UP(pm_rescue_modifier_node_create(parser, node, &token, value));
22787 }
22788 case PM_TOKEN_BRACKET_LEFT:
22789 return parse_aref(parser, node, previous_binding_power, flags, depth);
22790 case PM_TOKEN_KEYWORD_IN: {
22791 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22792 parser->pattern_matching_newlines = true;
22793
22794 pm_token_t operator = parser->current;
22795 parser->command_start = false;
22796 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22797 parser_lex(parser);
22798
22799 pm_constant_id_set_t captures = { 0 };
22800 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));
22801
22802 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22803
22804 return UP(pm_match_predicate_node_create(parser, node, pattern, &operator));
22805 }
22806 case PM_TOKEN_EQUAL_GREATER: {
22807 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22808 parser->pattern_matching_newlines = true;
22809
22810 pm_token_t operator = parser->current;
22811 parser->command_start = false;
22812 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22813 parser_lex(parser);
22814
22815 pm_constant_id_set_t captures = { 0 };
22816 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));
22817
22818 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22819
22820 return UP(pm_match_required_node_create(parser, node, pattern, &operator));
22821 }
22822 default:
22823 assert(false && "unreachable");
22824 return NULL;
22825 }
22826}
22827
22828#undef PM_PARSE_PATTERN_SINGLE
22829#undef PM_PARSE_PATTERN_TOP
22830#undef PM_PARSE_PATTERN_MULTI
22831
22844static bool
22845parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) {
22846 pm_binding_power_t left = pm_binding_powers[parser->current.type].left;
22847
22848 switch (PM_NODE_TYPE(node)) {
22849 case PM_MULTI_WRITE_NODE:
22850 case PM_RETURN_NODE:
22851 case PM_BREAK_NODE:
22852 case PM_NEXT_NODE:
22853 return left > PM_BINDING_POWER_MODIFIER;
22854 case PM_CLASS_VARIABLE_WRITE_NODE:
22855 case PM_CONSTANT_PATH_WRITE_NODE:
22856 case PM_CONSTANT_WRITE_NODE:
22857 case PM_GLOBAL_VARIABLE_WRITE_NODE:
22858 case PM_INSTANCE_VARIABLE_WRITE_NODE:
22859 case PM_LOCAL_VARIABLE_WRITE_NODE:
22860 return PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && left > PM_BINDING_POWER_MODIFIER;
22861 case PM_CALL_NODE: {
22862 // Calls with an implicit array on the right-hand side are
22863 // statements and can only be followed by modifiers.
22864 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)) {
22865 return left > PM_BINDING_POWER_MODIFIER;
22866 }
22867
22868 // Command-style calls (including block commands like
22869 // `foo bar do end`) can only be followed by composition
22870 // (and/or) and modifier (if/unless/etc.) operators.
22871 if (pm_command_call_value_p(parser, node)) {
22872 return left > PM_BINDING_POWER_COMPOSITION;
22873 }
22874
22875 // A block call (command with do-block, or any call chained
22876 // from one) can only be followed by call chaining (., ::,
22877 // &.), composition (and/or), and modifier operators.
22878 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22879 }
22880 case PM_SUPER_NODE:
22881 case PM_YIELD_NODE:
22882 // Command-style super/yield (without parens) can only be followed
22883 // by composition and modifier operators.
22884 if (pm_command_call_value_p(parser, node)) {
22885 return left > PM_BINDING_POWER_COMPOSITION;
22886 }
22887
22888 /* A super carrying a do-block is a block call, so it may also be
22889 * followed by call chaining (`.`, `::`, `&.`). */
22890 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22891 case PM_DEF_NODE:
22892 // An endless method whose body is a command-style call (e.g.,
22893 // `def f = foo bar`) is a command write and can only be
22894 // followed by modifiers.
22895 return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(parser, node);
22896 case PM_RESCUE_MODIFIER_NODE:
22897 // A rescue modifier whose handler is a pattern match (=> or in)
22898 // produces a statement and cannot be followed by operators above
22899 // the modifier level.
22900 if (left > PM_BINDING_POWER_MODIFIER) {
22902 pm_node_t *rescue_expression = cast->rescue_expression;
22903 return PM_NODE_TYPE_P(rescue_expression, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(rescue_expression, PM_MATCH_PREDICATE_NODE);
22904 }
22905 return false;
22906 default:
22907 return false;
22908 }
22909}
22910
22919static pm_node_t *
22920parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
22921 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
22922 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
22923 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
22924 }
22925
22926 pm_node_t *node = parse_expression_prefix(parser, binding_power, flags, diag_id, depth);
22927
22928 // Some prefix nodes are statements and can only be followed by modifiers
22929 // (if/unless/while/until/rescue) or nothing at all. We check these cheaply
22930 // here before entering the infix loop.
22931 switch (PM_NODE_TYPE(node)) {
22932 case PM_ERROR_RECOVERY_NODE:
22933 return node;
22934 case PM_PRE_EXECUTION_NODE:
22935 return node;
22936 case PM_POST_EXECUTION_NODE:
22937 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
22938 case PM_ALIAS_METHOD_NODE:
22939 case PM_UNDEF_NODE:
22940 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
22941 return node;
22942 }
22943 break;
22944 case PM_CALL_NODE:
22945 case PM_SUPER_NODE:
22946 case PM_YIELD_NODE:
22947 case PM_DEF_NODE:
22948 if (parse_expression_terminator(parser, node)) {
22949 return node;
22950 }
22951 break;
22952 case PM_SYMBOL_NODE:
22953 if (pm_symbol_node_label_p(parser, node)) {
22954 return node;
22955 }
22956 break;
22957 default:
22958 break;
22959 }
22960
22961 // Look and see if the next token can be parsed as an infix operator. If it
22962 // can, then we'll parse it using parse_expression_infix.
22963 pm_binding_powers_t current_binding_powers;
22964 pm_token_type_t current_token_type;
22965
22966 while (
22967 current_token_type = parser->current.type,
22968 current_binding_powers = pm_binding_powers[current_token_type],
22969 binding_power <= current_binding_powers.left &&
22970 current_binding_powers.binary
22971 ) {
22972 node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, flags, (uint16_t) (depth + 1));
22973 if (parse_expression_terminator(parser, node)) return node;
22974
22975 // If the operator is nonassoc and we should not be able to parse the
22976 // upcoming infix operator, break.
22977 if (current_binding_powers.nonassoc) {
22978 // If we are about to parse another non-associative operator at the
22979 // same precedence as the one we just parsed, then we need to add an
22980 // error. This covers chaining the same operator (`1 == 2 == 3`) as
22981 // well as different operators that share a precedence, since they
22982 // are equally non-associative with one another (`1 == 2 != 3`,
22983 // `1...2..3`).
22984 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22985 if (next_binding_powers.nonassoc && next_binding_powers.left == current_binding_powers.left) {
22986 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));
22987 break;
22988 }
22989
22990 // If this is an endless range, then we need to reject a couple of
22991 // additional operators because it violates the normal operator
22992 // precedence rules. Those patterns are:
22993 //
22994 // 1.. & 2
22995 // 1.. * 2
22996 //
22997 if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) {
22998 if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) {
22999 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));
23000 break;
23001 }
23002
23003 if (PM_BINDING_POWER_TERM <= next_binding_powers.left) {
23004 break;
23005 }
23006 } else if (current_binding_powers.left <= next_binding_powers.left) {
23007 break;
23008 }
23009 }
23010
23011 if (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) {
23012 // A command-style method call is only accepted on method chains.
23013 // Thus, we check whether the parsed node can continue method chains.
23014 // The method chain can continue if the parsed node is one of the following five kinds:
23015 // (1) index access: foo[1]
23016 // (2) attribute access: foo.bar
23017 // (3) method call with parenthesis: foo.bar(1)
23018 // (4) method call with a block: foo.bar do end
23019 // (5) constant path: foo::Bar
23020 switch (node->type) {
23021 case PM_CALL_NODE: {
23022 pm_call_node_t *cast = (pm_call_node_t *)node;
23023 if (
23024 // (1) foo[1]
23025 !(
23026 cast->call_operator_loc.length == 0 &&
23027 cast->message_loc.length > 0 &&
23028 parser->start[cast->message_loc.start] == '[' &&
23029 parser->start[cast->message_loc.start + cast->message_loc.length - 1] == ']'
23030 ) &&
23031 // (2) foo.bar
23032 !(
23033 cast->call_operator_loc.length > 0 &&
23034 cast->arguments == NULL &&
23035 cast->block == NULL &&
23036 cast->opening_loc.length == 0
23037 ) &&
23038 // (3) foo.bar(1)
23039 !(
23040 cast->call_operator_loc.length > 0 &&
23041 cast->opening_loc.length > 0
23042 ) &&
23043 // (4) foo.bar do end
23044 !(
23045 cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
23046 )
23047 ) {
23048 flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
23049 }
23050 break;
23051 }
23052 // (5) foo::Bar
23053 case PM_CONSTANT_PATH_NODE:
23054 break;
23055 default:
23056 flags &= (uint8_t) ~(PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_COMMAND_DEF);
23057 break;
23058 }
23059 }
23060
23061 if (context_terminator(parser->current_context->context, &parser->current)) {
23062 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
23063 if (
23064 !next_binding_powers.binary ||
23065 binding_power > next_binding_powers.left ||
23066 (PM_NODE_TYPE_P(node, PM_CALL_NODE) && pm_call_node_command_p((pm_call_node_t *) node))
23067 ) {
23068 return node;
23069 }
23070 }
23071 }
23072
23073 return node;
23074}
23075
23080static pm_statements_node_t *
23081wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
23082 if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) {
23083 if (statements == NULL) {
23084 statements = pm_statements_node_create(parser);
23085 }
23086
23087 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
23088 pm_arguments_node_arguments_append(
23089 parser->arena,
23090 arguments,
23091 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)))
23092 );
23093
23094 pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create(
23095 parser,
23096 arguments,
23097 pm_parser_constant_id_constant(parser, "print", 5)
23098 )), true);
23099 }
23100
23101 if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) {
23102 if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) {
23103 if (statements == NULL) {
23104 statements = pm_statements_node_create(parser);
23105 }
23106
23107 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
23108 pm_arguments_node_arguments_append(
23109 parser->arena,
23110 arguments,
23111 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)))
23112 );
23113
23114 pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2));
23115 pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, UP(receiver), "split", arguments);
23116
23117 pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create(
23118 parser,
23119 pm_parser_constant_id_constant(parser, "$F", 2),
23120 UP(call)
23121 );
23122
23123 pm_statements_node_body_prepend(parser->arena, statements, UP(write));
23124 }
23125
23126 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
23127 pm_arguments_node_arguments_append(
23128 parser->arena,
23129 arguments,
23130 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)))
23131 );
23132
23133 if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) {
23134 pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser);
23135 pm_keyword_hash_node_elements_append(parser->arena, keywords, UP(pm_assoc_node_create(
23136 parser,
23137 UP(pm_symbol_node_synthesized_create(parser, "chomp")),
23138 NULL,
23139 UP(pm_true_node_synthesized_create(parser))
23140 )));
23141
23142 pm_arguments_node_arguments_append(parser->arena, arguments, UP(keywords));
23143 pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS);
23144 }
23145
23146 pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
23147 pm_statements_node_body_append(parser, wrapped_statements, UP(pm_while_node_synthesized_create(
23148 parser,
23149 UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))),
23150 statements
23151 )), true);
23152
23153 statements = wrapped_statements;
23154 }
23155
23156 return statements;
23157}
23158
23162static pm_node_t *
23163parse_program(pm_parser_t *parser) {
23164 // If the current scope is NULL, then we want to push a new top level scope.
23165 // The current scope could exist in the event that we are parsing an eval
23166 // and the user has passed into scopes that already exist.
23167 if (parser->current_scope == NULL) {
23168 pm_parser_scope_push(parser, true);
23169 }
23170
23171 pm_node_list_t current_block_exits = { 0 };
23172 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
23173
23174 parser_lex(parser);
23175 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN, 0);
23176
23177 if (statements != NULL && !parser->parsing_eval) {
23178 // If we have statements, then the top-level statement should be
23179 // explicitly checked as well. We have to do this here because
23180 // everywhere else we check all but the last statement.
23181 assert(statements->body.size > 0);
23182 pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]);
23183 }
23184
23185 pm_constant_id_list_t locals;
23186 pm_locals_order(parser, &parser->current_scope->locals, &locals, true);
23187 pm_parser_scope_pop(parser);
23188
23189 // At the top level, see if we need to wrap the statements in a program
23190 // node with a while loop based on the options.
23191 if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
23192 statements = wrap_statements(parser, statements);
23193 } else {
23194 flush_block_exits(parser, previous_block_exits);
23195 }
23196
23197 // If this is an empty file, then we're still going to parse all of the
23198 // statements in order to gather up all of the comments and such. Here we'll
23199 // correct the location information.
23200 if (statements == NULL) {
23201 statements = pm_statements_node_create(parser);
23202 statements->base.location = (pm_location_t) { 0 };
23203 }
23204
23205 return UP(pm_program_node_create(parser, &locals, statements));
23206}
23207
23208/******************************************************************************/
23209/* External functions */
23210/******************************************************************************/
23211
23221static const char *
23222pm_strnstr(const char *big, const char *little, size_t big_length) {
23223 size_t little_length = strlen(little);
23224
23225 for (const char *max = big + big_length - little_length; big <= max; big++) {
23226 if (*big == *little && memcmp(big, little, little_length) == 0) return big;
23227 }
23228
23229 return NULL;
23230}
23231
23232#ifdef _WIN32
23233#define pm_parser_warn_shebang_carriage_return(parser, start, length) ((void) 0)
23234#else
23240static void
23241pm_parser_warn_shebang_carriage_return(pm_parser_t *parser, const uint8_t *start, size_t length) {
23242 if (length > 2 && start[length - 2] == '\r' && start[length - 1] == '\n') {
23243 pm_parser_warn(parser, U32(start - parser->start), U32(length), PM_WARN_SHEBANG_CARRIAGE_RETURN);
23244 }
23245}
23246#endif
23247
23252static void
23253pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const char *engine, size_t length) {
23254 const char *switches = pm_strnstr(engine, " -", length);
23255 if (switches == NULL) return;
23256
23257 pm_options_t next_options = *options;
23258 options->shebang_callback(
23259 &next_options,
23260 (const uint8_t *) (switches + 1),
23261 length - ((size_t) (switches - engine)) - 1,
23262 options->shebang_callback_data
23263 );
23264
23265 size_t encoding_length;
23266 if ((encoding_length = pm_string_length(&next_options.encoding)) > 0) {
23267 const uint8_t *encoding_source = pm_string_source(&next_options.encoding);
23268 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
23269 }
23270
23271 parser->command_line = next_options.command_line;
23272 parser->frozen_string_literal = next_options.frozen_string_literal;
23273}
23274
23278void
23279pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) {
23280 assert(arena != NULL);
23281 assert(source != NULL);
23282
23283 *parser = (pm_parser_t) {
23284 .arena = arena,
23285 .metadata_arena = { 0 },
23286 .node_id = 0,
23287 .lex_state = PM_LEX_STATE_BEG,
23288 .enclosure_nesting = 0,
23289 .lambda_enclosure_nesting = -1,
23290 .brace_nesting = 0,
23291 .do_loop_stack = 0,
23292 .accepts_block_stack = 0,
23293 .lex_modes = {
23294 .index = 0,
23295 .stack = {{ .mode = PM_LEX_DEFAULT }},
23296 .current = &parser->lex_modes.stack[0],
23297 },
23298 .start = source,
23299 .end = source + size,
23300 .previous = { .type = PM_TOKEN_EOF, .start = source, .end = source },
23301 .current = { .type = PM_TOKEN_EOF, .start = source, .end = source },
23302 .next_start = NULL,
23303 .heredoc_end = NULL,
23304 .data_loc = { 0 },
23305 .comment_list = { 0 },
23306 .magic_comment_list = { 0 },
23307 .warning_list = { 0 },
23308 .error_list = { 0 },
23309 .current_scope = NULL,
23310 .current_context = NULL,
23311 .encoding = PM_ENCODING_UTF_8_ENTRY,
23312 .encoding_changed_callback = NULL,
23313 .encoding_comment_start = source,
23314 .lex_callback = { 0 },
23315 .filepath = PM_STRING_EMPTY,
23316 .constant_pool = { 0 },
23317 .line_offsets = { 0 },
23318 .integer = { 0 },
23319 .current_string = PM_STRING_EMPTY,
23320 .start_line = 1,
23321 .explicit_encoding = NULL,
23322 .command_line = 0,
23323 .parsing_eval = false,
23324 .partial_script = false,
23325 .attach_comments = false,
23326 .command_start = true,
23327 .recovering = false,
23328 .continuable = true,
23329 .encoding_locked = false,
23330 .encoding_changed = false,
23331 .pattern_matching_newlines = false,
23332 .in_keyword_arg = false,
23333 .current_block_exits = NULL,
23334 .semantic_token_seen = false,
23335 .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET,
23336 .warn_mismatched_indentation = true
23337 };
23338
23339 /* Pre-size the arenas based on input size to reduce the number of block
23340 * allocations (and the kernel page zeroing they trigger). The ratios were
23341 * measured empirically: AST arena ~3.3x input, metadata arena ~1.1x input.
23342 * The reserve call is a no-op when the capacity is at or below the default
23343 * arena block size, so small inputs don't waste an extra allocation. */
23344 if (size <= SIZE_MAX / 4) pm_arena_reserve(arena, size * 4);
23345 if (size <= SIZE_MAX / 5 * 4) pm_arena_reserve(&parser->metadata_arena, size + size / 4);
23346
23347 /* Initialize the constant pool. Measured across 1532 Ruby stdlib files, the
23348 * bytes/constant ratio has a median of ~56 and a 90th percentile of ~135.
23349 * We use 120 as a balance between over-allocation waste and resize
23350 * frequency. Resizes are cheap with arena allocation, so we lean toward
23351 * under-estimating. */
23352 uint32_t constant_size = ((uint32_t) size) / 120;
23353 pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size < 4 ? 4 : constant_size);
23354
23355 /* Initialize the line offset list. Similar to the constant pool, we are
23356 * going to estimate the number of newlines that we will need based on the
23357 * size of the input. */
23358 size_t newline_size = size / 22;
23359 pm_line_offset_list_init(&parser->metadata_arena, &parser->line_offsets, newline_size < 4 ? 4 : newline_size);
23360
23361 // If options were provided to this parse, establish them here.
23362 if (options != NULL) {
23363 // filepath option
23364 if (pm_string_length(&options->filepath) > 0) {
23365 parser->filepath = options->filepath;
23366 }
23367
23368 // line option
23369 parser->start_line = options->line;
23370
23371 // encoding option
23372 size_t encoding_length = pm_string_length(&options->encoding);
23373 if (encoding_length > 0) {
23374 const uint8_t *encoding_source = pm_string_source(&options->encoding);
23375 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
23376 }
23377
23378 // encoding_locked option
23379 parser->encoding_locked = options->encoding_locked;
23380
23381 // frozen_string_literal option
23382 parser->frozen_string_literal = options->frozen_string_literal;
23383
23384 // command_line option
23385 parser->command_line = options->command_line;
23386
23387 // version option
23388 parser->version = options->version;
23389
23390 // partial_script
23391 parser->partial_script = options->partial_script;
23392
23393 // attach_comments
23394 parser->attach_comments = options->attach_comments;
23395
23396 // scopes option
23397 parser->parsing_eval = options->scopes_count > 0;
23398 if (parser->parsing_eval) parser->warn_mismatched_indentation = false;
23399
23400 for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
23401 const pm_options_scope_t *scope = pm_options_scope(options, scope_index);
23402 pm_parser_scope_push(parser, scope_index == 0);
23403
23404 // Scopes given from the outside are not allowed to have numbered
23405 // parameters.
23406 parser->current_scope->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED;
23407
23408 for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
23409 const pm_string_t *local = pm_options_scope_local(scope, local_index);
23410
23411 const uint8_t *source = pm_string_source(local);
23412 size_t length = pm_string_length(local);
23413
23414 uint8_t *allocated = (uint8_t *) pm_arena_alloc(&parser->metadata_arena, length, 1);
23415 memcpy(allocated, source, length);
23416 pm_parser_local_add_owned(parser, allocated, length);
23417 }
23418 }
23419 }
23420
23421 // Now that we have established the user-provided options, check if
23422 // a version was given and parse as the latest version otherwise.
23423 if (parser->version == PM_OPTIONS_VERSION_UNSET) {
23424 parser->version = PM_OPTIONS_VERSION_LATEST;
23425 }
23426
23427 pm_accepts_block_stack_push(parser, true);
23428
23429 // Skip past the UTF-8 BOM if it exists.
23430 if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) {
23431 parser->current.end += 3;
23432 parser->encoding_comment_start += 3;
23433
23434 if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
23435 parser->encoding = PM_ENCODING_UTF_8_ENTRY;
23436 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
23437 }
23438 }
23439
23440 // If the -x command line flag is set, or the first shebang of the file does
23441 // not include "ruby", then we'll search for a shebang that does include
23442 // "ruby" and start parsing from there.
23443 bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
23444
23445 // If the first two bytes of the source are a shebang, then we will do a bit
23446 // of extra processing.
23447 //
23448 // First, we'll indicate that the encoding comment is at the end of the
23449 // shebang. This means that when a shebang is present the encoding comment
23450 // can begin on the second line.
23451 //
23452 // Second, we will check if the shebang includes "ruby". If it does, then we
23453 // we will start parsing from there. We will also potentially warning the
23454 // user if there is a carriage return at the end of the shebang. We will
23455 // also potentially call the shebang callback if this is the main script to
23456 // allow the caller to parse the shebang and find any command-line options.
23457 // If the shebang does not include "ruby" and this is the main script being
23458 // parsed, then we will start searching the file for a shebang that does
23459 // contain "ruby" as if -x were passed on the command line.
23460 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
23461 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end);
23462
23463 if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') {
23464 const char *engine;
23465
23466 if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) {
23467 if (newline != NULL) {
23468 parser->encoding_comment_start = newline + 1;
23469
23470 if (options == NULL || options->main_script) {
23471 pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1);
23472 }
23473 }
23474
23475 if (options != NULL && options->main_script && options->shebang_callback != NULL) {
23476 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start)));
23477 }
23478
23479 search_shebang = false;
23480 } else if (options != NULL && options->main_script && !parser->parsing_eval) {
23481 search_shebang = true;
23482 }
23483 }
23484
23485 // Here we're going to find the first shebang that includes "ruby" and start
23486 // parsing from there.
23487 if (search_shebang) {
23488 // If a shebang that includes "ruby" is not found, then we're going to a
23489 // a load error to the list of errors on the parser.
23490 bool found_shebang = false;
23491
23492 // This is going to point to the start of each line as we check it.
23493 // We'll maintain a moving window looking at each line at they come.
23494 const uint8_t *cursor = parser->start;
23495
23496 // The newline pointer points to the end of the current line that we're
23497 // considering. If it is NULL, then we're at the end of the file.
23498 const uint8_t *newline = next_newline(cursor, parser->end - cursor);
23499
23500 while (newline != NULL) {
23501 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
23502
23503 cursor = newline + 1;
23504 newline = next_newline(cursor, parser->end - cursor);
23505
23506 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - cursor);
23507 if (length > 2 && cursor[0] == '#' && cursor[1] == '!') {
23508 const char *engine;
23509 if ((engine = pm_strnstr((const char *) cursor, "ruby", length)) != NULL) {
23510 found_shebang = true;
23511
23512 if (newline != NULL) {
23513 pm_parser_warn_shebang_carriage_return(parser, cursor, length + 1);
23514 parser->encoding_comment_start = newline + 1;
23515 }
23516
23517 if (options != NULL && options->shebang_callback != NULL) {
23518 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) cursor)));
23519 }
23520
23521 break;
23522 }
23523 }
23524 }
23525
23526 if (found_shebang) {
23527 parser->previous = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23528 parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23529 } else {
23530 pm_parser_err(parser, 0, 0, PM_ERR_SCRIPT_NOT_FOUND);
23531 pm_line_offset_list_clear(&parser->line_offsets);
23532 }
23533 }
23534
23535 // The encoding comment can start after any amount of inline whitespace, so
23536 // here we'll advance it to the first non-inline-whitespace character so
23537 // that it is ready for future comparisons.
23538 parser->encoding_comment_start += pm_strspn_inline_whitespace(parser->encoding_comment_start, parser->end - parser->encoding_comment_start);
23539}
23540
23549pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) {
23550 pm_parser_t *parser = (pm_parser_t *) xmalloc(sizeof(pm_parser_t));
23551 if (parser == NULL) abort();
23552
23553 pm_parser_init(arena, parser, source, size, options);
23554 return parser;
23555}
23556
23560void
23561pm_parser_cleanup(pm_parser_t *parser) {
23562 pm_string_cleanup(&parser->filepath);
23563 pm_arena_cleanup(&parser->metadata_arena);
23564
23565 while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
23566 lex_mode_pop(parser);
23567 }
23568}
23569
23573void
23575 pm_parser_cleanup(parser);
23576 xfree_sized(parser, sizeof(pm_parser_t));
23577}
23578
23584static bool
23585pm_parse_err_is_fatal(pm_diagnostic_id_t diag_id) {
23586 switch (diag_id) {
23587 case PM_ERR_ARRAY_EXPRESSION_AFTER_STAR:
23588 case PM_ERR_BEGIN_UPCASE_BRACE:
23589 case PM_ERR_CLASS_VARIABLE_BARE:
23590 case PM_ERR_END_UPCASE_BRACE:
23591 case PM_ERR_ESCAPE_INVALID_HEXADECIMAL:
23592 case PM_ERR_ESCAPE_INVALID_UNICODE_LIST:
23593 case PM_ERR_ESCAPE_INVALID_UNICODE_SHORT:
23594 case PM_ERR_EXPRESSION_NOT_WRITABLE:
23595 case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF:
23596 case PM_ERR_FLOAT_PARSE:
23597 case PM_ERR_GLOBAL_VARIABLE_BARE:
23598 case PM_ERR_HASH_KEY:
23599 case PM_ERR_HEREDOC_IDENTIFIER:
23600 case PM_ERR_INSTANCE_VARIABLE_BARE:
23601 case PM_ERR_INVALID_BLOCK_EXIT:
23602 case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT:
23603 case PM_ERR_INVALID_FLOAT_EXPONENT:
23604 case PM_ERR_INVALID_NUMBER_BINARY:
23605 case PM_ERR_INVALID_NUMBER_DECIMAL:
23606 case PM_ERR_INVALID_NUMBER_HEXADECIMAL:
23607 case PM_ERR_INVALID_NUMBER_OCTAL:
23608 case PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING:
23609 case PM_ERR_NO_LOCAL_VARIABLE:
23610 case PM_ERR_PARAMETER_ORDER:
23611 case PM_ERR_STATEMENT_UNDEF:
23612 case PM_ERR_VOID_EXPRESSION:
23613 return true;
23614 default:
23615 return false;
23616 }
23617}
23618
23652static void
23653pm_parse_continuable(pm_parser_t *parser) {
23654 // If there are no errors then there is nothing to continue.
23655 if (parser->error_list.size == 0) {
23656 parser->continuable = false;
23657 return;
23658 }
23659
23660 if (!parser->continuable) return;
23661
23662 size_t source_length = (size_t) (parser->end - parser->start);
23663
23664 // First pass: check if there are any non-stray, non-fatal errors.
23665 bool has_non_stray_error = false;
23666 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23667 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)) {
23668 has_non_stray_error = true;
23669 break;
23670 }
23671 }
23672
23673 // Second pass: check each error. We track the minimum source position
23674 // among non-stray, non-fatal errors seen so far in list order, which
23675 // lets us detect cascade stray tokens.
23676 size_t non_stray_min_start = SIZE_MAX;
23677
23678 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23679 size_t error_start = (size_t) error->location.start;
23680 size_t error_end = error_start + (size_t) error->location.length;
23681 bool at_eof = error_end >= source_length;
23682
23683 // Fatal errors are non-continuable unless they occur at EOF.
23684 if (pm_parse_err_is_fatal(error->diag_id) && !at_eof) {
23685 parser->continuable = false;
23686 return;
23687 }
23688
23689 // Track non-stray, non-fatal error positions in list order.
23690 if (error->diag_id != PM_ERR_UNEXPECTED_TOKEN_IGNORE &&
23691 error->diag_id != PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT) {
23692 if (error_start < non_stray_min_start) non_stray_min_start = error_start;
23693 continue;
23694 }
23695
23696 // This is a stray token. Determine if it is a cascade effect
23697 // of a preceding error or genuinely stray.
23698
23699 // Rule (a): a non-stray error was seen earlier in the list at a
23700 // strictly earlier position — this stray is a cascade effect.
23701 if (non_stray_min_start < error_start) continue;
23702
23703 // Rule (b): this stray is at EOF with valid code before it.
23704 // Single-byte stray tokens at EOF (like `\` for line continuation)
23705 // are likely truncated tokens. Multi-byte stray tokens (like the
23706 // keyword `end`) need additional evidence that they are cascade
23707 // effects (i.e. non-stray errors exist elsewhere).
23708 if (at_eof && error_start > 0) {
23709 // Exception: closing delimiters at EOF are genuinely stray.
23710 if (error->location.length == 1) {
23711 const uint8_t *byte = parser->start + error_start;
23712 if (*byte == ')' || *byte == ']' || *byte == '}') {
23713 parser->continuable = false;
23714 return;
23715 }
23716
23717 // Single-byte non-delimiter stray at EOF: cascade.
23718 continue;
23719 }
23720
23721 // Multi-byte stray at EOF: cascade only if there are
23722 // non-stray errors (evidence of a preceding parse failure).
23723 if (has_non_stray_error) continue;
23724 }
23725
23726 // Rule (c): a stray `=` at the start of a line could be the
23727 // beginning of an embedded document (`=begin`). The remaining
23728 // bytes after `=` parse as an identifier, so the error is not
23729 // at EOF, but the construct is genuinely incomplete.
23730 if (error->location.length == 1) {
23731 const uint8_t *byte = parser->start + error_start;
23732 if (*byte == '=' && (error_start == 0 || *(byte - 1) == '\n')) continue;
23733 }
23734
23735 // This stray token is genuinely non-continuable.
23736 parser->continuable = false;
23737 return;
23738 }
23739}
23740
23744pm_node_t *
23746 pm_node_t *node = parse_program(parser);
23747 pm_parse_continuable(parser);
23748 if (parser->attach_comments) pm_comments_attach(parser, node);
23749 return node;
23750}
23751
23758pm_node_t *
23759pm_parse_stream(pm_parser_t **parser, pm_arena_t *arena, pm_source_t *source, const pm_options_t *options) {
23760 bool eof = pm_source_stream_read(source);
23761
23762 pm_parser_t *tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23763 pm_node_t *node = pm_parse(tmp);
23764
23765 while (!eof && tmp->error_list.size > 0) {
23766 eof = pm_source_stream_read(source);
23767
23768 pm_parser_free(tmp);
23769 pm_arena_cleanup(arena);
23770
23771 tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23772 node = pm_parse(tmp);
23773 }
23774
23775 *parser = tmp;
23776 return node;
23777}
23778
23779#undef PM_CASE_KEYWORD
23780#undef PM_CASE_OPERATOR
23781#undef PM_CASE_WRITABLE
23782#undef PM_STRING_EMPTY
23783
23784// We optionally support serializing to a binary string. For systems that don't
23785// want or need this functionality, it can be turned off with the
23786// PRISM_EXCLUDE_SERIALIZATION define.
23787#ifndef PRISM_EXCLUDE_SERIALIZATION
23788
23789static PRISM_INLINE void
23790pm_serialize_header(pm_buffer_t *buffer) {
23791 pm_buffer_append_string(buffer, "PRISM", 5);
23792 pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR);
23793 pm_buffer_append_byte(buffer, PRISM_VERSION_MINOR);
23794 pm_buffer_append_byte(buffer, PRISM_VERSION_PATCH);
23795 pm_buffer_append_byte(buffer, PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS ? 1 : 0);
23796}
23797
23801void
23802pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
23803 pm_serialize_header(buffer);
23804 pm_serialize_content(parser, node, buffer);
23805 pm_buffer_append_byte(buffer, '\0');
23806}
23807
23812void
23813pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23814 pm_options_t options = { 0 };
23815 pm_options_read(&options, data);
23816
23817 pm_arena_t arena = { 0 };
23818 pm_parser_t parser;
23819 pm_parser_init(&arena, &parser, source, size, &options);
23820
23821 pm_node_t *node = pm_parse(&parser);
23822
23823 pm_serialize_header(buffer);
23824 pm_serialize_content(&parser, node, buffer);
23825 pm_buffer_append_byte(buffer, '\0');
23826
23827 pm_parser_cleanup(&parser);
23828 pm_arena_cleanup(&arena);
23829 pm_options_cleanup(&options);
23830}
23831
23836void
23837pm_serialize_parse_stream(pm_buffer_t *buffer, pm_source_t *source, const char *data) {
23838 pm_arena_t arena = { 0 };
23839 pm_parser_t *parser;
23840 pm_options_t options = { 0 };
23841 pm_options_read(&options, data);
23842
23843 pm_node_t *node = pm_parse_stream(&parser, &arena, source, &options);
23844 pm_serialize_header(buffer);
23845 pm_serialize_content(parser, node, buffer);
23846 pm_buffer_append_byte(buffer, '\0');
23847
23848 pm_parser_free(parser);
23849 pm_arena_cleanup(&arena);
23850 pm_options_cleanup(&options);
23851}
23852
23861int8_t
23862pm_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) {
23863 pm_options_t options = { 0 };
23864 pm_options_read(&options, data);
23865
23866 pm_arena_t arena = { 0 };
23867 pm_parser_t parser;
23868 pm_parser_init(&arena, &parser, source, size, &options);
23869
23870 pm_parse(&parser);
23871
23872 int8_t result = -1;
23873 if (parser.error_list.size > 0) {
23874 const char *encoding_name = parser.encoding->name;
23875 pm_buffer_append_string(buffer, encoding_name, strlen(encoding_name));
23876 pm_buffer_append_byte(buffer, '\0');
23877
23878 result = (int8_t) pm_errors_format(&parser, buffer, format_type);
23879 }
23880
23881 pm_parser_cleanup(&parser);
23882 pm_arena_cleanup(&arena);
23883 pm_options_cleanup(&options);
23884
23885 return result;
23886}
23887
23891void
23892pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23893 pm_options_t options = { 0 };
23894 pm_options_read(&options, data);
23895
23896 pm_arena_t arena = { 0 };
23897 pm_parser_t parser;
23898 pm_parser_init(&arena, &parser, source, size, &options);
23899
23900 pm_parse(&parser);
23901 pm_serialize_header(buffer);
23902 pm_serialize_encoding(parser.encoding, buffer);
23903 pm_buffer_append_varsint(buffer, parser.start_line);
23904 pm_serialize_line_offset_list(&parser.line_offsets, buffer);
23905 pm_serialize_comment_list(&parser.comment_list, buffer);
23906
23907 pm_parser_cleanup(&parser);
23908 pm_arena_cleanup(&arena);
23909 pm_options_cleanup(&options);
23910}
23911
23912#endif
#define PRISM_ALIGNOF
Get the alignment requirement of a type.
Definition align.h:15
pm_comment_type_t
This is the type of a comment that we've found while parsing.
Definition comments.h:18
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.
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:96
#define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
String literals should not be frozen.
Definition options.h:31
#define PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
String literals should be made frozen.
Definition options.h:42
#define PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET
String literals may be frozen or mutable depending on the implementation default.
Definition options.h:37
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:102
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:23549
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:23574
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:23745
#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:18
The version of the Prism library.
#define PRISM_VERSION
The version of the Prism library as a constant string.
Definition version.h:29
#define PRISM_VERSION_PATCH
The patch version of the Prism library as an int.
Definition version.h:24
#define PRISM_VERSION_MINOR
The minor version of the Prism library as an int.
Definition version.h:19
#define PRISM_VERSION_MAJOR
The major version of the Prism library as an int.
Definition version.h:14
The functions related to serializing the AST to a binary format.
Functions for parsing streams.
AndNode.
Definition ast.h:1312
PM_NODE_ALIGNAS struct pm_node * left
AndNode::left.
Definition ast.h:1327
PM_NODE_ALIGNAS struct pm_node * right
AndNode::right.
Definition ast.h:1340
ArgumentsNode.
Definition ast.h:1372
pm_node_t base
The embedded base node.
Definition ast.h:1374
struct pm_node_list arguments
ArgumentsNode::arguments.
Definition ast.h:1384
This is a special out parameter to the parse_arguments_list function that includes opening and closin...
Definition prism.c:1773
pm_node_t * block
The optional block attached to the call.
Definition prism.c:1784
bool has_forwarding
The flag indicating whether this arguments list has forwarding argument.
Definition prism.c:1787
pm_location_t opening_loc
The optional location of the opening parenthesis or bracket.
Definition prism.c:1775
pm_arguments_node_t * arguments
The lazily-allocated optional arguments node.
Definition prism.c:1778
pm_location_t closing_loc
The optional location of the closing parenthesis or bracket.
Definition prism.c:1781
ArrayNode.
Definition ast.h:1402
struct pm_node_list elements
ArrayNode::elements.
Definition ast.h:1411
ArrayPatternNode.
Definition ast.h:1462
PM_NODE_ALIGNAS struct pm_node * constant
ArrayPatternNode::constant.
Definition ast.h:1480
pm_location_t opening_loc
ArrayPatternNode::opening_loc.
Definition ast.h:1520
pm_location_t closing_loc
ArrayPatternNode::closing_loc.
Definition ast.h:1530
AssocNode.
Definition ast.h:1545
PM_NODE_ALIGNAS struct pm_node * value
AssocNode::value.
Definition ast.h:1576
PM_NODE_ALIGNAS struct pm_node * key
AssocNode::key.
Definition ast.h:1563
AssocSplatNode.
Definition ast.h:1601
BeginNode.
Definition ast.h:1668
PM_NODE_ALIGNAS struct pm_else_node * else_clause
BeginNode::else_clause.
Definition ast.h:1710
PM_NODE_ALIGNAS struct pm_ensure_node * ensure_clause
BeginNode::ensure_clause.
Definition ast.h:1720
PM_NODE_ALIGNAS struct pm_statements_node * statements
BeginNode::statements.
Definition ast.h:1690
PM_NODE_ALIGNAS struct pm_rescue_node * rescue_clause
BeginNode::rescue_clause.
Definition ast.h:1700
pm_node_t base
The embedded base node.
Definition ast.h:1670
This struct represents a set of binding powers used for a given token.
Definition prism.c:12726
bool binary
Whether or not this token can be used as a binary operator.
Definition prism.c:12734
pm_binding_power_t left
The left binding power.
Definition prism.c:12728
bool nonassoc
Whether or not this token can be used as non-associative binary operator.
Definition prism.c:12740
pm_binding_power_t right
The right binding power.
Definition prism.c:12731
BlockLocalVariableNode.
Definition ast.h:1785
BlockNode.
Definition ast.h:1812
BlockParametersNode.
Definition ast.h:1940
CallNode.
Definition ast.h:2164
pm_location_t opening_loc
CallNode::opening_loc.
Definition ast.h:2225
pm_location_t closing_loc
CallNode::closing_loc.
Definition ast.h:2245
pm_constant_id_t name
CallNode::name.
Definition ast.h:2205
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
CallNode::arguments.
Definition ast.h:2235
pm_location_t equal_loc
CallNode::equal_loc.
Definition ast.h:2258
pm_location_t call_operator_loc
CallNode::call_operator_loc.
Definition ast.h:2195
pm_location_t message_loc
CallNode::message_loc.
Definition ast.h:2215
PM_NODE_ALIGNAS struct pm_node * block
CallNode::block.
Definition ast.h:2268
PM_NODE_ALIGNAS struct pm_node * receiver
CallNode::receiver.
Definition ast.h:2182
CaseMatchNode.
Definition ast.h:2599
struct pm_node_list conditions
CaseMatchNode::conditions.
Definition ast.h:2621
PM_NODE_ALIGNAS struct pm_else_node * else_clause
CaseMatchNode::else_clause.
Definition ast.h:2631
CaseNode.
Definition ast.h:2668
PM_NODE_ALIGNAS struct pm_else_node * else_clause
CaseNode::else_clause.
Definition ast.h:2700
struct pm_node_list conditions
CaseNode::conditions.
Definition ast.h:2690
ClassVariableReadNode.
Definition ast.h:2957
ClassVariableTargetNode.
Definition ast.h:2985
ClassVariableWriteNode.
Definition ast.h:3007
A list of constant IDs.
ConstantPathNode.
Definition ast.h:3216
ConstantPathTargetNode.
Definition ast.h:3351
ConstantReadNode.
Definition ast.h:3444
ConstantTargetNode.
Definition ast.h:3472
ConstantWriteNode.
Definition ast.h:3494
DefNode.
Definition ast.h:3556
pm_location_t equal_loc
DefNode::equal_loc.
Definition ast.h:3613
PM_NODE_ALIGNAS struct pm_node * body
DefNode::body.
Definition ast.h:3583
DefinedNode.
Definition ast.h:3633
ElseNode.
Definition ast.h:3670
PM_NODE_ALIGNAS struct pm_statements_node * statements
ElseNode::statements.
Definition ast.h:3682
EnsureNode.
Definition ast.h:3765
PM_NODE_ALIGNAS struct pm_statements_node * statements
EnsureNode::statements.
Definition ast.h:3777
FindPatternNode.
Definition ast.h:3844
pm_location_t opening_loc
FindPatternNode::opening_loc.
Definition ast.h:3908
PM_NODE_ALIGNAS struct pm_node * constant
FindPatternNode::constant.
Definition ast.h:3856
pm_location_t closing_loc
FindPatternNode::closing_loc.
Definition ast.h:3921
FlipFlopNode.
Definition ast.h:3939
FloatNode.
Definition ast.h:3971
double value
FloatNode::value.
Definition ast.h:3980
pm_node_t base
The embedded base node.
Definition ast.h:3973
ForNode.
Definition ast.h:3995
ForwardingParameterNode.
Definition ast.h:4104
GlobalVariableReadNode.
Definition ast.h:4277
GlobalVariableTargetNode.
Definition ast.h:4305
GlobalVariableWriteNode.
Definition ast.h:4327
HashNode.
Definition ast.h:4388
struct pm_node_list elements
HashNode::elements.
Definition ast.h:4413
HashPatternNode.
Definition ast.h:4447
PM_NODE_ALIGNAS struct pm_node * constant
HashPatternNode::constant.
Definition ast.h:4462
pm_location_t opening_loc
HashPatternNode::opening_loc.
Definition ast.h:4501
pm_location_t closing_loc
HashPatternNode::closing_loc.
Definition ast.h:4514
IfNode.
Definition ast.h:4535
PM_NODE_ALIGNAS struct pm_statements_node * statements
IfNode::statements.
Definition ast.h:4594
PM_NODE_ALIGNAS struct pm_node * subsequent
IfNode::subsequent.
Definition ast.h:4613
ImaginaryNode.
Definition ast.h:4640
InNode.
Definition ast.h:4716
PM_NODE_ALIGNAS struct pm_statements_node * statements
InNode::statements.
Definition ast.h:4728
InstanceVariableReadNode.
Definition ast.h:5119
InstanceVariableTargetNode.
Definition ast.h:5147
InstanceVariableWriteNode.
Definition ast.h:5169
IntegerNode.
Definition ast.h:5236
pm_integer_t value
IntegerNode::value.
Definition ast.h:5245
pm_node_t base
The embedded base node.
Definition ast.h:5238
bool negative
Whether or not the integer is negative.
Definition integer.h:38
InterpolatedMatchLastLineNode.
Definition ast.h:5273
InterpolatedRegularExpressionNode.
Definition ast.h:5318
InterpolatedStringNode.
Definition ast.h:5354
pm_node_t base
The embedded base node.
Definition ast.h:5356
pm_location_t opening_loc
InterpolatedStringNode::opening_loc.
Definition ast.h:5361
InterpolatedSymbolNode.
Definition ast.h:5386
InterpolatedXStringNode.
Definition ast.h:5418
pm_location_t opening_loc
InterpolatedXStringNode::opening_loc.
Definition ast.h:5425
pm_node_t base
The embedded base node.
Definition ast.h:5420
struct pm_node_list parts
InterpolatedXStringNode::parts.
Definition ast.h:5430
KeywordHashNode.
Definition ast.h:5487
A Ruby keyword, along with the lex state that it transitions to and the token types that it produces.
Definition prism.c:8535
LambdaNode.
Definition ast.h:5545
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:5723
uint32_t depth
LocalVariableReadNode::depth.
Definition ast.h:5753
pm_constant_id_t name
LocalVariableReadNode::name.
Definition ast.h:5740
LocalVariableTargetNode.
Definition ast.h:5771
LocalVariableWriteNode.
Definition ast.h:5798
uint32_t depth
LocalVariableWriteNode::depth.
Definition ast.h:5824
pm_constant_id_t name
LocalVariableWriteNode::name.
Definition ast.h:5811
This struct represents a slice in the source code, defined by an offset and a length.
Definition ast.h:575
uint32_t start
The offset of the location from the start of the source.
Definition ast.h:577
uint32_t length
The length of the location.
Definition ast.h:580
MatchLastLineNode.
Definition ast.h:5889
MatchWriteNode.
Definition ast.h:6044
struct pm_node_list targets
MatchWriteNode::targets.
Definition ast.h:6056
MultiTargetNode.
Definition ast.h:6123
pm_location_t lparen_loc
MultiTargetNode::lparen_loc.
Definition ast.h:6180
struct pm_node_list lefts
MultiTargetNode::lefts.
Definition ast.h:6140
pm_location_t rparen_loc
MultiTargetNode::rparen_loc.
Definition ast.h:6190
MultiWriteNode.
Definition ast.h:6205
A list of nodes in the source, most often used for lists of children.
Definition ast.h:588
size_t size
The number of nodes in the list.
Definition ast.h:590
struct pm_node ** nodes
The nodes in the list.
Definition ast.h:596
This is the base structure that represents a node in the syntax tree.
Definition ast.h:1086
pm_node_type_t type
This represents the type of the node.
Definition ast.h:1091
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1109
OptionalParameterNode.
Definition ast.h:6499
OrNode.
Definition ast.h:6536
PM_NODE_ALIGNAS struct pm_node * right
OrNode::right.
Definition ast.h:6564
PM_NODE_ALIGNAS struct pm_node * left
OrNode::left.
Definition ast.h:6551
ParametersNode.
Definition ast.h:6590
PM_NODE_ALIGNAS struct pm_node * block
ParametersNode::block.
Definition ast.h:6627
PM_NODE_ALIGNAS struct pm_node * rest
ParametersNode::rest.
Definition ast.h:6607
PM_NODE_ALIGNAS struct pm_node * keyword_rest
ParametersNode::keyword_rest.
Definition ast.h:6622
ParenthesesNode.
Definition ast.h:6645
PM_NODE_ALIGNAS struct pm_node * body
ParenthesesNode::body.
Definition ast.h:6652
PostExecutionNode.
Definition ast.h:6771
PreExecutionNode.
Definition ast.h:6808
RangeNode.
Definition ast.h:6875
PM_NODE_ALIGNAS struct pm_node * right
RangeNode::right.
Definition ast.h:6904
PM_NODE_ALIGNAS struct pm_node * left
RangeNode::left.
Definition ast.h:6890
RationalNode.
Definition ast.h:6932
pm_node_t base
The embedded base node.
Definition ast.h:6934
pm_integer_t numerator
RationalNode::numerator.
Definition ast.h:6943
State for processing named capture groups found during regexp parsing.
Definition prism.c:21727
bool shared
Whether the content being parsed points into the original source.
Definition prism.c:21742
pm_constant_id_set_t names
The set of capture names found so far.
Definition prism.c:21735
pm_call_node_t * call
The call node wrapping the regular expression node (for =~).
Definition prism.c:21729
pm_match_write_node_t * match
The match write node being built, or NULL if no captures found yet.
Definition prism.c:21732
In order to properly set a regular expression's encoding and to validate the byte sequence for the un...
Definition prism.c:9909
pm_buffer_t regexp_buffer
The buffer holding the regexp source.
Definition prism.c:9914
pm_token_buffer_t base
The embedded base buffer.
Definition prism.c:9911
RegularExpressionNode.
Definition ast.h:6997
pm_string_t unescaped
RegularExpressionNode::unescaped.
Definition ast.h:7019
RequiredParameterNode.
Definition ast.h:7069
RescueModifierNode.
Definition ast.h:7091
PM_NODE_ALIGNAS struct pm_node * rescue_expression
RescueModifierNode::rescue_expression.
Definition ast.h:7108
RescueNode.
Definition ast.h:7128
PM_NODE_ALIGNAS struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7165
pm_location_t then_keyword_loc
RescueNode::then_keyword_loc.
Definition ast.h:7155
SplatNode.
Definition ast.h:7418
PM_NODE_ALIGNAS struct pm_node * expression
SplatNode::expression.
Definition ast.h:7430
StatementsNode.
Definition ast.h:7445
struct pm_node_list body
StatementsNode::body.
Definition ast.h:7452
pm_node_t base
The embedded base node.
Definition ast.h:7447
StringNode.
Definition ast.h:7479
pm_node_t base
The embedded base node.
Definition ast.h:7481
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7501
pm_location_t content_loc
StringNode::content_loc.
Definition ast.h:7491
pm_location_t closing_loc
StringNode::closing_loc.
Definition ast.h:7496
pm_location_t opening_loc
StringNode::opening_loc.
Definition ast.h:7486
A generic string type that can have various ownership semantics.
Definition stringy.h:18
const uint8_t * source
A pointer to the start of the string.
Definition stringy.h:20
enum pm_string_t::@118 type
The type of the string.
size_t length
The length of the string in bytes of memory.
Definition stringy.h:23
SuperNode.
Definition ast.h:7521
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
SuperNode::arguments.
Definition ast.h:7540
pm_location_t lparen_loc
SuperNode::lparen_loc.
Definition ast.h:7533
PM_NODE_ALIGNAS struct pm_node * block
SuperNode::block.
Definition ast.h:7550
SymbolNode.
Definition ast.h:7573
pm_location_t content_loc
SymbolNode::content_loc.
Definition ast.h:7585
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7595
When we're lexing certain types (strings, symbols, lists, etc.) we have string content associated wit...
Definition prism.c:9883
pm_buffer_t buffer
The buffer that we're using to keep track of the string content.
Definition prism.c:9888
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:9894
This struct represents a token in the Ruby source.
Definition ast.h:547
const uint8_t * end
A pointer to the end location of the token in the source.
Definition ast.h:555
const uint8_t * start
A pointer to the start location of the token in the source.
Definition ast.h:552
pm_token_type_t type
The type of the token.
Definition ast.h:549
UndefNode.
Definition ast.h:7627
UnlessNode.
Definition ast.h:7657
PM_NODE_ALIGNAS struct pm_statements_node * statements
UnlessNode::statements.
Definition ast.h:7706
PM_NODE_ALIGNAS struct pm_else_node * else_clause
UnlessNode::else_clause.
Definition ast.h:7716
UntilNode.
Definition ast.h:7747
WhenNode.
Definition ast.h:7791
PM_NODE_ALIGNAS struct pm_statements_node * statements
WhenNode::statements.
Definition ast.h:7813
WhileNode.
Definition ast.h:7834
XStringNode.
Definition ast.h:7880
YieldNode.
Definition ast.h:7917
pm_location_t lparen_loc
YieldNode::lparen_loc.
Definition ast.h:7929
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
YieldNode::arguments.
Definition ast.h:7934
#define PRISM_UNUSED
GCC will warn if you specify a function or parameter that is unused at runtime.
Definition unused.h:13