Ruby 4.1.0dev (2026-10-15 revision 8a060f90ba36441cfeb509eacb10f609314f5bfe)
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
706static bool
707pm_parser_scope_push(pm_parser_t *parser, bool closed) {
708 pm_scope_t *scope = (pm_scope_t *) xmalloc(sizeof(pm_scope_t));
709 if (scope == NULL) return false;
710
711 *scope = (pm_scope_t) {
712 .previous = parser->current_scope,
713 .locals = { 0 },
714 .parameters = PM_SCOPE_PARAMETERS_NONE,
715 .implicit_parameters = { 0 },
716 .shareable_constant = parser->current_scope == NULL ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant,
717 .closed = closed
718 };
719
720 parser->current_scope = scope;
721 return true;
722}
723
728static bool
729pm_parser_scope_toplevel_p(pm_parser_t *parser) {
730 pm_scope_t *scope = parser->current_scope;
731
732 do {
733 if (scope->previous == NULL) return true;
734 if (scope->closed) return false;
735 } while ((scope = scope->previous) != NULL);
736
737 assert(false && "unreachable");
738 return true;
739}
740
744static pm_scope_t *
745pm_parser_scope_find(pm_parser_t *parser, uint32_t depth) {
746 pm_scope_t *scope = parser->current_scope;
747
748 while (depth-- > 0) {
749 assert(scope != NULL);
750 scope = scope->previous;
751 }
752
753 return scope;
754}
755
756typedef enum {
757 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS,
758 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT,
759 PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL
760} pm_scope_forwarding_param_check_result_t;
761
762static pm_scope_forwarding_param_check_result_t
763pm_parser_scope_forwarding_param_check(pm_parser_t *parser, const uint8_t mask) {
764 pm_scope_t *scope = parser->current_scope;
765 bool conflict = false;
766
767 while (scope != NULL) {
768 if (scope->parameters & mask) {
769 if (scope->closed) {
770 if (conflict) {
771 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT;
772 } else {
773 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS;
774 }
775 }
776
777 conflict = true;
778 }
779
780 if (scope->closed) break;
781 scope = scope->previous;
782 }
783
784 return PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL;
785}
786
787static void
788pm_parser_scope_forwarding_block_check(pm_parser_t *parser, const pm_token_t * token) {
789 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_BLOCK)) {
790 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
791 // Pass.
792 break;
793 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
794 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_AMPERSAND);
795 break;
796 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
797 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_AMPERSAND);
798 break;
799 }
800}
801
802static void
803pm_parser_scope_forwarding_positionals_check(pm_parser_t *parser, const pm_token_t * token) {
804 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS)) {
805 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
806 // Pass.
807 break;
808 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
809 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR);
810 break;
811 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
812 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR);
813 break;
814 }
815}
816
817static void
818pm_parser_scope_forwarding_all_check(pm_parser_t *parser, const pm_token_t *token) {
819 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_ALL)) {
820 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
821 // Pass.
822 break;
823 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
824 // This shouldn't happen, because ... is not allowed in the
825 // declaration of blocks. If we get here, we assume we already have
826 // an error for this.
827 break;
828 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
829 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES);
830 break;
831 }
832}
833
834static void
835pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t * token) {
836 switch (pm_parser_scope_forwarding_param_check(parser, PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS)) {
837 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_PASS:
838 // Pass.
839 break;
840 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_CONFLICT:
841 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_CONFLICT_STAR_STAR);
842 break;
843 case PM_SCOPE_FORWARDING_PARAM_CHECK_RESULT_FAIL:
844 pm_parser_err_token(parser, token, PM_ERR_ARGUMENT_NO_FORWARDING_STAR_STAR);
845 break;
846 }
847}
848
852static PRISM_INLINE pm_shareable_constant_value_t
853pm_parser_scope_shareable_constant_get(pm_parser_t *parser) {
854 return parser->current_scope->shareable_constant;
855}
856
861static void
862pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constant_value_t shareable_constant) {
863 pm_scope_t *scope = parser->current_scope;
864
865 do {
866 scope->shareable_constant = shareable_constant;
867 } while (!scope->closed && (scope = scope->previous) != NULL);
868}
869
870/******************************************************************************/
871/* Local variable-related functions */
872/******************************************************************************/
873
877#define PM_LOCALS_HASH_THRESHOLD 5
878
879static void
880pm_locals_free(pm_locals_t *locals) {
881 if (locals->capacity > 0) {
882 xfree_sized(locals->locals, locals->capacity * sizeof(pm_local_t));
883 }
884}
885
890static void
891pm_locals_resize(pm_locals_t *locals) {
892 uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2);
893 assert(next_capacity > locals->capacity);
894
895 pm_local_t *next_locals = xcalloc(next_capacity, sizeof(pm_local_t));
896 if (next_locals == NULL) abort();
897
898 if (next_capacity < PM_LOCALS_HASH_THRESHOLD) {
899 if (locals->size > 0) {
900 memcpy(next_locals, locals->locals, locals->size * sizeof(pm_local_t));
901 }
902 } else {
903 // If we just switched from a list to a hash, then we need to fill in
904 // the hash values of all of the locals.
905 bool hash_needed = (locals->capacity <= PM_LOCALS_HASH_THRESHOLD);
906 uint32_t mask = next_capacity - 1;
907
908 for (uint32_t index = 0; index < locals->capacity; index++) {
909 pm_local_t *local = &locals->locals[index];
910
911 if (local->name != PM_CONSTANT_ID_UNSET) {
912 if (hash_needed) local->hash = pm_constant_id_hash(local->name);
913
914 uint32_t hash = local->hash;
915 while (next_locals[hash & mask].name != PM_CONSTANT_ID_UNSET) hash++;
916 next_locals[hash & mask] = *local;
917 }
918 }
919 }
920
921 pm_locals_free(locals);
922 locals->locals = next_locals;
923 locals->capacity = next_capacity;
924}
925
941static bool
942pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint32_t length, uint32_t reads) {
943 if (locals->size >= (locals->capacity / 4 * 3)) {
944 pm_locals_resize(locals);
945 }
946
947 locals->bloom |= (1u << (name & 31));
948
949 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
950 for (uint32_t index = 0; index < locals->capacity; index++) {
951 pm_local_t *local = &locals->locals[index];
952
953 if (local->name == PM_CONSTANT_ID_UNSET) {
954 *local = (pm_local_t) {
955 .name = name,
956 .location = { .start = start, .length = length },
957 .index = locals->size++,
958 .reads = reads,
959 .hash = 0
960 };
961 return true;
962 } else if (local->name == name) {
963 return false;
964 }
965 }
966 } else {
967 uint32_t mask = locals->capacity - 1;
968 uint32_t hash = pm_constant_id_hash(name);
969 uint32_t initial_hash = hash;
970
971 do {
972 pm_local_t *local = &locals->locals[hash & mask];
973
974 if (local->name == PM_CONSTANT_ID_UNSET) {
975 *local = (pm_local_t) {
976 .name = name,
977 .location = { .start = start, .length = length },
978 .index = locals->size++,
979 .reads = reads,
980 .hash = initial_hash
981 };
982 return true;
983 } else if (local->name == name) {
984 return false;
985 } else {
986 hash++;
987 }
988 } while ((hash & mask) != initial_hash);
989 }
990
991 assert(false && "unreachable");
992 return true;
993}
994
999static uint32_t
1000pm_locals_find(pm_locals_t *locals, pm_constant_id_t name) {
1001 if (!(locals->bloom & (1u << (name & 31)))) return UINT32_MAX;
1002
1003 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
1004 for (uint32_t index = 0; index < locals->size; index++) {
1005 pm_local_t *local = &locals->locals[index];
1006 if (local->name == name) return index;
1007 }
1008 } else {
1009 uint32_t mask = locals->capacity - 1;
1010 uint32_t hash = pm_constant_id_hash(name);
1011 uint32_t initial_hash = hash & mask;
1012
1013 do {
1014 pm_local_t *local = &locals->locals[hash & mask];
1015
1016 if (local->name == PM_CONSTANT_ID_UNSET) {
1017 return UINT32_MAX;
1018 } else if (local->name == name) {
1019 return hash & mask;
1020 } else {
1021 hash++;
1022 }
1023 } while ((hash & mask) != initial_hash);
1024 }
1025
1026 return UINT32_MAX;
1027}
1028
1033static void
1034pm_locals_read(pm_locals_t *locals, pm_constant_id_t name) {
1035 uint32_t index = pm_locals_find(locals, name);
1036 assert(index != UINT32_MAX);
1037
1038 pm_local_t *local = &locals->locals[index];
1039 assert(local->reads < UINT32_MAX);
1040
1041 local->reads++;
1042}
1043
1048static void
1049pm_locals_unread(pm_locals_t *locals, pm_constant_id_t name) {
1050 uint32_t index = pm_locals_find(locals, name);
1051 assert(index != UINT32_MAX);
1052
1053 pm_local_t *local = &locals->locals[index];
1054 assert(local->reads > 0);
1055
1056 local->reads--;
1057}
1058
1062static uint32_t
1063pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) {
1064 uint32_t index = pm_locals_find(locals, name);
1065 assert(index != UINT32_MAX);
1066
1067 return locals->locals[index].reads;
1068}
1069
1078static void
1079pm_locals_order(pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) {
1080 pm_constant_id_list_init_capacity(parser->arena, list, locals->size);
1081
1082 // If we're still below the threshold for switching to a hash, then we only
1083 // need to loop over the locals until we hit the size because the locals are
1084 // stored in a list.
1085 uint32_t capacity = locals->capacity < PM_LOCALS_HASH_THRESHOLD ? locals->size : locals->capacity;
1086
1087 // We will only warn for unused variables if we're not at the top level, or
1088 // if we're parsing a file outside of eval or -e.
1089 bool warn_unused = !toplevel || (!parser->parsing_eval && !PM_PARSER_COMMAND_LINE_OPTION_E(parser));
1090
1091 for (uint32_t index = 0; index < capacity; index++) {
1092 pm_local_t *local = &locals->locals[index];
1093
1094 if (local->name != PM_CONSTANT_ID_UNSET) {
1095 pm_constant_id_list_insert(list, (size_t) local->index, local->name);
1096
1097 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))) {
1098 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name);
1099
1100 if (constant->length >= 1 && *constant->start != '_') {
1101 PM_PARSER_WARN_FORMAT(
1102 parser,
1103 local->location.start,
1104 local->location.length,
1105 PM_WARN_UNUSED_LOCAL_VARIABLE,
1106 (int) constant->length,
1107 (const char *) constant->start
1108 );
1109 }
1110 }
1111 }
1112 }
1113}
1114
1115/******************************************************************************/
1116/* Node-related functions */
1117/******************************************************************************/
1118
1123pm_parser_constant_id_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
1124 /* Fast path: if this is the same token as the last lookup (same pointer
1125 * range), return the cached result. */
1126 if (start == parser->constant_cache.start && end == parser->constant_cache.end) {
1127 return parser->constant_cache.id;
1128 }
1129
1130 pm_constant_id_t id = pm_constant_pool_insert_shared(&parser->metadata_arena, &parser->constant_pool, start, (size_t) (end - start));
1131
1132 parser->constant_cache.start = start;
1133 parser->constant_cache.end = end;
1134 parser->constant_cache.id = id;
1135
1136 return id;
1137}
1138
1143pm_parser_constant_id_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
1144 return pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, start, length);
1145}
1146
1151pm_parser_constant_id_constant(pm_parser_t *parser, const char *start, size_t length) {
1152 return pm_constant_pool_insert_constant(&parser->metadata_arena, &parser->constant_pool, (const uint8_t *) start, length);
1153}
1154
1159pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
1160 return pm_parser_constant_id_raw(parser, token->start, token->end);
1161}
1162
1167#define PM_CASE_VOID_VALUE PM_RETURN_NODE: case PM_BREAK_NODE: case PM_NEXT_NODE: \
1168 case PM_REDO_NODE: case PM_RETRY_NODE: case PM_MATCH_REQUIRED_NODE
1169
1175static pm_node_t *
1176pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
1177 pm_node_t *void_node = NULL;
1178
1179 while (node != NULL) {
1180 switch (PM_NODE_TYPE(node)) {
1181 case PM_CASE_VOID_VALUE:
1182 return void_node != NULL ? void_node : node;
1183 case PM_MATCH_PREDICATE_NODE:
1184 return NULL;
1185 case PM_BEGIN_NODE: {
1186 pm_begin_node_t *cast = (pm_begin_node_t *) node;
1187
1188 if (cast->ensure_clause != NULL) {
1189 if (cast->rescue_clause != NULL) {
1190 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->rescue_clause));
1191 if (vn != NULL) return vn;
1192 }
1193
1194 if (cast->statements != NULL) {
1195 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1196 if (vn != NULL) return vn;
1197 }
1198
1199 node = UP(cast->ensure_clause);
1200 } else if (cast->rescue_clause != NULL) {
1201 // https://bugs.ruby-lang.org/issues/21669
1202 if (cast->else_clause == NULL || parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1203 if (cast->statements == NULL) return NULL;
1204
1205 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1206 if (vn == NULL) return NULL;
1207 if (void_node == NULL) void_node = vn;
1208 }
1209
1210 for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) {
1211 pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements));
1212
1213 if (vn == NULL) {
1214 // https://bugs.ruby-lang.org/issues/21669
1215 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1216 return NULL;
1217 }
1218 void_node = NULL;
1219 break;
1220 }
1221 }
1222
1223 if (cast->else_clause != NULL) {
1224 node = UP(cast->else_clause);
1225
1226 // https://bugs.ruby-lang.org/issues/21669
1227 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1228 pm_node_t *vn = pm_check_value_expression(parser, node);
1229 if (vn != NULL) return vn;
1230 }
1231 } else {
1232 return void_node;
1233 }
1234 } else {
1235 node = UP(cast->statements);
1236 }
1237
1238 break;
1239 }
1240 case PM_CASE_NODE: {
1241 // https://bugs.ruby-lang.org/issues/21669
1242 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1243 return NULL;
1244 }
1245
1246 pm_case_node_t *cast = (pm_case_node_t *) node;
1247 if (cast->else_clause == NULL) return NULL;
1248
1249 pm_node_t *condition;
1250 PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1251 assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
1252
1253 pm_when_node_t *cast = (pm_when_node_t *) condition;
1254 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1255 if (vn == NULL) return NULL;
1256 if (void_node == NULL) void_node = vn;
1257 }
1258
1259 node = UP(cast->else_clause);
1260 break;
1261 }
1262 case PM_CASE_MATCH_NODE: {
1263 // https://bugs.ruby-lang.org/issues/21669
1264 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1265 return NULL;
1266 }
1267
1269 if (cast->else_clause == NULL) return NULL;
1270
1271 pm_node_t *condition;
1272 PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1273 assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
1274
1275 pm_in_node_t *cast = (pm_in_node_t *) condition;
1276 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1277 if (vn == NULL) return NULL;
1278 if (void_node == NULL) void_node = vn;
1279 }
1280
1281 node = UP(cast->else_clause);
1282 break;
1283 }
1284 case PM_ENSURE_NODE: {
1285 pm_ensure_node_t *cast = (pm_ensure_node_t *) node;
1286 node = UP(cast->statements);
1287 break;
1288 }
1289 case PM_PARENTHESES_NODE: {
1291 node = UP(cast->body);
1292 break;
1293 }
1294 case PM_STATEMENTS_NODE: {
1296
1297 // https://bugs.ruby-lang.org/issues/21669
1298 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1299 pm_node_t *body_part;
1300 PM_NODE_LIST_FOREACH(&cast->body, index, body_part) {
1301 switch (PM_NODE_TYPE(body_part)) {
1302 case PM_CASE_VOID_VALUE:
1303 if (void_node == NULL) {
1304 void_node = body_part;
1305 }
1306 return void_node;
1307 default: break;
1308 }
1309 }
1310 }
1311
1312 node = cast->body.nodes[cast->body.size - 1];
1313 break;
1314 }
1315 case PM_IF_NODE: {
1316 pm_if_node_t *cast = (pm_if_node_t *) node;
1317 if (cast->statements == NULL || cast->subsequent == NULL) {
1318 return NULL;
1319 }
1320 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1321 if (vn == NULL) {
1322 return NULL;
1323 }
1324 if (void_node == NULL) {
1325 void_node = vn;
1326 }
1327 node = cast->subsequent;
1328 break;
1329 }
1330 case PM_UNLESS_NODE: {
1331 pm_unless_node_t *cast = (pm_unless_node_t *) node;
1332 if (cast->statements == NULL || cast->else_clause == NULL) {
1333 return NULL;
1334 }
1335 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1336 if (vn == NULL) {
1337 return NULL;
1338 }
1339 if (void_node == NULL) {
1340 void_node = vn;
1341 }
1342 node = UP(cast->else_clause);
1343 break;
1344 }
1345 case PM_ELSE_NODE: {
1346 pm_else_node_t *cast = (pm_else_node_t *) node;
1347 node = UP(cast->statements);
1348 break;
1349 }
1350 case PM_AND_NODE:
1351 case PM_OR_NODE:
1352 // The left operand of an and/or node was already checked for a
1353 // value when the node was created, so descending into it again
1354 // would re-report the same void value and, in a chain such as
1355 // `a && a && ...`, walk the whole left branch on every operator,
1356 // which is quadratic in the length of the chain.
1357 return NULL;
1358 case PM_LOCAL_VARIABLE_WRITE_NODE: {
1360
1361 pm_scope_t *scope = parser->current_scope;
1362 for (uint32_t depth = 0; depth < cast->depth; depth++) scope = scope->previous;
1363
1364 pm_locals_read(&scope->locals, cast->name);
1365 return NULL;
1366 }
1367 default:
1368 return NULL;
1369 }
1370 }
1371
1372 return NULL;
1373}
1374
1375static PRISM_INLINE void
1376pm_assert_value_expression(pm_parser_t *parser, pm_node_t *node) {
1377 pm_node_t *void_node = pm_check_value_expression(parser, node);
1378 if (void_node != NULL) {
1379 pm_parser_err_node(parser, void_node, PM_ERR_VOID_EXPRESSION);
1380 }
1381}
1382
1386static void
1387pm_void_statement_check(pm_parser_t *parser, const pm_node_t *node) {
1388 const char *type = NULL;
1389 int length = 0;
1390
1391 switch (PM_NODE_TYPE(node)) {
1392 case PM_BACK_REFERENCE_READ_NODE:
1393 case PM_CLASS_VARIABLE_READ_NODE:
1394 case PM_GLOBAL_VARIABLE_READ_NODE:
1395 case PM_INSTANCE_VARIABLE_READ_NODE:
1396 case PM_LOCAL_VARIABLE_READ_NODE:
1397 case PM_NUMBERED_REFERENCE_READ_NODE:
1398 type = "a variable";
1399 length = 10;
1400 break;
1401 case PM_CALL_NODE: {
1402 const pm_call_node_t *cast = (const pm_call_node_t *) node;
1403 if (cast->call_operator_loc.length > 0 || cast->message_loc.length == 0) break;
1404
1405 const pm_constant_t *message = pm_constant_pool_id_to_constant(&parser->constant_pool, cast->name);
1406 switch (message->length) {
1407 case 1:
1408 switch (message->start[0]) {
1409 case '+':
1410 case '-':
1411 case '*':
1412 case '/':
1413 case '%':
1414 case '|':
1415 case '^':
1416 case '&':
1417 case '>':
1418 case '<':
1419 type = (const char *) message->start;
1420 length = 1;
1421 break;
1422 }
1423 break;
1424 case 2:
1425 switch (message->start[1]) {
1426 case '=':
1427 if (message->start[0] == '<' || message->start[0] == '>' || message->start[0] == '!' || message->start[0] == '=') {
1428 type = (const char *) message->start;
1429 length = 2;
1430 }
1431 break;
1432 case '@':
1433 if (message->start[0] == '+' || message->start[0] == '-') {
1434 type = (const char *) message->start;
1435 length = 2;
1436 }
1437 break;
1438 case '*':
1439 if (message->start[0] == '*') {
1440 type = (const char *) message->start;
1441 length = 2;
1442 }
1443 break;
1444 }
1445 break;
1446 case 3:
1447 if (memcmp(message->start, "<=>", 3) == 0) {
1448 type = "<=>";
1449 length = 3;
1450 }
1451 break;
1452 }
1453
1454 break;
1455 }
1456 case PM_CONSTANT_PATH_NODE:
1457 type = "::";
1458 length = 2;
1459 break;
1460 case PM_CONSTANT_READ_NODE:
1461 type = "a constant";
1462 length = 10;
1463 break;
1464 case PM_DEFINED_NODE:
1465 type = "defined?";
1466 length = 8;
1467 break;
1468 case PM_FALSE_NODE:
1469 type = "false";
1470 length = 5;
1471 break;
1472 case PM_FLOAT_NODE:
1473 case PM_IMAGINARY_NODE:
1474 case PM_INTEGER_NODE:
1475 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1476 case PM_INTERPOLATED_STRING_NODE:
1477 case PM_RATIONAL_NODE:
1478 case PM_REGULAR_EXPRESSION_NODE:
1479 case PM_SOURCE_ENCODING_NODE:
1480 case PM_SOURCE_FILE_NODE:
1481 case PM_SOURCE_LINE_NODE:
1482 case PM_STRING_NODE:
1483 case PM_SYMBOL_NODE:
1484 type = "a literal";
1485 length = 9;
1486 break;
1487 case PM_NIL_NODE:
1488 type = "nil";
1489 length = 3;
1490 break;
1491 case PM_RANGE_NODE: {
1492 const pm_range_node_t *cast = (const pm_range_node_t *) node;
1493
1494 if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) {
1495 type = "...";
1496 length = 3;
1497 } else {
1498 type = "..";
1499 length = 2;
1500 }
1501
1502 break;
1503 }
1504 case PM_SELF_NODE:
1505 type = "self";
1506 length = 4;
1507 break;
1508 case PM_TRUE_NODE:
1509 type = "true";
1510 length = 4;
1511 break;
1512 default:
1513 break;
1514 }
1515
1516 if (type != NULL) {
1517 PM_PARSER_WARN_NODE_FORMAT(parser, node, PM_WARN_VOID_STATEMENT, length, type);
1518 }
1519}
1520
1525static void
1526pm_void_statements_check(pm_parser_t *parser, const pm_statements_node_t *node, bool last_value) {
1527 assert(node->body.size > 0);
1528 const size_t size = node->body.size - (last_value ? 1 : 0);
1529 for (size_t index = 0; index < size; index++) {
1530 pm_void_statement_check(parser, node->body.nodes[index]);
1531 }
1532}
1533
1539typedef enum {
1540 PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL,
1541 PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP,
1542 PM_CONDITIONAL_PREDICATE_TYPE_NOT
1543} pm_conditional_predicate_type_t;
1544
1548static void
1549pm_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) {
1550 switch (type) {
1551 case PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL:
1552 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "condition");
1553 break;
1554 case PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP:
1555 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "flip-flop");
1556 break;
1557 case PM_CONDITIONAL_PREDICATE_TYPE_NOT:
1558 break;
1559 }
1560}
1561
1566static bool
1567pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) {
1568 switch (PM_NODE_TYPE(node)) {
1569 case PM_ARRAY_NODE: {
1570 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1571
1572 const pm_array_node_t *cast = (const pm_array_node_t *) node;
1573 for (size_t index = 0; index < cast->elements.size; index++) {
1574 if (!pm_conditional_predicate_warn_write_literal_p(cast->elements.nodes[index])) return false;
1575 }
1576
1577 return true;
1578 }
1579 case PM_HASH_NODE: {
1580 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1581
1582 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
1583 for (size_t index = 0; index < cast->elements.size; index++) {
1584 const pm_node_t *element = cast->elements.nodes[index];
1585 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return false;
1586
1587 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1588 if (!pm_conditional_predicate_warn_write_literal_p(assoc->key) || !pm_conditional_predicate_warn_write_literal_p(assoc->value)) return false;
1589 }
1590
1591 return true;
1592 }
1593 case PM_FALSE_NODE:
1594 case PM_FLOAT_NODE:
1595 case PM_IMAGINARY_NODE:
1596 case PM_INTEGER_NODE:
1597 case PM_NIL_NODE:
1598 case PM_RATIONAL_NODE:
1599 case PM_REGULAR_EXPRESSION_NODE:
1600 case PM_SOURCE_ENCODING_NODE:
1601 case PM_SOURCE_FILE_NODE:
1602 case PM_SOURCE_LINE_NODE:
1603 case PM_STRING_NODE:
1604 case PM_SYMBOL_NODE:
1605 case PM_TRUE_NODE:
1606 return true;
1607 default:
1608 return false;
1609 }
1610}
1611
1616static PRISM_INLINE void
1617pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) {
1618 if (pm_conditional_predicate_warn_write_literal_p(node)) {
1619 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);
1620 }
1621}
1622
1635static void
1636pm_conditional_predicate(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type) {
1637 switch (PM_NODE_TYPE(node)) {
1638 case PM_AND_NODE: {
1639 pm_and_node_t *cast = (pm_and_node_t *) node;
1640 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1641 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1642 break;
1643 }
1644 case PM_OR_NODE: {
1645 pm_or_node_t *cast = (pm_or_node_t *) node;
1646 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1647 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1648 break;
1649 }
1650 case PM_PARENTHESES_NODE: {
1652
1653 if ((cast->body != NULL) && PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE)) {
1654 pm_statements_node_t *statements = (pm_statements_node_t *) cast->body;
1655 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1656 }
1657
1658 break;
1659 }
1660 case PM_BEGIN_NODE: {
1661 pm_begin_node_t *cast = (pm_begin_node_t *) node;
1662 if (cast->statements != NULL) {
1663 pm_statements_node_t *statements = cast->statements;
1664 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1665 }
1666 break;
1667 }
1668 case PM_RANGE_NODE: {
1669 pm_range_node_t *cast = (pm_range_node_t *) node;
1670
1671 if (cast->left != NULL) pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1672 if (cast->right != NULL) pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1673
1674 // Here we change the range node into a flip flop node. We can do
1675 // this since the nodes are exactly the same except for the type.
1676 // We're only asserting against the size when we should probably
1677 // assert against the entire layout, but we'll assume tests will
1678 // catch this.
1679 assert(sizeof(pm_range_node_t) == sizeof(pm_flip_flop_node_t));
1680 node->type = PM_FLIP_FLOP_NODE;
1681
1682 break;
1683 }
1684 case PM_REGULAR_EXPRESSION_NODE:
1685 // Here we change the regular expression node into a match last line
1686 // node. We can do this since the nodes are exactly the same except
1687 // for the type.
1689 node->type = PM_MATCH_LAST_LINE_NODE;
1690
1691 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1692 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "regex ");
1693 }
1694
1695 break;
1696 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1697 // Here we change the interpolated regular expression node into an
1698 // interpolated match last line node. We can do this since the nodes
1699 // are exactly the same except for the type.
1701 node->type = PM_INTERPOLATED_MATCH_LAST_LINE_NODE;
1702
1703 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1704 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "regex ");
1705 }
1706
1707 break;
1708 case PM_INTEGER_NODE:
1709 if (type == PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP) {
1710 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1711 pm_parser_warn_node(parser, node, PM_WARN_INTEGER_IN_FLIP_FLOP);
1712 }
1713 } else {
1714 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1715 }
1716 break;
1717 case PM_STRING_NODE:
1718 case PM_SOURCE_FILE_NODE:
1719 case PM_INTERPOLATED_STRING_NODE:
1720 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "string ");
1721 break;
1722 case PM_SYMBOL_NODE:
1723 case PM_INTERPOLATED_SYMBOL_NODE:
1724 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "symbol ");
1725 break;
1726 case PM_SOURCE_LINE_NODE:
1727 case PM_SOURCE_ENCODING_NODE:
1728 case PM_FLOAT_NODE:
1729 case PM_RATIONAL_NODE:
1730 case PM_IMAGINARY_NODE:
1731 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1732 break;
1733 case PM_CLASS_VARIABLE_WRITE_NODE:
1734 pm_conditional_predicate_warn_write_literal(parser, ((pm_class_variable_write_node_t *) node)->value);
1735 break;
1736 case PM_CONSTANT_WRITE_NODE:
1737 pm_conditional_predicate_warn_write_literal(parser, ((pm_constant_write_node_t *) node)->value);
1738 break;
1739 case PM_GLOBAL_VARIABLE_WRITE_NODE:
1740 pm_conditional_predicate_warn_write_literal(parser, ((pm_global_variable_write_node_t *) node)->value);
1741 break;
1742 case PM_INSTANCE_VARIABLE_WRITE_NODE:
1743 pm_conditional_predicate_warn_write_literal(parser, ((pm_instance_variable_write_node_t *) node)->value);
1744 break;
1745 case PM_LOCAL_VARIABLE_WRITE_NODE:
1746 pm_conditional_predicate_warn_write_literal(parser, ((pm_local_variable_write_node_t *) node)->value);
1747 break;
1748 case PM_MULTI_WRITE_NODE:
1749 pm_conditional_predicate_warn_write_literal(parser, ((pm_multi_write_node_t *) node)->value);
1750 break;
1751 default:
1752 break;
1753 }
1754}
1755
1778
1782static PRISM_INLINE const pm_location_t *
1783pm_arguments_end(pm_arguments_t *arguments) {
1784 if (arguments->block != NULL) {
1785 uint32_t end = PM_NODE_END(arguments->block);
1786
1787 if (arguments->closing_loc.length > 0) {
1788 uint32_t arguments_end = PM_LOCATION_END(&arguments->closing_loc);
1789 if (arguments_end > end) {
1790 return &arguments->closing_loc;
1791 }
1792 }
1793 return &arguments->block->location;
1794 }
1795 if (arguments->closing_loc.length > 0) {
1796 return &arguments->closing_loc;
1797 }
1798 if (arguments->arguments != NULL) {
1799 return &arguments->arguments->base.location;
1800 }
1801 if (arguments->opening_loc.length > 0) {
1802 return &arguments->opening_loc;
1803 }
1804 return NULL;
1805}
1806
1811static void
1812pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_block_node_t *block) {
1813 // First, check that we have arguments and that we don't have a closing
1814 // location for them.
1815 if (arguments->arguments == NULL || arguments->closing_loc.length > 0) {
1816 return;
1817 }
1818
1819 // Next, check that we don't have a single parentheses argument. This would
1820 // look like:
1821 //
1822 // foo (1) {}
1823 //
1824 // In this case, it's actually okay for the block to be attached to the
1825 // call, even though it looks like it's attached to the argument.
1826 if (arguments->arguments->arguments.size == 1 && PM_NODE_TYPE_P(arguments->arguments->arguments.nodes[0], PM_PARENTHESES_NODE)) {
1827 return;
1828 }
1829
1830 // If we didn't hit a case before this check, then at this point we need to
1831 // add a syntax error.
1832 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_UNEXPECTED_BLOCK);
1833}
1834
1835/******************************************************************************/
1836/* Basic character checks */
1837/******************************************************************************/
1838
1845static PRISM_INLINE size_t
1846char_is_identifier_start(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
1847 if (n <= 0) return 0;
1848
1849 if (parser->encoding_changed) {
1850 size_t width;
1851
1852 if ((width = parser->encoding->alpha_char(b, n)) != 0) {
1853 return width;
1854 } else if (*b == '_') {
1855 return 1;
1856 } else if (*b >= 0x80) {
1857 return parser->encoding->char_width(b, n);
1858 } else {
1859 return 0;
1860 }
1861 } else if (*b < 0x80) {
1862 return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT ? 1 : 0) || (*b == '_');
1863 } else {
1864 return pm_encoding_utf_8_char_width(b, n);
1865 }
1866}
1867
1872static PRISM_INLINE size_t
1873char_is_identifier_utf8(const uint8_t *b, ptrdiff_t n) {
1874 if (n <= 0) {
1875 return 0;
1876 } else if (*b < 0x80) {
1877 return (*b == '_') || (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT ? 1 : 0);
1878 } else {
1879 return pm_encoding_utf_8_char_width(b, n);
1880 }
1881}
1882
1896#if defined(PRISM_HAS_NEON)
1897#include <arm_neon.h>
1898
1899static PRISM_INLINE size_t
1900scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
1901 const uint8_t *cursor = start;
1902
1903 // Nibble-based lookup tables for classifying [a-zA-Z0-9_].
1904 // Each high nibble is assigned a unique bit; the low nibble table
1905 // contains the OR of bits for all high nibbles that have an
1906 // identifier character at that low nibble position. A byte is an
1907 // identifier character iff (low_lut[lo] & high_lut[hi]) != 0.
1908 static const uint8_t low_lut_data[16] = {
1909 0x15, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
1910 0x1F, 0x1F, 0x1E, 0x0A, 0x0A, 0x0A, 0x0A, 0x0E
1911 };
1912 static const uint8_t high_lut_data[16] = {
1913 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10,
1914 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1915 };
1916 const uint8x16_t low_lut = vld1q_u8(low_lut_data);
1917 const uint8x16_t high_lut = vld1q_u8(high_lut_data);
1918 const uint8x16_t mask_0f = vdupq_n_u8(0x0F);
1919
1920 while (cursor + 16 <= end) {
1921 uint8x16_t v = vld1q_u8(cursor);
1922
1923 uint8x16_t lo_class = vqtbl1q_u8(low_lut, vandq_u8(v, mask_0f));
1924 uint8x16_t hi_class = vqtbl1q_u8(high_lut, vshrq_n_u8(v, 4));
1925 uint8x16_t ident = vandq_u8(lo_class, hi_class);
1926
1927 // Fast check: if the per-byte minimum is nonzero, every byte matched.
1928 if (vminvq_u8(ident) != 0) {
1929 cursor += 16;
1930 continue;
1931 }
1932
1933 // Find the first non-identifier byte (zero in ident).
1934 uint8x16_t is_zero = vceqq_u8(ident, vdupq_n_u8(0));
1935 uint64_t lo = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 0);
1936
1937 if (lo != 0) {
1938 cursor += pm_ctzll(lo) / 8;
1939 } else {
1940 uint64_t hi = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 1);
1941 cursor += 8 + pm_ctzll(hi) / 8;
1942 }
1943
1944 return (size_t) (cursor - start);
1945 }
1946
1947 return (size_t) (cursor - start);
1948}
1949
1950#elif defined(PRISM_HAS_SSE2)
1951#include <emmintrin.h>
1952
1953static PRISM_INLINE size_t
1954scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
1955 const uint8_t *cursor = start;
1956
1957 while (cursor + 16 <= end) {
1958 __m128i v = _mm_loadu_si128((const __m128i *) cursor);
1959 __m128i zero = _mm_setzero_si128();
1960
1961 // Unsigned range check via saturating subtraction:
1962 // byte >= lo ⟺ saturate(lo - byte) == 0
1963 // byte <= hi ⟺ saturate(byte - hi) == 0
1964
1965 // Fold case: OR with 0x20 maps A-Z to a-z.
1966 __m128i lowered = _mm_or_si128(v, _mm_set1_epi8(0x20));
1967 __m128i letter = _mm_and_si128(
1968 _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x61), lowered), zero),
1969 _mm_cmpeq_epi8(_mm_subs_epu8(lowered, _mm_set1_epi8(0x7A)), zero));
1970
1971 __m128i digit = _mm_and_si128(
1972 _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x30), v), zero),
1973 _mm_cmpeq_epi8(_mm_subs_epu8(v, _mm_set1_epi8(0x39)), zero));
1974
1975 __m128i underscore = _mm_cmpeq_epi8(v, _mm_set1_epi8(0x5F));
1976
1977 __m128i ident = _mm_or_si128(_mm_or_si128(letter, digit), underscore);
1978 int mask = _mm_movemask_epi8(ident);
1979
1980 if (mask == 0xFFFF) {
1981 cursor += 16;
1982 continue;
1983 }
1984
1985 cursor += pm_ctzll((uint64_t) (~mask & 0xFFFF));
1986 return (size_t) (cursor - start);
1987 }
1988
1989 return (size_t) (cursor - start);
1990}
1991
1992// The SWAR path uses pm_ctzll to find the first non-matching byte within a
1993// word, which only yields the correct byte index on little-endian targets.
1994// We gate on a positive little-endian check so that unknown-endianness
1995// platforms safely fall through to the no-op fallback.
1996#elif defined(PRISM_HAS_SWAR)
1997
2007static PRISM_INLINE size_t
2008scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
2009 static const uint64_t ones = 0x0101010101010101ULL;
2010 static const uint64_t highs = 0x8080808080808080ULL;
2011 const uint8_t *cursor = start;
2012
2013 while (cursor + 8 <= end) {
2014 uint64_t word;
2015 memcpy(&word, cursor, 8);
2016
2017 // Bail on any non-ASCII byte.
2018 if (word & highs) break;
2019
2020 uint64_t digit = ((word | highs) - ones * 0x30) & ((ones * 0x39 | highs) - word) & highs;
2021
2022 // Fold upper- and lowercase together by forcing bit 5 (OR 0x20),
2023 // then check the lowercase range once. A-Z maps to a-z; the
2024 // only non-letter byte that could alias into [0x61,0x7A] is one
2025 // whose original value was in [0x41,0x5A] — which is exactly
2026 // the uppercase letters we want to match.
2027 uint64_t lowered = word | (ones * 0x20);
2028 uint64_t letter = ((lowered | highs) - ones * 0x61) & ((ones * 0x7A | highs) - lowered) & highs;
2029
2030 // Standard SWAR "has zero byte" idiom on (word XOR 0x5F) to find
2031 // bytes equal to underscore. Safe from cross-byte borrows because
2032 // the ASCII guard above ensures all bytes are < 0x80.
2033 uint64_t xor_us = word ^ (ones * 0x5F);
2034 uint64_t underscore = (xor_us - ones) & ~xor_us & highs;
2035
2036 uint64_t ident = digit | letter | underscore;
2037
2038 if (ident == highs) {
2039 cursor += 8;
2040 continue;
2041 }
2042
2043 // Find the first non-identifier byte. On little-endian the first
2044 // byte sits in the least-significant position.
2045 uint64_t not_ident = ~ident & highs;
2046 cursor += pm_ctzll(not_ident) / 8;
2047 return (size_t) (cursor - start);
2048 }
2049
2050 return (size_t) (cursor - start);
2051}
2052
2053#else
2054
2055// No-op fallback for big-endian or other unsupported platforms.
2056// The caller's byte-at-a-time loop handles everything.
2057#define scan_identifier_ascii(start, end) ((size_t) 0)
2058
2059#endif
2060
2066static PRISM_INLINE size_t
2067char_is_identifier(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
2068 if (n <= 0) {
2069 return 0;
2070 } else if (parser->encoding_changed) {
2071 size_t width;
2072
2073 if ((width = parser->encoding->alnum_char(b, n)) != 0) {
2074 return width;
2075 } else if (*b == '_') {
2076 return 1;
2077 } else if (*b >= 0x80) {
2078 return parser->encoding->char_width(b, n);
2079 } else {
2080 return 0;
2081 }
2082 } else {
2083 return char_is_identifier_utf8(b, n);
2084 }
2085}
2086
2087// Here we're defining a perfect hash for the characters that are allowed in
2088// global names. This is used to quickly check the next character after a $ to
2089// see if it's a valid character for a global name.
2090#define BIT(c, idx) (((c) / 32 - 1 == idx) ? (1U << ((c) % 32)) : 0)
2091#define PUNCT(idx) ( \
2092 BIT('~', idx) | BIT('*', idx) | BIT('$', idx) | BIT('?', idx) | \
2093 BIT('!', idx) | BIT('@', idx) | BIT('/', idx) | BIT('\\', idx) | \
2094 BIT(';', idx) | BIT(',', idx) | BIT('.', idx) | BIT('=', idx) | \
2095 BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \
2096 BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \
2097 BIT('0', idx))
2098
2099const unsigned int pm_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { PUNCT(0), PUNCT(1), PUNCT(2) };
2100
2101#undef BIT
2102#undef PUNCT
2103
2104static PRISM_INLINE bool
2105char_is_global_name_punctuation(const uint8_t b) {
2106 const unsigned int i = (const unsigned int) b;
2107 if (i <= 0x20 || 0x7e < i) return false;
2108
2109 return (pm_global_name_punctuation_hash[(i - 0x20) / 32] >> (i % 32)) & 1;
2110}
2111
2112static PRISM_INLINE bool
2113token_is_setter_name(pm_token_t *token) {
2114 return (
2115 (token->type == PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL) ||
2116 ((token->type == PM_TOKEN_IDENTIFIER) &&
2117 (token->end - token->start >= 2) &&
2118 (token->end[-1] == '='))
2119 );
2120}
2121
2125static bool
2126pm_local_is_keyword(const char *source, size_t length) {
2127#define KEYWORD(name) if (memcmp(source, name, length) == 0) return true
2128
2129 switch (length) {
2130 case 2:
2131 switch (source[0]) {
2132 case 'd': KEYWORD("do"); return false;
2133 case 'i': KEYWORD("if"); KEYWORD("in"); return false;
2134 case 'o': KEYWORD("or"); return false;
2135 default: return false;
2136 }
2137 case 3:
2138 switch (source[0]) {
2139 case 'a': KEYWORD("and"); return false;
2140 case 'd': KEYWORD("def"); return false;
2141 case 'e': KEYWORD("end"); return false;
2142 case 'f': KEYWORD("for"); return false;
2143 case 'n': KEYWORD("nil"); KEYWORD("not"); return false;
2144 default: return false;
2145 }
2146 case 4:
2147 switch (source[0]) {
2148 case 'c': KEYWORD("case"); return false;
2149 case 'e': KEYWORD("else"); return false;
2150 case 'n': KEYWORD("next"); return false;
2151 case 'r': KEYWORD("redo"); return false;
2152 case 's': KEYWORD("self"); return false;
2153 case 't': KEYWORD("then"); KEYWORD("true"); return false;
2154 case 'w': KEYWORD("when"); return false;
2155 default: return false;
2156 }
2157 case 5:
2158 switch (source[0]) {
2159 case 'a': KEYWORD("alias"); return false;
2160 case 'b': KEYWORD("begin"); KEYWORD("break"); return false;
2161 case 'c': KEYWORD("class"); return false;
2162 case 'e': KEYWORD("elsif"); return false;
2163 case 'f': KEYWORD("false"); return false;
2164 case 'r': KEYWORD("retry"); return false;
2165 case 's': KEYWORD("super"); return false;
2166 case 'u': KEYWORD("undef"); KEYWORD("until"); return false;
2167 case 'w': KEYWORD("while"); return false;
2168 case 'y': KEYWORD("yield"); return false;
2169 default: return false;
2170 }
2171 case 6:
2172 switch (source[0]) {
2173 case 'e': KEYWORD("ensure"); return false;
2174 case 'm': KEYWORD("module"); return false;
2175 case 'r': KEYWORD("rescue"); KEYWORD("return"); return false;
2176 case 'u': KEYWORD("unless"); return false;
2177 default: return false;
2178 }
2179 case 8:
2180 KEYWORD("__LINE__");
2181 KEYWORD("__FILE__");
2182 return false;
2183 case 12:
2184 KEYWORD("__ENCODING__");
2185 return false;
2186 default:
2187 return false;
2188 }
2189
2190#undef KEYWORD
2191}
2192
2193/******************************************************************************/
2194/* Node flag handling functions */
2195/******************************************************************************/
2196
2200static PRISM_INLINE void
2201pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) {
2202 node->flags |= flag;
2203}
2204
2208static PRISM_INLINE void
2209pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) {
2210 node->flags &= (pm_node_flags_t) ~flag;
2211}
2212
2216static PRISM_INLINE void
2217pm_node_flag_set_repeated_parameter(pm_node_t *node) {
2218 assert(PM_NODE_TYPE(node) == PM_BLOCK_LOCAL_VARIABLE_NODE ||
2219 PM_NODE_TYPE(node) == PM_BLOCK_PARAMETER_NODE ||
2220 PM_NODE_TYPE(node) == PM_KEYWORD_REST_PARAMETER_NODE ||
2221 PM_NODE_TYPE(node) == PM_OPTIONAL_KEYWORD_PARAMETER_NODE ||
2222 PM_NODE_TYPE(node) == PM_OPTIONAL_PARAMETER_NODE ||
2223 PM_NODE_TYPE(node) == PM_REQUIRED_KEYWORD_PARAMETER_NODE ||
2224 PM_NODE_TYPE(node) == PM_REQUIRED_PARAMETER_NODE ||
2225 PM_NODE_TYPE(node) == PM_REST_PARAMETER_NODE);
2226
2227 pm_node_flag_set(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER);
2228}
2229
2230/******************************************************************************/
2231/* Node creation functions */
2232/******************************************************************************/
2233
2239#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)
2240
2244static PRISM_INLINE pm_node_flags_t
2245pm_regular_expression_flags_create(pm_parser_t *parser, const pm_token_t *closing) {
2246 pm_node_flags_t flags = 0;
2247
2248 if (closing->type == PM_TOKEN_REGEXP_END) {
2249 pm_buffer_t unknown_flags = { 0 };
2250
2251 // The closing delimiter is normally a single byte, so the options
2252 // follow it. A `\r\n` newline delimiter is two bytes, however, so we
2253 // skip past it to avoid misreading the trailing `\n` as an option.
2254 const uint8_t *flag = closing->start + 1;
2255 if ((closing->end - closing->start) >= 2 && closing->start[0] == '\r' && closing->start[1] == '\n') {
2256 flag++;
2257 }
2258
2259 for (; flag < closing->end; flag++) {
2260 switch (*flag) {
2261 case 'i': flags |= PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE; break;
2262 case 'm': flags |= PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE; break;
2263 case 'x': flags |= PM_REGULAR_EXPRESSION_FLAGS_EXTENDED; break;
2264 case 'o': flags |= PM_REGULAR_EXPRESSION_FLAGS_ONCE; break;
2265
2266 case 'e': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_EUC_JP); break;
2267 case 'n': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT); break;
2268 case 's': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J); break;
2269 case 'u': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_UTF_8); break;
2270
2271 default: pm_buffer_append_byte(&unknown_flags, *flag);
2272 }
2273 }
2274
2275 size_t unknown_flags_length = pm_buffer_length(&unknown_flags);
2276 if (unknown_flags_length != 0) {
2277 const char *word = unknown_flags_length >= 2 ? "options" : "option";
2278 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_REGEXP_UNKNOWN_OPTIONS, word, unknown_flags_length, pm_buffer_value(&unknown_flags));
2279 }
2280 pm_buffer_cleanup(&unknown_flags);
2281 }
2282
2283 return flags;
2284}
2285
2286#undef PM_REGULAR_EXPRESSION_ENCODING_MASK
2287
2288static pm_statements_node_t *
2289pm_statements_node_create(pm_parser_t *parser);
2290
2291static void
2292pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline);
2293
2294static size_t
2295pm_statements_node_body_length(pm_statements_node_t *node);
2296
2301static PRISM_INLINE void
2302pm_integer_arena_move(pm_arena_t *arena, pm_integer_t *integer) {
2303 if (integer->values != NULL) {
2304 size_t byte_size = integer->length * sizeof(uint32_t);
2305 uint32_t *old_values = integer->values;
2306 integer->values = (uint32_t *) pm_arena_memdup(arena, old_values, byte_size, PRISM_ALIGNOF(uint32_t));
2307 xfree(old_values);
2308 }
2309}
2310
2314static pm_error_recovery_node_t *
2315pm_error_recovery_node_create(pm_parser_t *parser, uint32_t start, uint32_t length) {
2316 return pm_error_recovery_node_new(
2317 parser->arena,
2318 ++parser->node_id,
2319 0,
2320 ((pm_location_t) { .start = start, .length = length }),
2321 NULL
2322 );
2323}
2324
2328static pm_error_recovery_node_t *
2329pm_error_recovery_node_create_unexpected(pm_parser_t *parser, pm_node_t *unexpected) {
2330 return pm_error_recovery_node_new(
2331 parser->arena,
2332 ++parser->node_id,
2333 0,
2334 unexpected->location,
2335 unexpected
2336 );
2337}
2338
2342static pm_alias_global_variable_node_t *
2343pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
2344 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
2345
2346 return pm_alias_global_variable_node_new(
2347 parser->arena,
2348 ++parser->node_id,
2349 0,
2350 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name),
2351 new_name,
2352 old_name,
2353 TOK2LOC(parser, keyword)
2354 );
2355}
2356
2360static pm_alias_method_node_t *
2361pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
2362 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
2363
2364 return pm_alias_method_node_new(
2365 parser->arena,
2366 ++parser->node_id,
2367 0,
2368 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name),
2369 new_name,
2370 old_name,
2371 TOK2LOC(parser, keyword)
2372 );
2373}
2374
2378static pm_alternation_pattern_node_t *
2379pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node_t *right, const pm_token_t *operator) {
2380 return pm_alternation_pattern_node_new(
2381 parser->arena,
2382 ++parser->node_id,
2383 0,
2384 PM_LOCATION_INIT_NODES(left, right),
2385 left,
2386 right,
2387 TOK2LOC(parser, operator)
2388 );
2389}
2390
2394static pm_and_node_t *
2395pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
2396 pm_assert_value_expression(parser, left);
2397
2398 return pm_and_node_new(
2399 parser->arena,
2400 ++parser->node_id,
2401 0,
2402 PM_LOCATION_INIT_NODES(left, right),
2403 left,
2404 right,
2405 TOK2LOC(parser, operator)
2406 );
2407}
2408
2412static pm_arguments_node_t *
2413pm_arguments_node_create(pm_parser_t *parser) {
2414 return pm_arguments_node_new(
2415 parser->arena,
2416 ++parser->node_id,
2417 0,
2418 PM_LOCATION_INIT_UNSET,
2419 ((pm_node_list_t) { 0 })
2420 );
2421}
2422
2426static size_t
2427pm_arguments_node_size(pm_arguments_node_t *node) {
2428 return node->arguments.size;
2429}
2430
2434static void
2435pm_arguments_node_arguments_append(pm_arena_t *arena, pm_arguments_node_t *node, pm_node_t *argument) {
2436 if (pm_arguments_node_size(node) == 0) {
2437 PM_NODE_START_SET_NODE(node, argument);
2438 }
2439
2440 if (PM_NODE_END(node) < PM_NODE_END(argument)) {
2441 PM_NODE_LENGTH_SET_NODE(node, argument);
2442 }
2443
2444 pm_node_list_append(arena, &node->arguments, argument);
2445
2446 if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) {
2447 if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
2448 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS);
2449 } else {
2450 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT);
2451 }
2452 }
2453}
2454
2458static pm_array_node_t *
2459pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) {
2460 if (opening == NULL) {
2461 return pm_array_node_new(
2462 parser->arena,
2463 ++parser->node_id,
2464 PM_NODE_FLAG_STATIC_LITERAL,
2465 PM_LOCATION_INIT_UNSET,
2466 ((pm_node_list_t) { 0 }),
2467 ((pm_location_t) { 0 }),
2468 ((pm_location_t) { 0 })
2469 );
2470 } else {
2471 return pm_array_node_new(
2472 parser->arena,
2473 ++parser->node_id,
2474 PM_NODE_FLAG_STATIC_LITERAL,
2475 PM_LOCATION_INIT_TOKEN(parser, opening),
2476 ((pm_node_list_t) { 0 }),
2477 TOK2LOC(parser, opening),
2478 TOK2LOC(parser, opening)
2479 );
2480 }
2481}
2482
2486static PRISM_INLINE void
2487pm_array_node_elements_append(pm_arena_t *arena, pm_array_node_t *node, pm_node_t *element) {
2488 if (!node->elements.size && !node->opening_loc.length) {
2489 PM_NODE_START_SET_NODE(node, element);
2490 }
2491
2492 pm_node_list_append(arena, &node->elements, element);
2493 PM_NODE_LENGTH_SET_NODE(node, element);
2494
2495 // If the element is not a static literal, then the array is not a static
2496 // literal. Turn that flag off.
2497 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)) {
2498 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
2499 }
2500
2501 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
2502 pm_node_flag_set(UP(node), PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT);
2503 }
2504}
2505
2509static void
2510pm_array_node_close_set(const pm_parser_t *parser, pm_array_node_t *node, const pm_token_t *closing) {
2511 assert(closing->type == PM_TOKEN_BRACKET_RIGHT || closing->type == PM_TOKEN_STRING_END || closing->type == 0);
2512 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
2513 node->closing_loc = TOK2LOC(parser, closing);
2514}
2515
2520static pm_array_pattern_node_t *
2521pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *nodes) {
2522 pm_array_pattern_node_t *node = pm_array_pattern_node_new(
2523 parser->arena,
2524 ++parser->node_id,
2525 0,
2526 PM_LOCATION_INIT_NODES(nodes->nodes[0], nodes->nodes[nodes->size - 1]),
2527 NULL,
2528 ((pm_node_list_t) { 0 }),
2529 NULL,
2530 ((pm_node_list_t) { 0 }),
2531 ((pm_location_t) { 0 }),
2532 ((pm_location_t) { 0 })
2533 );
2534
2535 // For now we're going to just copy over each pointer manually. This could be
2536 // much more efficient, as we could instead resize the node list.
2537 bool found_rest = false;
2538 pm_node_t *child;
2539
2540 PM_NODE_LIST_FOREACH(nodes, index, child) {
2541 if (!found_rest && (PM_NODE_TYPE_P(child, PM_SPLAT_NODE) || PM_NODE_TYPE_P(child, PM_IMPLICIT_REST_NODE))) {
2542 node->rest = child;
2543 found_rest = true;
2544 } else if (found_rest) {
2545 pm_node_list_append(parser->arena, &node->posts, child);
2546 } else {
2547 pm_node_list_append(parser->arena, &node->requireds, child);
2548 }
2549 }
2550
2551 return node;
2552}
2553
2557static pm_array_pattern_node_t *
2558pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) {
2559 return pm_array_pattern_node_new(
2560 parser->arena,
2561 ++parser->node_id,
2562 0,
2563 PM_LOCATION_INIT_NODE(rest),
2564 NULL,
2565 ((pm_node_list_t) { 0 }),
2566 rest,
2567 ((pm_node_list_t) { 0 }),
2568 ((pm_location_t) { 0 }),
2569 ((pm_location_t) { 0 })
2570 );
2571}
2572
2577static pm_array_pattern_node_t *
2578pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, const pm_token_t *opening, const pm_token_t *closing) {
2579 return pm_array_pattern_node_new(
2580 parser->arena,
2581 ++parser->node_id,
2582 0,
2583 PM_LOCATION_INIT_NODE_TOKEN(parser, constant, closing),
2584 constant,
2585 ((pm_node_list_t) { 0 }),
2586 NULL,
2587 ((pm_node_list_t) { 0 }),
2588 TOK2LOC(parser, opening),
2589 TOK2LOC(parser, closing)
2590 );
2591}
2592
2597static pm_array_pattern_node_t *
2598pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
2599 return pm_array_pattern_node_new(
2600 parser->arena,
2601 ++parser->node_id,
2602 0,
2603 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
2604 NULL,
2605 ((pm_node_list_t) { 0 }),
2606 NULL,
2607 ((pm_node_list_t) { 0 }),
2608 TOK2LOC(parser, opening),
2609 TOK2LOC(parser, closing)
2610 );
2611}
2612
2613static PRISM_INLINE void
2614pm_array_pattern_node_requireds_append(pm_arena_t *arena, pm_array_pattern_node_t *node, pm_node_t *inner) {
2615 pm_node_list_append(arena, &node->requireds, inner);
2616}
2617
2621static pm_assoc_node_t *
2622pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *operator, pm_node_t *value) {
2623 uint32_t end;
2624
2625 if (value != NULL && PM_NODE_END(value) > PM_NODE_END(key)) {
2626 end = PM_NODE_END(value);
2627 } else if (operator != NULL) {
2628 end = PM_TOKEN_END(parser, operator);
2629 } else {
2630 end = PM_NODE_END(key);
2631 }
2632
2633 // Hash string keys will be frozen, so we can mark them as frozen here so
2634 // that the compiler picks them up and also when we check for static literal
2635 // on the keys it gets factored in.
2636 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
2637 key->flags |= PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL;
2638 }
2639
2640 // If the key and value of this assoc node are both static literals, then
2641 // we can mark this node as a static literal.
2642 pm_node_flags_t flags = 0;
2643 if (
2644 !PM_NODE_TYPE_P(key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(key, PM_HASH_NODE) && !PM_NODE_TYPE_P(key, PM_RANGE_NODE) &&
2645 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)
2646 ) {
2647 flags = key->flags & value->flags & PM_NODE_FLAG_STATIC_LITERAL;
2648 }
2649
2650 return pm_assoc_node_new(
2651 parser->arena,
2652 ++parser->node_id,
2653 flags,
2654 ((pm_location_t) { .start = PM_NODE_START(key), .length = U32(end - PM_NODE_START(key)) }),
2655 key,
2656 value,
2657 NTOK2LOC(parser, operator)
2658 );
2659}
2660
2664static pm_assoc_splat_node_t *
2665pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token_t *operator) {
2666 assert(operator->type == PM_TOKEN_USTAR_STAR);
2667
2668 return pm_assoc_splat_node_new(
2669 parser->arena,
2670 ++parser->node_id,
2671 0,
2672 (value == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, value),
2673 value,
2674 TOK2LOC(parser, operator)
2675 );
2676}
2677
2681static pm_back_reference_read_node_t *
2682pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
2683 assert(name->type == PM_TOKEN_BACK_REFERENCE);
2684
2685 return pm_back_reference_read_node_new(
2686 parser->arena,
2687 ++parser->node_id,
2688 0,
2689 PM_LOCATION_INIT_TOKEN(parser, name),
2690 pm_parser_constant_id_token(parser, name)
2691 );
2692}
2693
2697static pm_begin_node_t *
2698pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_statements_node_t *statements) {
2699 uint32_t start = begin_keyword == NULL ? 0 : PM_TOKEN_START(parser, begin_keyword);
2700 uint32_t end = statements == NULL ? (begin_keyword == NULL ? 0 : PM_TOKEN_END(parser, begin_keyword)) : PM_NODE_END(statements);
2701
2702 return pm_begin_node_new(
2703 parser->arena,
2704 ++parser->node_id,
2705 0,
2706 ((pm_location_t) { .start = start, .length = U32(end - start) }),
2707 NTOK2LOC(parser, begin_keyword),
2708 statements,
2709 NULL,
2710 NULL,
2711 NULL,
2712 ((pm_location_t) { 0 })
2713 );
2714}
2715
2719static void
2720pm_begin_node_rescue_clause_set(pm_begin_node_t *node, pm_rescue_node_t *rescue_clause) {
2721 if (node->begin_keyword_loc.length == 0) {
2722 PM_NODE_START_SET_NODE(node, rescue_clause);
2723 }
2724 PM_NODE_LENGTH_SET_NODE(node, rescue_clause);
2725 node->rescue_clause = rescue_clause;
2726}
2727
2731static void
2732pm_begin_node_else_clause_set(pm_begin_node_t *node, pm_else_node_t *else_clause) {
2733 if ((node->begin_keyword_loc.length == 0) && PM_NODE_START(node) == 0) {
2734 PM_NODE_START_SET_NODE(node, else_clause);
2735 }
2736 PM_NODE_LENGTH_SET_NODE(node, else_clause);
2737 node->else_clause = else_clause;
2738}
2739
2743static void
2744pm_begin_node_ensure_clause_set(pm_begin_node_t *node, pm_ensure_node_t *ensure_clause) {
2745 if ((node->begin_keyword_loc.length == 0) && PM_NODE_START(node) == 0) {
2746 PM_NODE_START_SET_NODE(node, ensure_clause);
2747 }
2748 PM_NODE_LENGTH_SET_NODE(node, ensure_clause);
2749 node->ensure_clause = ensure_clause;
2750}
2751
2755static void
2756pm_begin_node_end_keyword_set(const pm_parser_t *parser, pm_begin_node_t *node, const pm_token_t *end_keyword) {
2757 assert(end_keyword->type == PM_TOKEN_KEYWORD_END || end_keyword->type == 0);
2758 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
2759 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
2760}
2761
2765static pm_block_argument_node_t *
2766pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
2767 assert(operator->type == PM_TOKEN_UAMPERSAND);
2768
2769 return pm_block_argument_node_new(
2770 parser->arena,
2771 ++parser->node_id,
2772 0,
2773 (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression),
2774 expression,
2775 TOK2LOC(parser, operator)
2776 );
2777}
2778
2782static pm_block_node_t *
2783pm_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) {
2784 return pm_block_node_new(
2785 parser->arena,
2786 ++parser->node_id,
2787 0,
2788 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
2789 *locals,
2790 parameters,
2791 body,
2792 TOK2LOC(parser, opening),
2793 TOK2LOC(parser, closing)
2794 );
2795}
2796
2800static pm_block_parameter_node_t *
2801pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator) {
2802 assert(operator->type == PM_TOKEN_UAMPERSAND || operator->type == PM_TOKEN_AMPERSAND);
2803
2804 return pm_block_parameter_node_new(
2805 parser->arena,
2806 ++parser->node_id,
2807 0,
2808 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
2809 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
2810 NTOK2LOC(parser, name),
2811 TOK2LOC(parser, operator)
2812 );
2813}
2814
2818static pm_block_parameters_node_t *
2819pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *parameters, const pm_token_t *opening) {
2820 uint32_t start;
2821 if (opening != NULL) {
2822 start = PM_TOKEN_START(parser, opening);
2823 } else if (parameters != NULL) {
2824 start = PM_NODE_START(parameters);
2825 } else {
2826 start = 0;
2827 }
2828
2829 uint32_t end;
2830 if (parameters != NULL) {
2831 end = PM_NODE_END(parameters);
2832 } else if (opening != NULL) {
2833 end = PM_TOKEN_END(parser, opening);
2834 } else {
2835 end = 0;
2836 }
2837
2838 return pm_block_parameters_node_new(
2839 parser->arena,
2840 ++parser->node_id,
2841 0,
2842 ((pm_location_t) { .start = start, .length = U32(end - start) }),
2843 parameters,
2844 ((pm_node_list_t) { 0 }),
2845 NTOK2LOC(parser, opening),
2846 ((pm_location_t) { 0 })
2847 );
2848}
2849
2853static void
2854pm_block_parameters_node_closing_set(const pm_parser_t *parser, pm_block_parameters_node_t *node, const pm_token_t *closing) {
2855 assert(closing->type == PM_TOKEN_PIPE || closing->type == PM_TOKEN_PARENTHESIS_RIGHT || closing->type == 0);
2856 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
2857 node->closing_loc = TOK2LOC(parser, closing);
2858}
2859
2863static pm_block_local_variable_node_t *
2864pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) {
2865 return pm_block_local_variable_node_new(
2866 parser->arena,
2867 ++parser->node_id,
2868 0,
2869 PM_LOCATION_INIT_TOKEN(parser, name),
2870 pm_parser_constant_id_token(parser, name)
2871 );
2872}
2873
2877static void
2878pm_block_parameters_node_append_local(pm_arena_t *arena, pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) {
2879 pm_node_list_append(arena, &node->locals, UP(local));
2880
2881 if (PM_NODE_LENGTH(node) == 0) {
2882 PM_NODE_START_SET_NODE(node, local);
2883 }
2884
2885 PM_NODE_LENGTH_SET_NODE(node, local);
2886}
2887
2891static pm_break_node_t *
2892pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
2893 assert(keyword->type == PM_TOKEN_KEYWORD_BREAK);
2894
2895 return pm_break_node_new(
2896 parser->arena,
2897 ++parser->node_id,
2898 0,
2899 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
2900 arguments,
2901 TOK2LOC(parser, keyword)
2902 );
2903}
2904
2905// There are certain flags that we want to use internally but don't want to
2906// expose because they are not relevant beyond parsing. Therefore we'll define
2907// them here and not define them in config.yml/a header file.
2908static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2);
2909
2910static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1);
2911static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2);
2912static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3);
2913
2919static pm_call_node_t *
2920pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) {
2921 return pm_call_node_new(
2922 parser->arena,
2923 ++parser->node_id,
2924 flags,
2925 PM_LOCATION_INIT_UNSET,
2926 NULL,
2927 ((pm_location_t) { 0 }),
2928 0,
2929 ((pm_location_t) { 0 }),
2930 ((pm_location_t) { 0 }),
2931 NULL,
2932 ((pm_location_t) { 0 }),
2933 ((pm_location_t) { 0 }),
2934 NULL
2935 );
2936}
2937
2942static PRISM_INLINE pm_node_flags_t
2943pm_call_node_ignore_visibility_flag(const pm_node_t *receiver) {
2944 return PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY : 0;
2945}
2946
2951static pm_call_node_t *
2952pm_call_node_aref_create(pm_parser_t *parser, pm_node_t *receiver, pm_arguments_t *arguments) {
2953 pm_assert_value_expression(parser, receiver);
2954
2955 pm_node_flags_t flags = pm_call_node_ignore_visibility_flag(receiver);
2956 if (arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_ARGUMENT_NODE)) {
2957 flags |= PM_CALL_NODE_FLAGS_INDEX;
2958 }
2959
2960 pm_call_node_t *node = pm_call_node_create(parser, flags);
2961
2962 PM_NODE_START_SET_NODE(node, receiver);
2963
2964 const pm_location_t *end = pm_arguments_end(arguments);
2965 assert(end != NULL && "unreachable");
2966 PM_NODE_LENGTH_SET_LOCATION(node, end);
2967
2968 node->receiver = receiver;
2969 node->message_loc.start = arguments->opening_loc.start;
2970 node->message_loc.length = (arguments->closing_loc.start + arguments->closing_loc.length) - arguments->opening_loc.start;
2971
2972 node->opening_loc = arguments->opening_loc;
2973 node->arguments = arguments->arguments;
2974 node->closing_loc = arguments->closing_loc;
2975 node->block = arguments->block;
2976
2977 node->name = pm_parser_constant_id_constant(parser, "[]", 2);
2978 return node;
2979}
2980
2984static pm_call_node_t *
2985pm_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) {
2986 pm_assert_value_expression(parser, receiver);
2987 pm_assert_value_expression(parser, argument);
2988
2989 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver) | flags);
2990
2991 PM_NODE_START_SET_NODE(node, PM_NODE_START(receiver) < PM_NODE_START(argument) ? receiver : argument);
2992 PM_NODE_LENGTH_SET_NODE(node, PM_NODE_END(receiver) > PM_NODE_END(argument) ? receiver : argument);
2993
2994 node->receiver = receiver;
2995 node->message_loc = TOK2LOC(parser, operator);
2996
2997 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
2998 pm_arguments_node_arguments_append(parser->arena, arguments, argument);
2999 node->arguments = arguments;
3000
3001 node->name = pm_parser_constant_id_token(parser, operator);
3002 return node;
3003}
3004
3005static const uint8_t * parse_operator_symbol_name(const pm_token_t *);
3006
3010static pm_call_node_t *
3011pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_token_t *message, pm_arguments_t *arguments) {
3012 pm_assert_value_expression(parser, receiver);
3013
3014 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3015
3016 PM_NODE_START_SET_NODE(node, receiver);
3017 const pm_location_t *end = pm_arguments_end(arguments);
3018 if (end == NULL) {
3019 PM_NODE_LENGTH_SET_TOKEN(parser, node, message);
3020 } else {
3021 PM_NODE_LENGTH_SET_LOCATION(node, end);
3022 }
3023
3024 node->receiver = receiver;
3025 node->call_operator_loc = TOK2LOC(parser, operator);
3026 node->message_loc = TOK2LOC(parser, message);
3027 node->opening_loc = arguments->opening_loc;
3028 node->arguments = arguments->arguments;
3029 node->closing_loc = arguments->closing_loc;
3030 node->block = arguments->block;
3031
3032 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
3033 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
3034 }
3035
3040 node->name = pm_parser_constant_id_raw(parser, message->start, parse_operator_symbol_name(message));
3041 return node;
3042}
3043
3047static pm_call_node_t *
3048pm_call_node_call_synthesized_create(pm_parser_t *parser, pm_node_t *receiver, const char *message, pm_arguments_node_t *arguments) {
3049 pm_call_node_t *node = pm_call_node_create(parser, 0);
3050 node->base.location = (pm_location_t) { .start = 0, .length = U32(parser->end - parser->start) };
3051
3052 node->receiver = receiver;
3053 node->arguments = arguments;
3054
3055 node->name = pm_parser_constant_id_constant(parser, message, strlen(message));
3056 return node;
3057}
3058
3063static pm_call_node_t *
3064pm_call_node_fcall_create(pm_parser_t *parser, pm_token_t *message, pm_arguments_t *arguments) {
3065 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3066
3067 PM_NODE_START_SET_TOKEN(parser, node, message);
3068 const pm_location_t *end = pm_arguments_end(arguments);
3069 assert(end != NULL && "unreachable");
3070 PM_NODE_LENGTH_SET_LOCATION(node, end);
3071
3072 node->message_loc = TOK2LOC(parser, message);
3073 node->opening_loc = arguments->opening_loc;
3074 node->arguments = arguments->arguments;
3075 node->closing_loc = arguments->closing_loc;
3076 node->block = arguments->block;
3077
3078 node->name = pm_parser_constant_id_token(parser, message);
3079 return node;
3080}
3081
3086static pm_call_node_t *
3087pm_call_node_fcall_synthesized_create(pm_parser_t *parser, pm_arguments_node_t *arguments, pm_constant_id_t name) {
3088 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3089
3090 node->base.location = (pm_location_t) { 0 };
3091 node->arguments = arguments;
3092
3093 node->name = name;
3094 return node;
3095}
3096
3100static pm_call_node_t *
3101pm_call_node_not_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *message, pm_arguments_t *arguments) {
3102 pm_assert_value_expression(parser, receiver);
3103 if (receiver != NULL) pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
3104
3105 pm_call_node_t *node = pm_call_node_create(parser, receiver == NULL ? 0 : pm_call_node_ignore_visibility_flag(receiver));
3106
3107 PM_NODE_START_SET_TOKEN(parser, node, message);
3108 if (arguments->closing_loc.length > 0) {
3109 PM_NODE_LENGTH_SET_LOCATION(node, &arguments->closing_loc);
3110 } else {
3111 assert(receiver != NULL);
3112 PM_NODE_LENGTH_SET_NODE(node, receiver);
3113 }
3114
3115 node->receiver = receiver;
3116 node->message_loc = TOK2LOC(parser, message);
3117 node->opening_loc = arguments->opening_loc;
3118 node->arguments = arguments->arguments;
3119 node->closing_loc = arguments->closing_loc;
3120
3121 node->name = pm_parser_constant_id_constant(parser, "!", 1);
3122 return node;
3123}
3124
3128static pm_call_node_t *
3129pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_arguments_t *arguments) {
3130 pm_assert_value_expression(parser, receiver);
3131
3132 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3133
3134 PM_NODE_START_SET_NODE(node, receiver);
3135 const pm_location_t *end = pm_arguments_end(arguments);
3136 assert(end != NULL && "unreachable");
3137 PM_NODE_LENGTH_SET_LOCATION(node, end);
3138
3139 node->receiver = receiver;
3140 node->call_operator_loc = TOK2LOC(parser, operator);
3141 node->opening_loc = arguments->opening_loc;
3142 node->arguments = arguments->arguments;
3143 node->closing_loc = arguments->closing_loc;
3144 node->block = arguments->block;
3145
3146 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
3147 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
3148 }
3149
3150 node->name = pm_parser_constant_id_constant(parser, "call", 4);
3151 return node;
3152}
3153
3157static pm_call_node_t *
3158pm_call_node_unary_create(pm_parser_t *parser, pm_token_t *operator, pm_node_t *receiver, const char *name) {
3159 pm_assert_value_expression(parser, receiver);
3160
3161 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3162
3163 PM_NODE_START_SET_TOKEN(parser, node, operator);
3164 PM_NODE_LENGTH_SET_NODE(node, receiver);
3165
3166 node->receiver = receiver;
3167 node->message_loc = TOK2LOC(parser, operator);
3168
3169 node->name = pm_parser_constant_id_constant(parser, name, strlen(name));
3170 return node;
3171}
3172
3177static pm_call_node_t *
3178pm_call_node_variable_call_create(pm_parser_t *parser, pm_token_t *message) {
3179 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3180
3181 node->base.location = TOK2LOC(parser, message);
3182 node->message_loc = TOK2LOC(parser, message);
3183
3184 node->name = pm_parser_constant_id_token(parser, message);
3185 return node;
3186}
3187
3192static PRISM_INLINE bool
3193pm_call_node_writable_p(const pm_parser_t *parser, const pm_call_node_t *node) {
3194 return (
3195 (node->message_loc.length > 0) &&
3196 (parser->start[node->message_loc.start + node->message_loc.length - 1] != '!') &&
3197 (parser->start[node->message_loc.start + node->message_loc.length - 1] != '?') &&
3198 char_is_identifier_start(parser, parser->start + node->message_loc.start, (ptrdiff_t) node->message_loc.length) &&
3199 (node->opening_loc.length == 0) &&
3200 (node->arguments == NULL) &&
3201 (node->block == NULL)
3202 );
3203}
3204
3208static void
3209pm_call_write_read_name_init(pm_parser_t *parser, pm_constant_id_t *read_name, pm_constant_id_t *write_name) {
3210 pm_constant_t *write_constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *write_name);
3211
3212 if (write_constant->length > 0) {
3213 size_t length = write_constant->length - 1;
3214
3215 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
3216 memcpy(memory, write_constant->start, length);
3217
3218 *read_name = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, memory, length);
3219 } else {
3220 // We can get here if the message was missing because of a syntax error.
3221 *read_name = pm_parser_constant_id_constant(parser, "", 0);
3222 }
3223}
3224
3228static pm_call_and_write_node_t *
3229pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3230 assert(target->block == NULL);
3231 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3232
3233 pm_call_and_write_node_t *node = pm_call_and_write_node_new(
3234 parser->arena,
3235 ++parser->node_id,
3236 FL(target),
3237 PM_LOCATION_INIT_NODES(target, value),
3238 target->receiver,
3239 target->call_operator_loc,
3240 target->message_loc,
3241 0,
3242 target->name,
3243 TOK2LOC(parser, operator),
3244 value
3245 );
3246
3247 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3248
3249 // The target is no longer necessary because we've reused its children.
3250 // It is arena-allocated so no explicit free is needed.
3251
3252 return node;
3253}
3254
3259static void
3260pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) {
3261 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
3262 if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) {
3263 pm_node_t *node;
3264 PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) {
3265 if (PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)) {
3266 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_INDEX_KEYWORDS);
3267 break;
3268 }
3269 }
3270 }
3271
3272 if (block != NULL) {
3273 pm_parser_err_node(parser, block, PM_ERR_UNEXPECTED_INDEX_BLOCK);
3274 }
3275 }
3276}
3277
3281static pm_index_and_write_node_t *
3282pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3283 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3284
3285 pm_index_arguments_check(parser, target->arguments, target->block);
3286
3287 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3288
3289 pm_index_and_write_node_t *node = pm_index_and_write_node_new(
3290 parser->arena,
3291 ++parser->node_id,
3292 FL(target),
3293 PM_LOCATION_INIT_NODES(target, value),
3294 target->receiver,
3295 target->call_operator_loc,
3296 target->opening_loc,
3297 target->arguments,
3298 target->closing_loc,
3299 (pm_block_argument_node_t *) target->block,
3300 TOK2LOC(parser, operator),
3301 value
3302 );
3303
3304 // The target is no longer necessary because we've reused its children.
3305 // It is arena-allocated so no explicit free is needed.
3306
3307 return node;
3308}
3309
3313static pm_call_operator_write_node_t *
3314pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3315 assert(target->block == NULL);
3316
3317 pm_call_operator_write_node_t *node = pm_call_operator_write_node_new(
3318 parser->arena,
3319 ++parser->node_id,
3320 FL(target),
3321 PM_LOCATION_INIT_NODES(target, value),
3322 target->receiver,
3323 target->call_operator_loc,
3324 target->message_loc,
3325 0,
3326 target->name,
3327 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
3328 TOK2LOC(parser, operator),
3329 value
3330 );
3331
3332 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3333
3334 // The target is no longer necessary because we've reused its children.
3335 // It is arena-allocated so no explicit free is needed.
3336
3337 return node;
3338}
3339
3343static pm_index_operator_write_node_t *
3344pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3345 pm_index_arguments_check(parser, target->arguments, target->block);
3346
3347 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3348
3349 pm_index_operator_write_node_t *node = pm_index_operator_write_node_new(
3350 parser->arena,
3351 ++parser->node_id,
3352 FL(target),
3353 PM_LOCATION_INIT_NODES(target, value),
3354 target->receiver,
3355 target->call_operator_loc,
3356 target->opening_loc,
3357 target->arguments,
3358 target->closing_loc,
3359 (pm_block_argument_node_t *) target->block,
3360 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
3361 TOK2LOC(parser, operator),
3362 value
3363 );
3364
3365 // The target is no longer necessary because we've reused its children.
3366 // It is arena-allocated so no explicit free is needed.
3367
3368 return node;
3369}
3370
3374static pm_call_or_write_node_t *
3375pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3376 assert(target->block == NULL);
3377 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3378
3379 pm_call_or_write_node_t *node = pm_call_or_write_node_new(
3380 parser->arena,
3381 ++parser->node_id,
3382 FL(target),
3383 PM_LOCATION_INIT_NODES(target, value),
3384 target->receiver,
3385 target->call_operator_loc,
3386 target->message_loc,
3387 0,
3388 target->name,
3389 TOK2LOC(parser, operator),
3390 value
3391 );
3392
3393 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3394
3395 // The target is no longer necessary because we've reused its children.
3396 // It is arena-allocated so no explicit free is needed.
3397
3398 return node;
3399}
3400
3404static pm_index_or_write_node_t *
3405pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3406 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3407
3408 pm_index_arguments_check(parser, target->arguments, target->block);
3409
3410 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3411
3412 pm_index_or_write_node_t *node = pm_index_or_write_node_new(
3413 parser->arena,
3414 ++parser->node_id,
3415 FL(target),
3416 PM_LOCATION_INIT_NODES(target, value),
3417 target->receiver,
3418 target->call_operator_loc,
3419 target->opening_loc,
3420 target->arguments,
3421 target->closing_loc,
3422 (pm_block_argument_node_t *) target->block,
3423 TOK2LOC(parser, operator),
3424 value
3425 );
3426
3427 // The target is no longer necessary because we've reused its children.
3428 // It is arena-allocated so no explicit free is needed.
3429
3430 return node;
3431}
3432
3437static pm_call_target_node_t *
3438pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3439 pm_call_target_node_t *node = pm_call_target_node_new(
3440 parser->arena,
3441 ++parser->node_id,
3442 FL(target),
3443 PM_LOCATION_INIT_NODE(target),
3444 target->receiver,
3445 target->call_operator_loc,
3446 target->name,
3447 target->message_loc
3448 );
3449
3450 /* It is possible to get here where we have parsed an invalid syntax tree
3451 * where the call operator was not present. In that case we will have a
3452 * problem because it is a required location. In this case we need to fill
3453 * it in with a fake location so that the syntax tree remains valid. */
3454 if (node->call_operator_loc.length == 0) {
3455 node->call_operator_loc = target->base.location;
3456 }
3457
3458 // The target is no longer necessary because we've reused its children.
3459 // It is arena-allocated so no explicit free is needed.
3460
3461 return node;
3462}
3463
3468static pm_index_target_node_t *
3469pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3470 pm_index_arguments_check(parser, target->arguments, target->block);
3471 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3472
3473 pm_index_target_node_t *node = pm_index_target_node_new(
3474 parser->arena,
3475 ++parser->node_id,
3476 FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE,
3477 PM_LOCATION_INIT_NODE(target),
3478 target->receiver,
3479 target->opening_loc,
3480 target->arguments,
3481 target->closing_loc,
3482 (pm_block_argument_node_t *) target->block
3483 );
3484
3485 // The target is no longer necessary because we've reused its children.
3486 // It is arena-allocated so no explicit free is needed.
3487
3488 return node;
3489}
3490
3494static pm_capture_pattern_node_t *
3495pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_local_variable_target_node_t *target, const pm_token_t *operator) {
3496 return pm_capture_pattern_node_new(
3497 parser->arena,
3498 ++parser->node_id,
3499 0,
3500 PM_LOCATION_INIT_NODES(value, target),
3501 value,
3502 target,
3503 TOK2LOC(parser, operator)
3504 );
3505}
3506
3510static pm_case_node_t *
3511pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) {
3512 return pm_case_node_new(
3513 parser->arena,
3514 ++parser->node_id,
3515 0,
3516 PM_LOCATION_INIT_TOKENS(parser, case_keyword, end_keyword == NULL ? case_keyword : end_keyword),
3517 predicate,
3518 ((pm_node_list_t) { 0 }),
3519 NULL,
3520 TOK2LOC(parser, case_keyword),
3521 NTOK2LOC(parser, end_keyword)
3522 );
3523}
3524
3528static void
3529pm_case_node_condition_append(pm_arena_t *arena, pm_case_node_t *node, pm_node_t *condition) {
3530 assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
3531
3532 pm_node_list_append(arena, &node->conditions, condition);
3533 PM_NODE_LENGTH_SET_NODE(node, condition);
3534}
3535
3539static void
3540pm_case_node_else_clause_set(pm_case_node_t *node, pm_else_node_t *else_clause) {
3541 node->else_clause = else_clause;
3542 PM_NODE_LENGTH_SET_NODE(node, else_clause);
3543}
3544
3548static void
3549pm_case_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_node_t *node, const pm_token_t *end_keyword) {
3550 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
3551 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
3552}
3553
3557static pm_case_match_node_t *
3558pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate) {
3559 return pm_case_match_node_new(
3560 parser->arena,
3561 ++parser->node_id,
3562 0,
3563 PM_LOCATION_INIT_TOKEN(parser, case_keyword),
3564 predicate,
3565 ((pm_node_list_t) { 0 }),
3566 NULL,
3567 TOK2LOC(parser, case_keyword),
3568 ((pm_location_t) { 0 })
3569 );
3570}
3571
3575static void
3576pm_case_match_node_condition_append(pm_arena_t *arena, pm_case_match_node_t *node, pm_node_t *condition) {
3577 assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
3578
3579 pm_node_list_append(arena, &node->conditions, condition);
3580 PM_NODE_LENGTH_SET_NODE(node, condition);
3581}
3582
3586static void
3587pm_case_match_node_else_clause_set(pm_case_match_node_t *node, pm_else_node_t *else_clause) {
3588 node->else_clause = else_clause;
3589 PM_NODE_LENGTH_SET_NODE(node, else_clause);
3590}
3591
3595static void
3596pm_case_match_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_match_node_t *node, const pm_token_t *end_keyword) {
3597 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
3598 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
3599}
3600
3604static pm_class_node_t *
3605pm_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) {
3606 return pm_class_node_new(
3607 parser->arena,
3608 ++parser->node_id,
3609 0,
3610 PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword),
3611 *locals,
3612 TOK2LOC(parser, class_keyword),
3613 constant_path,
3614 NTOK2LOC(parser, inheritance_operator),
3615 superclass,
3616 body,
3617 TOK2LOC(parser, end_keyword),
3618 pm_parser_constant_id_token(parser, name)
3619 );
3620}
3621
3625static pm_class_variable_and_write_node_t *
3626pm_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) {
3627 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3628
3629 return pm_class_variable_and_write_node_new(
3630 parser->arena,
3631 ++parser->node_id,
3632 0,
3633 PM_LOCATION_INIT_NODES(target, value),
3634 target->name,
3635 target->base.location,
3636 TOK2LOC(parser, operator),
3637 value
3638 );
3639}
3640
3644static pm_class_variable_operator_write_node_t *
3645pm_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) {
3646 return pm_class_variable_operator_write_node_new(
3647 parser->arena,
3648 ++parser->node_id,
3649 0,
3650 PM_LOCATION_INIT_NODES(target, value),
3651 target->name,
3652 target->base.location,
3653 TOK2LOC(parser, operator),
3654 value,
3655 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3656 );
3657}
3658
3662static pm_class_variable_or_write_node_t *
3663pm_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) {
3664 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3665
3666 return pm_class_variable_or_write_node_new(
3667 parser->arena,
3668 ++parser->node_id,
3669 0,
3670 PM_LOCATION_INIT_NODES(target, value),
3671 target->name,
3672 target->base.location,
3673 TOK2LOC(parser, operator),
3674 value
3675 );
3676}
3677
3681static pm_class_variable_read_node_t *
3682pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
3683 assert(token->type == PM_TOKEN_CLASS_VARIABLE);
3684
3685 return pm_class_variable_read_node_new(
3686 parser->arena,
3687 ++parser->node_id,
3688 0,
3689 PM_LOCATION_INIT_TOKEN(parser, token),
3690 pm_parser_constant_id_token(parser, token)
3691 );
3692}
3693
3700static PRISM_INLINE pm_node_flags_t
3701pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) {
3702 if (PM_NODE_TYPE_P(node, PM_ARRAY_NODE) && ((const pm_array_node_t *) node)->opening_loc.length == 0) {
3703 return flags;
3704 }
3705 return 0;
3706}
3707
3711static pm_class_variable_write_node_t *
3712pm_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) {
3713 return pm_class_variable_write_node_new(
3714 parser->arena,
3715 ++parser->node_id,
3716 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3717 PM_LOCATION_INIT_NODES(read_node, value),
3718 read_node->name,
3719 read_node->base.location,
3720 value,
3721 TOK2LOC(parser, operator)
3722 );
3723}
3724
3728static pm_constant_path_and_write_node_t *
3729pm_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) {
3730 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3731
3732 return pm_constant_path_and_write_node_new(
3733 parser->arena,
3734 ++parser->node_id,
3735 0,
3736 PM_LOCATION_INIT_NODES(target, value),
3737 target,
3738 TOK2LOC(parser, operator),
3739 value
3740 );
3741}
3742
3746static pm_constant_path_operator_write_node_t *
3747pm_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) {
3748 return pm_constant_path_operator_write_node_new(
3749 parser->arena,
3750 ++parser->node_id,
3751 0,
3752 PM_LOCATION_INIT_NODES(target, value),
3753 target,
3754 TOK2LOC(parser, operator),
3755 value,
3756 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3757 );
3758}
3759
3763static pm_constant_path_or_write_node_t *
3764pm_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) {
3765 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3766
3767 return pm_constant_path_or_write_node_new(
3768 parser->arena,
3769 ++parser->node_id,
3770 0,
3771 PM_LOCATION_INIT_NODES(target, value),
3772 target,
3773 TOK2LOC(parser, operator),
3774 value
3775 );
3776}
3777
3781static pm_constant_path_node_t *
3782pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_token_t *delimiter, const pm_token_t *name_token) {
3783 pm_assert_value_expression(parser, parent);
3784
3785 pm_constant_id_t name = PM_CONSTANT_ID_UNSET;
3786 if (name_token->type == PM_TOKEN_CONSTANT) {
3787 name = pm_parser_constant_id_token(parser, name_token);
3788 }
3789
3790 return pm_constant_path_node_new(
3791 parser->arena,
3792 ++parser->node_id,
3793 0,
3794 (parent == NULL) ? PM_LOCATION_INIT_TOKENS(parser, delimiter, name_token) : PM_LOCATION_INIT_NODE_TOKEN(parser, parent, name_token),
3795 parent,
3796 name,
3797 TOK2LOC(parser, delimiter),
3798 TOK2LOC(parser, name_token)
3799 );
3800}
3801
3805static pm_constant_path_write_node_t *
3806pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3807 return pm_constant_path_write_node_new(
3808 parser->arena,
3809 ++parser->node_id,
3810 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3811 PM_LOCATION_INIT_NODES(target, value),
3812 target,
3813 TOK2LOC(parser, operator),
3814 value
3815 );
3816}
3817
3821static pm_constant_and_write_node_t *
3822pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3823 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3824
3825 return pm_constant_and_write_node_new(
3826 parser->arena,
3827 ++parser->node_id,
3828 0,
3829 PM_LOCATION_INIT_NODES(target, value),
3830 target->name,
3831 target->base.location,
3832 TOK2LOC(parser, operator),
3833 value
3834 );
3835}
3836
3840static pm_constant_operator_write_node_t *
3841pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3842 return pm_constant_operator_write_node_new(
3843 parser->arena,
3844 ++parser->node_id,
3845 0,
3846 PM_LOCATION_INIT_NODES(target, value),
3847 target->name,
3848 target->base.location,
3849 TOK2LOC(parser, operator),
3850 value,
3851 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3852 );
3853}
3854
3858static pm_constant_or_write_node_t *
3859pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3860 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3861
3862 return pm_constant_or_write_node_new(
3863 parser->arena,
3864 ++parser->node_id,
3865 0,
3866 PM_LOCATION_INIT_NODES(target, value),
3867 target->name,
3868 target->base.location,
3869 TOK2LOC(parser, operator),
3870 value
3871 );
3872}
3873
3877static pm_constant_read_node_t *
3878pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
3879 assert(name->type == PM_TOKEN_CONSTANT || name->type == 0);
3880
3881 return pm_constant_read_node_new(
3882 parser->arena,
3883 ++parser->node_id,
3884 0,
3885 PM_LOCATION_INIT_TOKEN(parser, name),
3886 pm_parser_constant_id_token(parser, name)
3887 );
3888}
3889
3893static pm_constant_write_node_t *
3894pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3895 return pm_constant_write_node_new(
3896 parser->arena,
3897 ++parser->node_id,
3898 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3899 PM_LOCATION_INIT_NODES(target, value),
3900 target->name,
3901 target->base.location,
3902 value,
3903 TOK2LOC(parser, operator)
3904 );
3905}
3906
3910static void
3911pm_def_node_receiver_check(pm_parser_t *parser, const pm_node_t *node) {
3912 switch (PM_NODE_TYPE(node)) {
3913 case PM_BEGIN_NODE: {
3914 const pm_begin_node_t *cast = (pm_begin_node_t *) node;
3915 if (cast->statements != NULL) pm_def_node_receiver_check(parser, UP(cast->statements));
3916 break;
3917 }
3918 case PM_PARENTHESES_NODE: {
3919 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
3920 if (cast->body != NULL) pm_def_node_receiver_check(parser, cast->body);
3921 break;
3922 }
3923 case PM_STATEMENTS_NODE: {
3924 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3925 pm_def_node_receiver_check(parser, cast->body.nodes[cast->body.size - 1]);
3926 break;
3927 }
3928 case PM_ARRAY_NODE:
3929 case PM_FLOAT_NODE:
3930 case PM_IMAGINARY_NODE:
3931 case PM_INTEGER_NODE:
3932 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3933 case PM_INTERPOLATED_STRING_NODE:
3934 case PM_INTERPOLATED_SYMBOL_NODE:
3935 case PM_INTERPOLATED_X_STRING_NODE:
3936 case PM_RATIONAL_NODE:
3937 case PM_REGULAR_EXPRESSION_NODE:
3938 case PM_SOURCE_ENCODING_NODE:
3939 case PM_SOURCE_FILE_NODE:
3940 case PM_SOURCE_LINE_NODE:
3941 case PM_STRING_NODE:
3942 case PM_SYMBOL_NODE:
3943 case PM_X_STRING_NODE:
3944 pm_parser_err_node(parser, node, PM_ERR_SINGLETON_FOR_LITERALS);
3945 break;
3946 default:
3947 break;
3948 }
3949}
3950
3954static pm_def_node_t *
3955pm_def_node_create(
3956 pm_parser_t *parser,
3957 pm_constant_id_t name,
3958 const pm_token_t *name_loc,
3959 pm_node_t *receiver,
3960 pm_parameters_node_t *parameters,
3961 pm_node_t *body,
3962 pm_constant_id_list_t *locals,
3963 const pm_token_t *def_keyword,
3964 const pm_token_t *operator,
3965 const pm_token_t *lparen,
3966 const pm_token_t *rparen,
3967 const pm_token_t *equal,
3968 const pm_token_t *end_keyword
3969) {
3970 if (receiver != NULL) {
3971 pm_def_node_receiver_check(parser, receiver);
3972 }
3973
3974 return pm_def_node_new(
3975 parser->arena,
3976 ++parser->node_id,
3977 0,
3978 (end_keyword == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, def_keyword, body) : PM_LOCATION_INIT_TOKENS(parser, def_keyword, end_keyword),
3979 name,
3980 TOK2LOC(parser, name_loc),
3981 receiver,
3982 parameters,
3983 body,
3984 *locals,
3985 TOK2LOC(parser, def_keyword),
3986 NTOK2LOC(parser, operator),
3987 NTOK2LOC(parser, lparen),
3988 NTOK2LOC(parser, rparen),
3989 NTOK2LOC(parser, equal),
3990 NTOK2LOC(parser, end_keyword)
3991 );
3992}
3993
3997static pm_defined_node_t *
3998pm_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) {
3999 return pm_defined_node_new(
4000 parser->arena,
4001 ++parser->node_id,
4002 0,
4003 (rparen == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, value) : PM_LOCATION_INIT_TOKENS(parser, keyword, rparen),
4004 NTOK2LOC(parser, lparen),
4005 value,
4006 NTOK2LOC(parser, rparen),
4007 TOK2LOC(parser, keyword)
4008 );
4009}
4010
4014static pm_else_node_t *
4015pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
4016 return pm_else_node_new(
4017 parser->arena,
4018 ++parser->node_id,
4019 0,
4020 ((end_keyword == NULL) && (statements != NULL)) ? PM_LOCATION_INIT_TOKEN_NODE(parser, else_keyword, statements) : PM_LOCATION_INIT_TOKENS(parser, else_keyword, end_keyword),
4021 TOK2LOC(parser, else_keyword),
4022 statements,
4023 NTOK2LOC(parser, end_keyword)
4024 );
4025}
4026
4030static pm_embedded_statements_node_t *
4031pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
4032 return pm_embedded_statements_node_new(
4033 parser->arena,
4034 ++parser->node_id,
4035 0,
4036 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
4037 TOK2LOC(parser, opening),
4038 statements,
4039 TOK2LOC(parser, closing)
4040 );
4041}
4042
4046static pm_embedded_variable_node_t *
4047pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
4048 return pm_embedded_variable_node_new(
4049 parser->arena,
4050 ++parser->node_id,
4051 0,
4052 PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable),
4053 TOK2LOC(parser, operator),
4054 variable
4055 );
4056}
4057
4061static pm_ensure_node_t *
4062pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
4063 return pm_ensure_node_new(
4064 parser->arena,
4065 ++parser->node_id,
4066 0,
4067 PM_LOCATION_INIT_TOKENS(parser, ensure_keyword, end_keyword),
4068 TOK2LOC(parser, ensure_keyword),
4069 statements,
4070 TOK2LOC(parser, end_keyword)
4071 );
4072}
4073
4077static pm_false_node_t *
4078pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) {
4079 assert(token->type == PM_TOKEN_KEYWORD_FALSE);
4080
4081 return pm_false_node_new(
4082 parser->arena,
4083 ++parser->node_id,
4084 PM_NODE_FLAG_STATIC_LITERAL,
4085 PM_LOCATION_INIT_TOKEN(parser, token)
4086 );
4087}
4088
4093static pm_find_pattern_node_t *
4094pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) {
4095 assert(nodes->size >= 2);
4096 pm_node_t *left = nodes->nodes[0];
4097 pm_node_t *right = nodes->nodes[nodes->size - 1];
4098
4099 assert(PM_NODE_TYPE_P(left, PM_SPLAT_NODE));
4100 assert(PM_NODE_TYPE_P(right, PM_SPLAT_NODE));
4101
4102 pm_find_pattern_node_t *node = pm_find_pattern_node_new(
4103 parser->arena,
4104 ++parser->node_id,
4105 0,
4106 PM_LOCATION_INIT_NODES(left, right),
4107 NULL,
4108 (pm_splat_node_t *) left,
4109 ((pm_node_list_t) { 0 }),
4110 (pm_splat_node_t *) right,
4111 ((pm_location_t) { 0 }),
4112 ((pm_location_t) { 0 })
4113 );
4114
4115 // For now we're going to just copy over each pointer manually. This could be
4116 // much more efficient, as we could instead resize the node list to only point
4117 // to 1...-1.
4118 for (size_t index = 1; index < nodes->size - 1; index++) {
4119 pm_node_list_append(parser->arena, &node->requireds, nodes->nodes[index]);
4120 }
4121
4122 return node;
4123}
4124
4129static double
4130pm_double_parse(pm_parser_t *parser, const pm_token_t *token) {
4131 ptrdiff_t diff = token->end - token->start;
4132 if (diff <= 0) return 0.0;
4133
4134 // First, get a buffer of the content.
4135 size_t length = (size_t) diff;
4136 const size_t buffer_size = sizeof(char) * (length + 1);
4137 char *buffer = xmalloc(buffer_size);
4138 memcpy((void *) buffer, token->start, length);
4139
4140 // Next, determine if we need to replace the decimal point because of
4141 // locale-specific options, and then normalize them if we have to.
4142 char decimal_point = *localeconv()->decimal_point;
4143 if (decimal_point != '.') {
4144 for (size_t index = 0; index < length; index++) {
4145 if (buffer[index] == '.') buffer[index] = decimal_point;
4146 }
4147 }
4148
4149 /*
4150 * Next, handle underscores by removing them from the buffer in a single
4151 * pass to avoid quadratic-time copies.
4152 */
4153 {
4154 size_t write = 0;
4155 for (size_t read = 0; read < length; read++) {
4156 if (buffer[read] != '_') buffer[write++] = buffer[read];
4157 }
4158 length = write;
4159 }
4160
4161 // Null-terminate the buffer so that strtod cannot read off the end.
4162 buffer[length] = '\0';
4163
4164 // Now, call strtod to parse the value. Note that CRuby has their own
4165 // version of strtod which avoids locales. We're okay using the locale-aware
4166 // version because we've already validated through the parser that the token
4167 // is in a valid format.
4168 errno = 0;
4169 char *eptr;
4170 double value = strtod(buffer, &eptr);
4171
4172 // This should never happen, because we've already checked that the token
4173 // is in a valid format. However it's good to be safe.
4174 if ((eptr != buffer + length) || (errno != 0 && errno != ERANGE)) {
4175 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, PM_ERR_FLOAT_PARSE);
4176 xfree_sized(buffer, buffer_size);
4177 return 0.0;
4178 }
4179
4180 // If errno is set, then it should only be ERANGE. At this point we need to
4181 // check if it's infinity (it should be).
4182 if (errno == ERANGE && PRISM_ISINF(value)) {
4183 int warn_width;
4184 const char *ellipsis;
4185
4186 if (length > 20) {
4187 warn_width = 20;
4188 ellipsis = "...";
4189 } else {
4190 warn_width = (int) length;
4191 ellipsis = "";
4192 }
4193
4194 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);
4195 value = (value < 0.0) ? -HUGE_VAL : HUGE_VAL;
4196 }
4197
4198 // Finally we can free the buffer and return the value.
4199 xfree_sized(buffer, buffer_size);
4200 return value;
4201}
4202
4206static pm_float_node_t *
4207pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) {
4208 assert(token->type == PM_TOKEN_FLOAT);
4209
4210 return pm_float_node_new(
4211 parser->arena,
4212 ++parser->node_id,
4213 PM_NODE_FLAG_STATIC_LITERAL,
4214 PM_LOCATION_INIT_TOKEN(parser, token),
4215 pm_double_parse(parser, token)
4216 );
4217}
4218
4222static pm_imaginary_node_t *
4223pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
4224 assert(token->type == PM_TOKEN_FLOAT_IMAGINARY);
4225
4226 return pm_imaginary_node_new(
4227 parser->arena,
4228 ++parser->node_id,
4229 PM_NODE_FLAG_STATIC_LITERAL,
4230 PM_LOCATION_INIT_TOKEN(parser, token),
4231 UP(pm_float_node_create(parser, &((pm_token_t) {
4232 .type = PM_TOKEN_FLOAT,
4233 .start = token->start,
4234 .end = token->end - 1
4235 })))
4236 );
4237}
4238
4242static pm_rational_node_t *
4243pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
4244 assert(token->type == PM_TOKEN_FLOAT_RATIONAL);
4245
4246 pm_rational_node_t *node = pm_rational_node_new(
4247 parser->arena,
4248 ++parser->node_id,
4249 PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL,
4250 PM_LOCATION_INIT_TOKEN(parser, token),
4251 ((pm_integer_t) { 0 }),
4252 ((pm_integer_t) { 0 })
4253 );
4254
4255 const uint8_t *start = token->start;
4256 const uint8_t *end = token->end - 1; // r
4257
4258 while (start < end && *start == '0') start++; // 0.1 -> .1
4259 while (end > start && end[-1] == '0') end--; // 1.0 -> 1.
4260
4261 size_t length = (size_t) (end - start);
4262 if (length == 1) {
4263 node->denominator.value = 1;
4264 return node;
4265 }
4266
4267 const uint8_t *point = memchr(start, '.', length);
4268 assert(point && "should have a decimal point");
4269
4270 uint8_t *digits = xmalloc(length);
4271 if (digits == NULL) {
4272 fputs("[pm_float_node_rational_create] Failed to allocate memory", stderr);
4273 abort();
4274 }
4275
4276 memcpy(digits, start, (unsigned long) (point - start));
4277 memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1));
4278 pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1);
4279
4280 size_t fract_length = 0;
4281 for (const uint8_t *fract = point; fract < end; ++fract) {
4282 if (*fract != '_') ++fract_length;
4283 }
4284 digits[0] = '1';
4285 if (fract_length > 1) memset(digits + 1, '0', fract_length - 1);
4286 pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + fract_length);
4287 xfree_sized(digits, length);
4288
4289 pm_integers_reduce(&node->numerator, &node->denominator);
4290 pm_integer_arena_move(parser->arena, &node->numerator);
4291 pm_integer_arena_move(parser->arena, &node->denominator);
4292 return node;
4293}
4294
4299static pm_imaginary_node_t *
4300pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
4301 assert(token->type == PM_TOKEN_FLOAT_RATIONAL_IMAGINARY);
4302
4303 return pm_imaginary_node_new(
4304 parser->arena,
4305 ++parser->node_id,
4306 PM_NODE_FLAG_STATIC_LITERAL,
4307 PM_LOCATION_INIT_TOKEN(parser, token),
4308 UP(pm_float_node_rational_create(parser, &((pm_token_t) {
4309 .type = PM_TOKEN_FLOAT_RATIONAL,
4310 .start = token->start,
4311 .end = token->end - 1
4312 })))
4313 );
4314}
4315
4319static pm_for_node_t *
4320pm_for_node_create(
4321 pm_parser_t *parser,
4322 pm_node_t *index,
4323 pm_node_t *collection,
4324 pm_statements_node_t *statements,
4325 const pm_token_t *for_keyword,
4326 const pm_token_t *in_keyword,
4327 const pm_token_t *do_keyword,
4328 const pm_token_t *end_keyword
4329) {
4330 return pm_for_node_new(
4331 parser->arena,
4332 ++parser->node_id,
4333 0,
4334 PM_LOCATION_INIT_TOKENS(parser, for_keyword, end_keyword),
4335 index,
4336 collection,
4337 statements,
4338 TOK2LOC(parser, for_keyword),
4339 TOK2LOC(parser, in_keyword),
4340 NTOK2LOC(parser, do_keyword),
4341 TOK2LOC(parser, end_keyword)
4342 );
4343}
4344
4348static pm_forwarding_arguments_node_t *
4349pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token) {
4350 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4351
4352 return pm_forwarding_arguments_node_new(
4353 parser->arena,
4354 ++parser->node_id,
4355 0,
4356 PM_LOCATION_INIT_TOKEN(parser, token)
4357 );
4358}
4359
4363static pm_forwarding_parameter_node_t *
4364pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
4365 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4366
4367 return pm_forwarding_parameter_node_new(
4368 parser->arena,
4369 ++parser->node_id,
4370 0,
4371 PM_LOCATION_INIT_TOKEN(parser, token)
4372 );
4373}
4374
4378static pm_forwarding_super_node_t *
4379pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm_arguments_t *arguments) {
4380 assert(arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_NODE));
4381 assert(token->type == PM_TOKEN_KEYWORD_SUPER);
4382
4383 pm_block_node_t *block = NULL;
4384 if (arguments->block != NULL) {
4385 block = (pm_block_node_t *) arguments->block;
4386 }
4387
4388 return pm_forwarding_super_node_new(
4389 parser->arena,
4390 ++parser->node_id,
4391 0,
4392 (block == NULL) ? PM_LOCATION_INIT_TOKEN(parser, token) : PM_LOCATION_INIT_TOKEN_NODE(parser, token, block),
4393 PM_LOCATION_INIT_TOKEN(parser, token),
4394 block
4395 );
4396}
4397
4402static pm_hash_pattern_node_t *
4403pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
4404 return pm_hash_pattern_node_new(
4405 parser->arena,
4406 ++parser->node_id,
4407 0,
4408 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
4409 NULL,
4410 ((pm_node_list_t) { 0 }),
4411 NULL,
4412 TOK2LOC(parser, opening),
4413 TOK2LOC(parser, closing)
4414 );
4415}
4416
4420static pm_hash_pattern_node_t *
4421pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *elements, pm_node_t *rest) {
4422 uint32_t start;
4423 uint32_t end;
4424
4425 if (elements->size > 0) {
4426 if (rest) {
4427 start = MIN(PM_NODE_START(rest), PM_NODE_START(elements->nodes[0]));
4428 end = MAX(PM_NODE_END(rest), PM_NODE_END(elements->nodes[elements->size - 1]));
4429 } else {
4430 start = PM_NODE_START(elements->nodes[0]);
4431 end = PM_NODE_END(elements->nodes[elements->size - 1]);
4432 }
4433 } else {
4434 assert(rest != NULL);
4435 start = PM_NODE_START(rest);
4436 end = PM_NODE_END(rest);
4437 }
4438
4439 pm_hash_pattern_node_t *node = pm_hash_pattern_node_new(
4440 parser->arena,
4441 ++parser->node_id,
4442 0,
4443 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4444 NULL,
4445 ((pm_node_list_t) { 0 }),
4446 rest,
4447 ((pm_location_t) { 0 }),
4448 ((pm_location_t) { 0 })
4449 );
4450
4451 pm_node_list_concat(parser->arena, &node->elements, elements);
4452 return node;
4453}
4454
4458static pm_constant_id_t
4459pm_global_variable_write_name(pm_parser_t *parser, const pm_node_t *target) {
4460 switch (PM_NODE_TYPE(target)) {
4461 case PM_GLOBAL_VARIABLE_READ_NODE:
4462 return ((pm_global_variable_read_node_t *) target)->name;
4463 case PM_BACK_REFERENCE_READ_NODE:
4464 return ((pm_back_reference_read_node_t *) target)->name;
4465 case PM_NUMBERED_REFERENCE_READ_NODE:
4466 // This will only ever happen in the event of a syntax error, but we
4467 // still need to provide something for the node.
4468 return pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target));
4469 default:
4470 assert(false && "unreachable");
4471 return (pm_constant_id_t) -1;
4472 }
4473}
4474
4478static pm_global_variable_and_write_node_t *
4479pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4480 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4481
4482 return pm_global_variable_and_write_node_new(
4483 parser->arena,
4484 ++parser->node_id,
4485 0,
4486 PM_LOCATION_INIT_NODES(target, value),
4487 pm_global_variable_write_name(parser, target),
4488 target->location,
4489 TOK2LOC(parser, operator),
4490 value
4491 );
4492}
4493
4497static pm_global_variable_operator_write_node_t *
4498pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4499 return pm_global_variable_operator_write_node_new(
4500 parser->arena,
4501 ++parser->node_id,
4502 0,
4503 PM_LOCATION_INIT_NODES(target, value),
4504 pm_global_variable_write_name(parser, target),
4505 target->location,
4506 TOK2LOC(parser, operator),
4507 value,
4508 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
4509 );
4510}
4511
4515static pm_global_variable_or_write_node_t *
4516pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4517 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4518
4519 return pm_global_variable_or_write_node_new(
4520 parser->arena,
4521 ++parser->node_id,
4522 0,
4523 PM_LOCATION_INIT_NODES(target, value),
4524 pm_global_variable_write_name(parser, target),
4525 target->location,
4526 TOK2LOC(parser, operator),
4527 value
4528 );
4529}
4530
4534static pm_global_variable_read_node_t *
4535pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
4536 return pm_global_variable_read_node_new(
4537 parser->arena,
4538 ++parser->node_id,
4539 0,
4540 PM_LOCATION_INIT_TOKEN(parser, name),
4541 pm_parser_constant_id_token(parser, name)
4542 );
4543}
4544
4548static pm_global_variable_read_node_t *
4549pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name) {
4550 return pm_global_variable_read_node_new(
4551 parser->arena,
4552 ++parser->node_id,
4553 0,
4554 PM_LOCATION_INIT_UNSET,
4555 name
4556 );
4557}
4558
4562static pm_global_variable_write_node_t *
4563pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4564 return pm_global_variable_write_node_new(
4565 parser->arena,
4566 ++parser->node_id,
4567 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
4568 PM_LOCATION_INIT_NODES(target, value),
4569 pm_global_variable_write_name(parser, target),
4570 target->location,
4571 value,
4572 TOK2LOC(parser, operator)
4573 );
4574}
4575
4579static pm_global_variable_write_node_t *
4580pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name, pm_node_t *value) {
4581 return pm_global_variable_write_node_new(
4582 parser->arena,
4583 ++parser->node_id,
4584 0,
4585 PM_LOCATION_INIT_UNSET,
4586 name,
4587 ((pm_location_t) { 0 }),
4588 value,
4589 ((pm_location_t) { 0 })
4590 );
4591}
4592
4596static pm_hash_node_t *
4597pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) {
4598 assert(opening != NULL);
4599
4600 return pm_hash_node_new(
4601 parser->arena,
4602 ++parser->node_id,
4603 PM_NODE_FLAG_STATIC_LITERAL,
4604 PM_LOCATION_INIT_TOKEN(parser, opening),
4605 TOK2LOC(parser, opening),
4606 ((pm_node_list_t) { 0 }),
4607 ((pm_location_t) { 0 })
4608 );
4609}
4610
4614static PRISM_INLINE void
4615pm_hash_node_elements_append(pm_arena_t *arena, pm_hash_node_t *hash, pm_node_t *element) {
4616 pm_node_list_append(arena, &hash->elements, element);
4617
4618 bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE);
4619 if (static_literal) {
4620 pm_assoc_node_t *assoc = (pm_assoc_node_t *) element;
4621 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);
4622 static_literal = static_literal && PM_NODE_FLAG_P(assoc->key, PM_NODE_FLAG_STATIC_LITERAL);
4623 static_literal = static_literal && PM_NODE_FLAG_P(assoc, PM_NODE_FLAG_STATIC_LITERAL);
4624 }
4625
4626 if (!static_literal) {
4627 pm_node_flag_unset(UP(hash), PM_NODE_FLAG_STATIC_LITERAL);
4628 }
4629}
4630
4631static PRISM_INLINE void
4632pm_hash_node_closing_loc_set(const pm_parser_t *parser, pm_hash_node_t *hash, pm_token_t *token) {
4633 PM_NODE_LENGTH_SET_TOKEN(parser, hash, token);
4634 hash->closing_loc = TOK2LOC(parser, token);
4635}
4636
4640static pm_if_node_t *
4641pm_if_node_create(pm_parser_t *parser,
4642 const pm_token_t *if_keyword,
4643 pm_node_t *predicate,
4644 const pm_token_t *then_keyword,
4645 pm_statements_node_t *statements,
4646 pm_node_t *subsequent,
4647 const pm_token_t *end_keyword
4648) {
4649 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4650
4651 uint32_t start = PM_TOKEN_START(parser, if_keyword);
4652 uint32_t end;
4653
4654 if (end_keyword != NULL) {
4655 end = PM_TOKEN_END(parser, end_keyword);
4656 } else if (subsequent != NULL) {
4657 end = PM_NODE_END(subsequent);
4658 } else if (pm_statements_node_body_length(statements) != 0) {
4659 end = PM_NODE_END(statements);
4660 } else {
4661 end = PM_NODE_END(predicate);
4662 }
4663
4664 return pm_if_node_new(
4665 parser->arena,
4666 ++parser->node_id,
4667 PM_NODE_FLAG_NEWLINE,
4668 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4669 TOK2LOC(parser, if_keyword),
4670 predicate,
4671 NTOK2LOC(parser, then_keyword),
4672 statements,
4673 subsequent,
4674 NTOK2LOC(parser, end_keyword)
4675 );
4676}
4677
4681static pm_if_node_t *
4682pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *if_keyword, pm_node_t *predicate) {
4683 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4684
4685 pm_statements_node_t *statements = pm_statements_node_create(parser);
4686 pm_statements_node_body_append(parser, statements, statement, true);
4687
4688 return pm_if_node_new(
4689 parser->arena,
4690 ++parser->node_id,
4691 PM_NODE_FLAG_NEWLINE,
4692 PM_LOCATION_INIT_NODES(statement, predicate),
4693 TOK2LOC(parser, if_keyword),
4694 predicate,
4695 ((pm_location_t) { 0 }),
4696 statements,
4697 NULL,
4698 ((pm_location_t) { 0 })
4699 );
4700}
4701
4705static pm_if_node_t *
4706pm_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) {
4707 pm_assert_value_expression(parser, predicate);
4708 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4709
4710 pm_statements_node_t *if_statements = pm_statements_node_create(parser);
4711 pm_statements_node_body_append(parser, if_statements, true_expression, true);
4712
4713 pm_statements_node_t *else_statements = pm_statements_node_create(parser);
4714 pm_statements_node_body_append(parser, else_statements, false_expression, true);
4715
4716 pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, NULL);
4717 return pm_if_node_new(
4718 parser->arena,
4719 ++parser->node_id,
4720 PM_NODE_FLAG_NEWLINE,
4721 PM_LOCATION_INIT_NODES(predicate, false_expression),
4722 ((pm_location_t) { 0 }),
4723 predicate,
4724 TOK2LOC(parser, qmark),
4725 if_statements,
4726 UP(else_node),
4727 ((pm_location_t) { 0 })
4728 );
4729}
4730
4731static PRISM_INLINE void
4732pm_if_node_end_keyword_loc_set(const pm_parser_t *parser, pm_if_node_t *node, const pm_token_t *keyword) {
4733 PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword);
4734 node->end_keyword_loc = TOK2LOC(parser, keyword);
4735}
4736
4737static PRISM_INLINE void
4738pm_else_node_end_keyword_loc_set(const pm_parser_t *parser, pm_else_node_t *node, const pm_token_t *keyword) {
4739 PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword);
4740 node->end_keyword_loc = TOK2LOC(parser, keyword);
4741}
4742
4746static pm_implicit_node_t *
4747pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) {
4748 return pm_implicit_node_new(
4749 parser->arena,
4750 ++parser->node_id,
4751 0,
4752 PM_LOCATION_INIT_NODE(value),
4753 value
4754 );
4755}
4756
4760static pm_implicit_rest_node_t *
4761pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) {
4762 assert(token->type == PM_TOKEN_COMMA);
4763
4764 return pm_implicit_rest_node_new(
4765 parser->arena,
4766 ++parser->node_id,
4767 0,
4768 PM_LOCATION_INIT_TOKEN(parser, token)
4769 );
4770}
4771
4775static pm_integer_node_t *
4776pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4777 assert(token->type == PM_TOKEN_INTEGER);
4778
4779 pm_integer_node_t *node = pm_integer_node_new(
4780 parser->arena,
4781 ++parser->node_id,
4782 base | PM_NODE_FLAG_STATIC_LITERAL,
4783 PM_LOCATION_INIT_TOKEN(parser, token),
4784 ((pm_integer_t) { 0 })
4785 );
4786
4787 if (parser->integer.lexed) {
4788 // The value was already computed during lexing.
4789 node->value.value = parser->integer.value;
4790 parser->integer.lexed = false;
4791 } else {
4792 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4793 switch (base) {
4794 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4795 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4796 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4797 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4798 default: assert(false && "unreachable"); break;
4799 }
4800
4801 pm_integer_parse(&node->value, integer_base, token->start, token->end);
4802 pm_integer_arena_move(parser->arena, &node->value);
4803 }
4804
4805 return node;
4806}
4807
4812static pm_imaginary_node_t *
4813pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4814 assert(token->type == PM_TOKEN_INTEGER_IMAGINARY);
4815
4816 return pm_imaginary_node_new(
4817 parser->arena,
4818 ++parser->node_id,
4819 PM_NODE_FLAG_STATIC_LITERAL,
4820 PM_LOCATION_INIT_TOKEN(parser, token),
4821 UP(pm_integer_node_create(parser, base, &((pm_token_t) {
4822 .type = PM_TOKEN_INTEGER,
4823 .start = token->start,
4824 .end = token->end - 1
4825 })))
4826 );
4827}
4828
4833static pm_rational_node_t *
4834pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4835 assert(token->type == PM_TOKEN_INTEGER_RATIONAL);
4836
4837 pm_rational_node_t *node = pm_rational_node_new(
4838 parser->arena,
4839 ++parser->node_id,
4840 base | PM_NODE_FLAG_STATIC_LITERAL,
4841 PM_LOCATION_INIT_TOKEN(parser, token),
4842 ((pm_integer_t) { 0 }),
4843 ((pm_integer_t) { .value = 1 })
4844 );
4845
4846 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4847 switch (base) {
4848 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4849 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4850 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4851 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4852 default: assert(false && "unreachable"); break;
4853 }
4854
4855 pm_integer_parse(&node->numerator, integer_base, token->start, token->end - 1);
4856 pm_integer_arena_move(parser->arena, &node->numerator);
4857
4858 return node;
4859}
4860
4865static pm_imaginary_node_t *
4866pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4867 assert(token->type == PM_TOKEN_INTEGER_RATIONAL_IMAGINARY);
4868
4869 return pm_imaginary_node_new(
4870 parser->arena,
4871 ++parser->node_id,
4872 PM_NODE_FLAG_STATIC_LITERAL,
4873 PM_LOCATION_INIT_TOKEN(parser, token),
4874 UP(pm_integer_node_rational_create(parser, base, &((pm_token_t) {
4875 .type = PM_TOKEN_INTEGER_RATIONAL,
4876 .start = token->start,
4877 .end = token->end - 1
4878 })))
4879 );
4880}
4881
4885static pm_in_node_t *
4886pm_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) {
4887 uint32_t start = PM_TOKEN_START(parser, in_keyword);
4888 uint32_t end;
4889
4890 if (statements != NULL) {
4891 end = PM_NODE_END(statements);
4892 } else if (then_keyword != NULL) {
4893 end = PM_TOKEN_END(parser, then_keyword);
4894 } else {
4895 end = PM_NODE_END(pattern);
4896 }
4897
4898 return pm_in_node_new(
4899 parser->arena,
4900 ++parser->node_id,
4901 0,
4902 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4903 pattern,
4904 statements,
4905 TOK2LOC(parser, in_keyword),
4906 NTOK2LOC(parser, then_keyword)
4907 );
4908}
4909
4913static pm_instance_variable_and_write_node_t *
4914pm_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) {
4915 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4916
4917 return pm_instance_variable_and_write_node_new(
4918 parser->arena,
4919 ++parser->node_id,
4920 0,
4921 PM_LOCATION_INIT_NODES(target, value),
4922 target->name,
4923 target->base.location,
4924 TOK2LOC(parser, operator),
4925 value
4926 );
4927}
4928
4932static pm_instance_variable_operator_write_node_t *
4933pm_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) {
4934 return pm_instance_variable_operator_write_node_new(
4935 parser->arena,
4936 ++parser->node_id,
4937 0,
4938 PM_LOCATION_INIT_NODES(target, value),
4939 target->name,
4940 target->base.location,
4941 TOK2LOC(parser, operator),
4942 value,
4943 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
4944 );
4945}
4946
4950static pm_instance_variable_or_write_node_t *
4951pm_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) {
4952 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4953
4954 return pm_instance_variable_or_write_node_new(
4955 parser->arena,
4956 ++parser->node_id,
4957 0,
4958 PM_LOCATION_INIT_NODES(target, value),
4959 target->name,
4960 target->base.location,
4961 TOK2LOC(parser, operator),
4962 value
4963 );
4964}
4965
4969static pm_instance_variable_read_node_t *
4970pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
4971 assert(token->type == PM_TOKEN_INSTANCE_VARIABLE);
4972
4973 return pm_instance_variable_read_node_new(
4974 parser->arena,
4975 ++parser->node_id,
4976 0,
4977 PM_LOCATION_INIT_TOKEN(parser, token),
4978 pm_parser_constant_id_token(parser, token)
4979 );
4980}
4981
4986static pm_instance_variable_write_node_t *
4987pm_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) {
4988 return pm_instance_variable_write_node_new(
4989 parser->arena,
4990 ++parser->node_id,
4991 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
4992 PM_LOCATION_INIT_NODES(read_node, value),
4993 read_node->name,
4994 read_node->base.location,
4995 value,
4996 TOK2LOC(parser, operator)
4997 );
4998}
4999
5005static void
5006pm_interpolated_node_append(pm_arena_t *arena, pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) {
5007 switch (PM_NODE_TYPE(part)) {
5008 case PM_STRING_NODE:
5009 pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
5010 break;
5011 case PM_EMBEDDED_STATEMENTS_NODE: {
5012 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
5013 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
5014
5015 if (embedded == NULL) {
5016 // If there are no statements or more than one statement, then
5017 // we lose the static literal flag.
5018 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
5019 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
5020 // If the embedded statement is a string, then we can keep the
5021 // static literal flag and mark the string as frozen.
5022 pm_node_flag_set(embedded, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
5023 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
5024 // If the embedded statement is an interpolated string and it's
5025 // a static literal, then we can keep the static literal flag.
5026 } else {
5027 // Otherwise we lose the static literal flag.
5028 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
5029 }
5030
5031 break;
5032 }
5033 case PM_EMBEDDED_VARIABLE_NODE:
5034 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
5035 break;
5036 default:
5037 assert(false && "unexpected node type");
5038 break;
5039 }
5040
5041 pm_node_list_append(arena, parts, part);
5042}
5043
5047static pm_interpolated_regular_expression_node_t *
5048pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening) {
5049 return pm_interpolated_regular_expression_node_new(
5050 parser->arena,
5051 ++parser->node_id,
5052 PM_NODE_FLAG_STATIC_LITERAL,
5053 PM_LOCATION_INIT_TOKEN(parser, opening),
5054 TOK2LOC(parser, opening),
5055 ((pm_node_list_t) { 0 }),
5056 TOK2LOC(parser, opening)
5057 );
5058}
5059
5060static PRISM_INLINE void
5061pm_interpolated_regular_expression_node_append(pm_arena_t *arena, pm_interpolated_regular_expression_node_t *node, pm_node_t *part) {
5062 if (PM_NODE_START(node) > PM_NODE_START(part)) {
5063 PM_NODE_START_SET_NODE(node, part);
5064 }
5065 if (PM_NODE_END(node) < PM_NODE_END(part)) {
5066 PM_NODE_LENGTH_SET_NODE(node, part);
5067 }
5068
5069 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5070}
5071
5072static PRISM_INLINE void
5073pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) {
5074 node->closing_loc = TOK2LOC(parser, closing);
5075 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5076 pm_node_flag_set(UP(node), pm_regular_expression_flags_create(parser, closing));
5077}
5078
5102static PRISM_INLINE void
5103pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_node_t *node, pm_node_t *part) {
5104 pm_arena_t *arena = parser->arena;
5105#define CLEAR_FLAGS(node) \
5106 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))
5107
5108#define MUTABLE_FLAGS(node) \
5109 node->base.flags = (pm_node_flags_t) ((FL(node) | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN);
5110
5111 if (node->parts.size == 0 && node->opening_loc.length == 0) {
5112 PM_NODE_START_SET_NODE(node, part);
5113 }
5114
5115 if (PM_NODE_END(part) > PM_NODE_END(node)) {
5116 PM_NODE_LENGTH_SET_NODE(node, part);
5117 }
5118
5119 switch (PM_NODE_TYPE(part)) {
5120 case PM_STRING_NODE:
5121 // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags,
5122 // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for
5123 // as long as this interpolation only consists of other string literals.
5124 if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
5125 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
5126 }
5127 part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5128 break;
5129 case PM_INTERPOLATED_STRING_NODE:
5130 if (PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
5131 // If the string that we're concatenating is a static literal,
5132 // then we can keep the static literal flag for this string.
5133 } else {
5134 // Otherwise, we lose the static literal flag here and we should
5135 // also clear the mutability flags.
5136 CLEAR_FLAGS(node);
5137 }
5138 break;
5139 case PM_EMBEDDED_STATEMENTS_NODE: {
5140 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
5141 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
5142
5143 if (embedded == NULL) {
5144 // If we're embedding multiple statements or no statements, then
5145 // the string is not longer a static literal.
5146 CLEAR_FLAGS(node);
5147 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
5148 // If the embedded statement is a string, then we can make that
5149 // string as frozen and static literal, and not touch the static
5150 // literal status of this string.
5151 embedded->flags = (pm_node_flags_t) ((embedded->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5152
5153 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
5154 MUTABLE_FLAGS(node);
5155 }
5156 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
5157 // If the embedded statement is an interpolated string, but that
5158 // string is marked as static literal, then we can keep our
5159 // static literal status for this string.
5160 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
5161 MUTABLE_FLAGS(node);
5162 }
5163 } else {
5164 // In all other cases, we lose the static literal flag here and
5165 // become mutable.
5166 CLEAR_FLAGS(node);
5167 }
5168
5169 break;
5170 }
5171 case PM_EMBEDDED_VARIABLE_NODE:
5172 // Embedded variables clear static literal, which means we also
5173 // should clear the mutability flags.
5174 CLEAR_FLAGS(node);
5175 break;
5176 case PM_X_STRING_NODE:
5177 case PM_INTERPOLATED_X_STRING_NODE:
5178 case PM_SYMBOL_NODE:
5179 case PM_INTERPOLATED_SYMBOL_NODE:
5180 // These will only happen in error cases. But we want to handle it
5181 // here so that we don't fail the assertion.
5182 CLEAR_FLAGS(node);
5183 pm_node_list_append(arena, &node->parts, UP(pm_error_recovery_node_create_unexpected(parser, part)));
5184 return;
5185 case PM_ERROR_RECOVERY_NODE:
5186 CLEAR_FLAGS(node);
5187 break;
5188 default:
5189 assert(false && "unexpected node type");
5190 break;
5191 }
5192
5193 pm_node_list_append(arena, &node->parts, part);
5194
5195#undef CLEAR_FLAGS
5196#undef MUTABLE_FLAGS
5197}
5198
5202static pm_interpolated_string_node_t *
5203pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
5204 pm_node_flags_t flags = PM_NODE_FLAG_STATIC_LITERAL;
5205
5206 switch (parser->frozen_string_literal) {
5207 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
5208 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE;
5209 break;
5210 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
5211 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN;
5212 break;
5213 }
5214
5215 uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening);
5216 uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing);
5217
5218 pm_interpolated_string_node_t *node = pm_interpolated_string_node_new(
5219 parser->arena,
5220 ++parser->node_id,
5221 flags,
5222 ((pm_location_t) { .start = start, .length = U32(end - start) }),
5223 NTOK2LOC(parser, opening),
5224 ((pm_node_list_t) { 0 }),
5225 NTOK2LOC(parser, closing)
5226 );
5227
5228 if (parts != NULL) {
5229 pm_node_t *part;
5230 PM_NODE_LIST_FOREACH(parts, index, part) {
5231 pm_interpolated_string_node_append(parser, node, part);
5232 }
5233 }
5234
5235 return node;
5236}
5237
5241static void
5242pm_interpolated_string_node_closing_set(const pm_parser_t *parser, pm_interpolated_string_node_t *node, const pm_token_t *closing) {
5243 node->closing_loc = TOK2LOC(parser, closing);
5244 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5245}
5246
5247static void
5248pm_interpolated_symbol_node_append(pm_arena_t *arena, pm_interpolated_symbol_node_t *node, pm_node_t *part) {
5249 if (node->parts.size == 0 && node->opening_loc.length == 0) {
5250 PM_NODE_START_SET_NODE(node, part);
5251 }
5252
5253 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5254
5255 if (PM_NODE_END(part) > PM_NODE_END(node)) {
5256 PM_NODE_LENGTH_SET_NODE(node, part);
5257 }
5258}
5259
5260static void
5261pm_interpolated_symbol_node_closing_loc_set(const pm_parser_t *parser, pm_interpolated_symbol_node_t *node, const pm_token_t *closing) {
5262 node->closing_loc = TOK2LOC(parser, closing);
5263 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5264}
5265
5269static pm_interpolated_symbol_node_t *
5270pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
5271 uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening);
5272 uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing);
5273
5274 pm_interpolated_symbol_node_t *node = pm_interpolated_symbol_node_new(
5275 parser->arena,
5276 ++parser->node_id,
5277 PM_NODE_FLAG_STATIC_LITERAL,
5278 ((pm_location_t) { .start = start, .length = U32(end - start) }),
5279 NTOK2LOC(parser, opening),
5280 ((pm_node_list_t) { 0 }),
5281 NTOK2LOC(parser, closing)
5282 );
5283
5284 if (parts != NULL) {
5285 pm_node_t *part;
5286 PM_NODE_LIST_FOREACH(parts, index, part) {
5287 pm_interpolated_symbol_node_append(parser->arena, node, part);
5288 }
5289 }
5290
5291 return node;
5292}
5293
5297static pm_interpolated_x_string_node_t *
5298pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
5299 return pm_interpolated_x_string_node_new(
5300 parser->arena,
5301 ++parser->node_id,
5302 0,
5303 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
5304 TOK2LOC(parser, opening),
5305 ((pm_node_list_t) { 0 }),
5306 TOK2LOC(parser, closing)
5307 );
5308}
5309
5310static PRISM_INLINE void
5311pm_interpolated_xstring_node_append(pm_arena_t *arena, pm_interpolated_x_string_node_t *node, pm_node_t *part) {
5312 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5313 PM_NODE_LENGTH_SET_NODE(node, part);
5314}
5315
5316static PRISM_INLINE void
5317pm_interpolated_xstring_node_closing_set(const pm_parser_t *parser, pm_interpolated_x_string_node_t *node, const pm_token_t *closing) {
5318 node->closing_loc = TOK2LOC(parser, closing);
5319 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5320}
5321
5325static pm_it_local_variable_read_node_t *
5326pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
5327 return pm_it_local_variable_read_node_new(
5328 parser->arena,
5329 ++parser->node_id,
5330 0,
5331 PM_LOCATION_INIT_TOKEN(parser, name)
5332 );
5333}
5334
5338static pm_it_parameters_node_t *
5339pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
5340 return pm_it_parameters_node_new(
5341 parser->arena,
5342 ++parser->node_id,
5343 0,
5344 PM_LOCATION_INIT_TOKENS(parser, opening, closing)
5345 );
5346}
5347
5351static pm_keyword_hash_node_t *
5352pm_keyword_hash_node_create(pm_parser_t *parser) {
5353 return pm_keyword_hash_node_new(
5354 parser->arena,
5355 ++parser->node_id,
5356 PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS,
5357 PM_LOCATION_INIT_UNSET,
5358 ((pm_node_list_t) { 0 })
5359 );
5360}
5361
5365static void
5366pm_keyword_hash_node_elements_append(pm_arena_t *arena, pm_keyword_hash_node_t *hash, pm_node_t *element) {
5367 // If the element being added is not an AssocNode or does not have a symbol
5368 // key, then we want to turn the SYMBOL_KEYS flag off.
5369 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_TYPE_P(((pm_assoc_node_t *) element)->key, PM_SYMBOL_NODE)) {
5370 pm_node_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS);
5371 }
5372
5373 pm_node_list_append(arena, &hash->elements, element);
5374 if (PM_NODE_LENGTH(hash) == 0) {
5375 PM_NODE_START_SET_NODE(hash, element);
5376 }
5377 PM_NODE_LENGTH_SET_NODE(hash, element);
5378}
5379
5383static pm_required_keyword_parameter_node_t *
5384pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name) {
5385 return pm_required_keyword_parameter_node_new(
5386 parser->arena,
5387 ++parser->node_id,
5388 0,
5389 PM_LOCATION_INIT_TOKEN(parser, name),
5390 pm_parser_constant_id_raw(parser, name->start, name->end - 1),
5391 TOK2LOC(parser, name)
5392 );
5393}
5394
5398static pm_optional_keyword_parameter_node_t *
5399pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, pm_node_t *value) {
5400 return pm_optional_keyword_parameter_node_new(
5401 parser->arena,
5402 ++parser->node_id,
5403 0,
5404 PM_LOCATION_INIT_TOKEN_NODE(parser, name, value),
5405 pm_parser_constant_id_raw(parser, name->start, name->end - 1),
5406 TOK2LOC(parser, name),
5407 value
5408 );
5409}
5410
5414static pm_keyword_rest_parameter_node_t *
5415pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
5416 return pm_keyword_rest_parameter_node_new(
5417 parser->arena,
5418 ++parser->node_id,
5419 0,
5420 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
5421 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
5422 NTOK2LOC(parser, name),
5423 TOK2LOC(parser, operator)
5424 );
5425}
5426
5430static pm_lambda_node_t *
5431pm_lambda_node_create(
5432 pm_parser_t *parser,
5433 pm_constant_id_list_t *locals,
5434 const pm_token_t *operator,
5435 const pm_token_t *opening,
5436 const pm_token_t *closing,
5437 pm_node_t *parameters,
5438 pm_node_t *body
5439) {
5440 return pm_lambda_node_new(
5441 parser->arena,
5442 ++parser->node_id,
5443 0,
5444 PM_LOCATION_INIT_TOKENS(parser, operator, closing),
5445 *locals,
5446 TOK2LOC(parser, operator),
5447 TOK2LOC(parser, opening),
5448 TOK2LOC(parser, closing),
5449 parameters,
5450 body
5451 );
5452}
5453
5457static pm_local_variable_and_write_node_t *
5458pm_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) {
5459 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));
5460 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
5461
5462 return pm_local_variable_and_write_node_new(
5463 parser->arena,
5464 ++parser->node_id,
5465 0,
5466 PM_LOCATION_INIT_NODES(target, value),
5467 target->location,
5468 TOK2LOC(parser, operator),
5469 value,
5470 name,
5471 depth
5472 );
5473}
5474
5478static pm_local_variable_operator_write_node_t *
5479pm_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) {
5480 return pm_local_variable_operator_write_node_new(
5481 parser->arena,
5482 ++parser->node_id,
5483 0,
5484 PM_LOCATION_INIT_NODES(target, value),
5485 target->location,
5486 TOK2LOC(parser, operator),
5487 value,
5488 name,
5489 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
5490 depth
5491 );
5492}
5493
5497static pm_local_variable_or_write_node_t *
5498pm_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) {
5499 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));
5500 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
5501
5502 return pm_local_variable_or_write_node_new(
5503 parser->arena,
5504 ++parser->node_id,
5505 0,
5506 PM_LOCATION_INIT_NODES(target, value),
5507 target->location,
5508 TOK2LOC(parser, operator),
5509 value,
5510 name,
5511 depth
5512 );
5513}
5514
5518static pm_local_variable_read_node_t *
5519pm_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) {
5520 if (!missing) pm_locals_read(&pm_parser_scope_find(parser, depth)->locals, name_id);
5521
5522 return pm_local_variable_read_node_new(
5523 parser->arena,
5524 ++parser->node_id,
5525 0,
5526 PM_LOCATION_INIT_TOKEN(parser, name),
5527 name_id,
5528 depth
5529 );
5530}
5531
5535static pm_local_variable_read_node_t *
5536pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5537 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5538 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, false);
5539}
5540
5545static pm_local_variable_read_node_t *
5546pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5547 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5548 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, true);
5549}
5550
5554static pm_local_variable_write_node_t *
5555pm_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) {
5556 return pm_local_variable_write_node_new(
5557 parser->arena,
5558 ++parser->node_id,
5559 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5560 ((pm_location_t) { .start = name_loc->start, .length = PM_NODE_END(value) - name_loc->start }),
5561 name,
5562 depth,
5563 *name_loc,
5564 value,
5565 TOK2LOC(parser, operator)
5566 );
5567}
5568
5572static PRISM_INLINE bool
5573pm_token_is_it(const uint8_t *start, const uint8_t *end) {
5574 return (end - start == 2) && (start[0] == 'i') && (start[1] == 't');
5575}
5576
5581static PRISM_INLINE bool
5582pm_token_is_numbered_parameter(const pm_parser_t *parser, uint32_t start, uint32_t length) {
5583 return (
5584 (length == 2) &&
5585 (parser->start[start] == '_') &&
5586 (parser->start[start + 1] != '0') &&
5587 pm_char_is_decimal_digit(parser->start[start + 1])
5588 );
5589}
5590
5595static PRISM_INLINE void
5596pm_refute_numbered_parameter(pm_parser_t *parser, uint32_t start, uint32_t length) {
5597 if (pm_token_is_numbered_parameter(parser, start, length)) {
5598 PM_PARSER_ERR_FORMAT(parser, start, length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + start);
5599 }
5600}
5601
5606static pm_local_variable_target_node_t *
5607pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *location, pm_constant_id_t name, uint32_t depth) {
5608 pm_refute_numbered_parameter(parser, location->start, location->length);
5609
5610 return pm_local_variable_target_node_new(
5611 parser->arena,
5612 ++parser->node_id,
5613 0,
5614 ((pm_location_t) { .start = location->start, .length = location->length }),
5615 name,
5616 depth
5617 );
5618}
5619
5623static pm_match_predicate_node_t *
5624pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5625 pm_assert_value_expression(parser, value);
5626
5627 return pm_match_predicate_node_new(
5628 parser->arena,
5629 ++parser->node_id,
5630 0,
5631 PM_LOCATION_INIT_NODES(value, pattern),
5632 value,
5633 pattern,
5634 TOK2LOC(parser, operator)
5635 );
5636}
5637
5641static pm_match_required_node_t *
5642pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5643 pm_assert_value_expression(parser, value);
5644
5645 return pm_match_required_node_new(
5646 parser->arena,
5647 ++parser->node_id,
5648 0,
5649 PM_LOCATION_INIT_NODES(value, pattern),
5650 value,
5651 pattern,
5652 TOK2LOC(parser, operator)
5653 );
5654}
5655
5659static pm_match_write_node_t *
5660pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) {
5661 return pm_match_write_node_new(
5662 parser->arena,
5663 ++parser->node_id,
5664 0,
5665 PM_LOCATION_INIT_NODE(call),
5666 call,
5667 ((pm_node_list_t) { 0 })
5668 );
5669}
5670
5674static pm_module_node_t *
5675pm_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) {
5676 pm_constant_id_list_t module_locals = { .ids = NULL, .size = 0, .capacity = 0 };
5677 if (locals != NULL) module_locals = *locals;
5678
5679 return pm_module_node_new(
5680 parser->arena,
5681 ++parser->node_id,
5682 0,
5683 PM_LOCATION_INIT_TOKENS(parser, module_keyword, end_keyword),
5684 module_locals,
5685 TOK2LOC(parser, module_keyword),
5686 constant_path,
5687 body,
5688 TOK2LOC(parser, end_keyword),
5689 pm_parser_constant_id_token(parser, name)
5690 );
5691}
5692
5696static pm_multi_target_node_t *
5697pm_multi_target_node_create(pm_parser_t *parser) {
5698 return pm_multi_target_node_new(
5699 parser->arena,
5700 ++parser->node_id,
5701 0,
5702 PM_LOCATION_INIT_UNSET,
5703 ((pm_node_list_t) { 0 }),
5704 NULL,
5705 ((pm_node_list_t) { 0 }),
5706 ((pm_location_t) { 0 }),
5707 ((pm_location_t) { 0 })
5708 );
5709}
5710
5714static void
5715pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t *node, pm_node_t *target) {
5716 if (PM_NODE_TYPE_P(target, PM_SPLAT_NODE)) {
5717 if (node->rest == NULL) {
5718 node->rest = target;
5719 } else {
5720 pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
5721 pm_node_list_append(parser->arena, &node->rights, target);
5722 }
5723 } else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
5724 if (node->rest == NULL) {
5725 node->rest = target;
5726 } else {
5727 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
5728 pm_node_list_append(parser->arena, &node->rights, target);
5729 }
5730 } else if (node->rest == NULL) {
5731 pm_node_list_append(parser->arena, &node->lefts, target);
5732 } else {
5733 pm_node_list_append(parser->arena, &node->rights, target);
5734 }
5735
5736 if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_START(node) > PM_NODE_START(target))) {
5737 PM_NODE_START_SET_NODE(node, target);
5738 }
5739
5740 if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_END(node) < PM_NODE_END(target))) {
5741 PM_NODE_LENGTH_SET_NODE(node, target);
5742 }
5743}
5744
5748static void
5749pm_multi_target_node_opening_set(const pm_parser_t *parser, pm_multi_target_node_t *node, const pm_token_t *lparen) {
5750 PM_NODE_START_SET_TOKEN(parser, node, lparen);
5751 PM_NODE_LENGTH_SET_TOKEN(parser, node, lparen);
5752 node->lparen_loc = TOK2LOC(parser, lparen);
5753}
5754
5758static void
5759pm_multi_target_node_closing_set(const pm_parser_t *parser, pm_multi_target_node_t *node, const pm_token_t *rparen) {
5760 PM_NODE_LENGTH_SET_TOKEN(parser, node, rparen);
5761 node->rparen_loc = TOK2LOC(parser, rparen);
5762}
5763
5767static pm_multi_write_node_t *
5768pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, const pm_token_t *operator, pm_node_t *value) {
5769 /* The target is no longer necessary because we have reused its children. It
5770 * is arena-allocated so no explicit free is needed. */
5771 return pm_multi_write_node_new(
5772 parser->arena,
5773 ++parser->node_id,
5774 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5775 PM_LOCATION_INIT_NODES(target, value),
5776 target->lefts,
5777 target->rest,
5778 target->rights,
5779 target->lparen_loc,
5780 target->rparen_loc,
5781 TOK2LOC(parser, operator),
5782 value
5783 );
5784}
5785
5789static pm_next_node_t *
5790pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
5791 assert(keyword->type == PM_TOKEN_KEYWORD_NEXT);
5792
5793 return pm_next_node_new(
5794 parser->arena,
5795 ++parser->node_id,
5796 0,
5797 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
5798 arguments,
5799 TOK2LOC(parser, keyword)
5800 );
5801}
5802
5806static pm_nil_node_t *
5807pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) {
5808 assert(token->type == PM_TOKEN_KEYWORD_NIL);
5809
5810 return pm_nil_node_new(
5811 parser->arena,
5812 ++parser->node_id,
5813 PM_NODE_FLAG_STATIC_LITERAL,
5814 PM_LOCATION_INIT_TOKEN(parser, token)
5815 );
5816}
5817
5821static pm_no_block_parameter_node_t *
5822pm_no_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
5823 assert(operator->type == PM_TOKEN_AMPERSAND || operator->type == PM_TOKEN_UAMPERSAND);
5824 assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
5825
5826 return pm_no_block_parameter_node_new(
5827 parser->arena,
5828 ++parser->node_id,
5829 0,
5830 PM_LOCATION_INIT_TOKENS(parser, operator, keyword),
5831 TOK2LOC(parser, operator),
5832 TOK2LOC(parser, keyword)
5833 );
5834}
5835
5839static pm_no_keywords_parameter_node_t *
5840pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
5841 assert(operator->type == PM_TOKEN_USTAR_STAR || operator->type == PM_TOKEN_STAR_STAR);
5842 assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
5843
5844 return pm_no_keywords_parameter_node_new(
5845 parser->arena,
5846 ++parser->node_id,
5847 0,
5848 PM_LOCATION_INIT_TOKENS(parser, operator, keyword),
5849 TOK2LOC(parser, operator),
5850 TOK2LOC(parser, keyword)
5851 );
5852}
5853
5857static pm_numbered_parameters_node_t *
5858pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing, uint8_t maximum) {
5859 return pm_numbered_parameters_node_new(
5860 parser->arena,
5861 ++parser->node_id,
5862 0,
5863 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
5864 maximum
5865 );
5866}
5867
5872#define NTH_REF_MAX ((uint32_t) (INT_MAX >> 1))
5873
5880static uint32_t
5881pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *token) {
5882 const uint8_t *start = token->start + 1;
5883 const uint8_t *end = token->end;
5884
5885 ptrdiff_t diff = end - start;
5886 assert(diff > 0);
5887#if PTRDIFF_MAX > SIZE_MAX
5888 assert(diff < (ptrdiff_t) SIZE_MAX);
5889#endif
5890 size_t length = (size_t) diff;
5891
5892 char *digits = xcalloc(length + 1, sizeof(char));
5893 memcpy(digits, start, length);
5894 digits[length] = '\0';
5895
5896 char *endptr;
5897 errno = 0;
5898 unsigned long value = strtoul(digits, &endptr, 10);
5899
5900 if ((digits == endptr) || (*endptr != '\0')) {
5901 pm_parser_err(parser, U32(start - parser->start), U32(length), PM_ERR_INVALID_NUMBER_DECIMAL);
5902 value = 0;
5903 }
5904
5905 xfree_sized(digits, sizeof(char) * (length + 1));
5906
5907 if ((errno == ERANGE) || (value > NTH_REF_MAX)) {
5908 PM_PARSER_WARN_FORMAT(parser, U32(start - parser->start), U32(length), PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start);
5909 value = 0;
5910 }
5911
5912 return (uint32_t) value;
5913}
5914
5915#undef NTH_REF_MAX
5916
5920static pm_numbered_reference_read_node_t *
5921pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
5922 assert(name->type == PM_TOKEN_NUMBERED_REFERENCE);
5923
5924 return pm_numbered_reference_read_node_new(
5925 parser->arena,
5926 ++parser->node_id,
5927 0,
5928 PM_LOCATION_INIT_TOKEN(parser, name),
5929 pm_numbered_reference_read_node_number(parser, name)
5930 );
5931}
5932
5936static pm_optional_parameter_node_t *
5937pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator, pm_node_t *value) {
5938 return pm_optional_parameter_node_new(
5939 parser->arena,
5940 ++parser->node_id,
5941 0,
5942 PM_LOCATION_INIT_TOKEN_NODE(parser, name, value),
5943 pm_parser_constant_id_token(parser, name),
5944 TOK2LOC(parser, name),
5945 TOK2LOC(parser, operator),
5946 value
5947 );
5948}
5949
5953static pm_or_node_t *
5954pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
5955 pm_assert_value_expression(parser, left);
5956
5957 return pm_or_node_new(
5958 parser->arena,
5959 ++parser->node_id,
5960 0,
5961 PM_LOCATION_INIT_NODES(left, right),
5962 left,
5963 right,
5964 TOK2LOC(parser, operator)
5965 );
5966}
5967
5971static pm_parameters_node_t *
5972pm_parameters_node_create(pm_parser_t *parser) {
5973 return pm_parameters_node_new(
5974 parser->arena,
5975 ++parser->node_id,
5976 0,
5977 PM_LOCATION_INIT_UNSET,
5978 ((pm_node_list_t) { 0 }),
5979 ((pm_node_list_t) { 0 }),
5980 NULL,
5981 ((pm_node_list_t) { 0 }),
5982 ((pm_node_list_t) { 0 }),
5983 NULL,
5984 NULL
5985 );
5986}
5987
5991static void
5992pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param) {
5993 if ((params->base.location.length == 0) || PM_NODE_START(params) > PM_NODE_START(param)) {
5994 PM_NODE_START_SET_NODE(params, param);
5995 }
5996
5997 if ((params->base.location.length == 0) || (PM_NODE_END(params) < PM_NODE_END(param))) {
5998 PM_NODE_LENGTH_SET_NODE(params, param);
5999 }
6000}
6001
6005static void
6006pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6007 pm_parameters_node_location_set(params, param);
6008 pm_node_list_append(arena, &params->requireds, param);
6009}
6010
6014static void
6015pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_optional_parameter_node_t *param) {
6016 pm_parameters_node_location_set(params, UP(param));
6017 pm_node_list_append(arena, &params->optionals, UP(param));
6018}
6019
6023static void
6024pm_parameters_node_posts_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6025 pm_parameters_node_location_set(params, param);
6026 pm_node_list_append(arena, &params->posts, param);
6027}
6028
6032static void
6033pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
6034 pm_parameters_node_location_set(params, param);
6035 params->rest = param;
6036}
6037
6041static void
6042pm_parameters_node_keywords_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6043 pm_parameters_node_location_set(params, param);
6044 pm_node_list_append(arena, &params->keywords, param);
6045}
6046
6050static void
6051pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
6052 assert(params->keyword_rest == NULL);
6053 pm_parameters_node_location_set(params, param);
6054 params->keyword_rest = param;
6055}
6056
6060static void
6061pm_parameters_node_block_set(pm_parameters_node_t *params, pm_node_t *param) {
6062 assert(params->block == NULL);
6063 pm_parameters_node_location_set(params, param);
6064 params->block = param;
6065}
6066
6070static pm_program_node_t *
6071pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) {
6072 return pm_program_node_new(
6073 parser->arena,
6074 ++parser->node_id,
6075 0,
6076 PM_LOCATION_INIT_NODE(statements),
6077 *locals,
6078 statements
6079 );
6080}
6081
6085static pm_parentheses_node_t *
6086pm_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) {
6087 return pm_parentheses_node_new(
6088 parser->arena,
6089 ++parser->node_id,
6090 flags,
6091 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6092 body,
6093 TOK2LOC(parser, opening),
6094 TOK2LOC(parser, closing)
6095 );
6096}
6097
6101static pm_pinned_expression_node_t *
6102pm_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) {
6103 return pm_pinned_expression_node_new(
6104 parser->arena,
6105 ++parser->node_id,
6106 0,
6107 PM_LOCATION_INIT_TOKENS(parser, operator, rparen),
6108 expression,
6109 TOK2LOC(parser, operator),
6110 TOK2LOC(parser, lparen),
6111 TOK2LOC(parser, rparen)
6112 );
6113}
6114
6118static pm_pinned_variable_node_t *
6119pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
6120 return pm_pinned_variable_node_new(
6121 parser->arena,
6122 ++parser->node_id,
6123 0,
6124 PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable),
6125 variable,
6126 TOK2LOC(parser, operator)
6127 );
6128}
6129
6133static pm_post_execution_node_t *
6134pm_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) {
6135 return pm_post_execution_node_new(
6136 parser->arena,
6137 ++parser->node_id,
6138 0,
6139 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
6140 statements,
6141 TOK2LOC(parser, keyword),
6142 TOK2LOC(parser, opening),
6143 TOK2LOC(parser, closing)
6144 );
6145}
6146
6150static pm_pre_execution_node_t *
6151pm_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) {
6152 return pm_pre_execution_node_new(
6153 parser->arena,
6154 ++parser->node_id,
6155 0,
6156 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
6157 statements,
6158 TOK2LOC(parser, keyword),
6159 TOK2LOC(parser, opening),
6160 TOK2LOC(parser, closing)
6161 );
6162}
6163
6167static pm_range_node_t *
6168pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
6169 pm_assert_value_expression(parser, left);
6170 pm_assert_value_expression(parser, right);
6171 pm_node_flags_t flags = 0;
6172
6173 // Indicate that this node is an exclusive range if the operator is `...`.
6174 if (operator->type == PM_TOKEN_DOT_DOT_DOT || operator->type == PM_TOKEN_UDOT_DOT_DOT) {
6175 flags |= PM_RANGE_FLAGS_EXCLUDE_END;
6176 }
6177
6178 // Indicate that this node is a static literal (i.e., can be compiled with
6179 // a putobject in CRuby) if the left and right are implicit nil, explicit
6180 // nil, or integers.
6181 if (
6182 (left == NULL || PM_NODE_TYPE_P(left, PM_NIL_NODE) || PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) &&
6183 (right == NULL || PM_NODE_TYPE_P(right, PM_NIL_NODE) || PM_NODE_TYPE_P(right, PM_INTEGER_NODE))
6184 ) {
6185 flags |= PM_NODE_FLAG_STATIC_LITERAL;
6186 }
6187
6188 uint32_t start = left == NULL ? PM_TOKEN_START(parser, operator) : PM_NODE_START(left);
6189 uint32_t end = right == NULL ? PM_TOKEN_END(parser, operator) : PM_NODE_END(right);
6190
6191 return pm_range_node_new(
6192 parser->arena,
6193 ++parser->node_id,
6194 flags,
6195 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6196 left,
6197 right,
6198 TOK2LOC(parser, operator)
6199 );
6200}
6201
6205static pm_redo_node_t *
6206pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) {
6207 assert(token->type == PM_TOKEN_KEYWORD_REDO);
6208
6209 return pm_redo_node_new(
6210 parser->arena,
6211 ++parser->node_id,
6212 0,
6213 PM_LOCATION_INIT_TOKEN(parser, token)
6214 );
6215}
6216
6221static pm_regular_expression_node_t *
6222pm_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) {
6223 return pm_regular_expression_node_new(
6224 parser->arena,
6225 ++parser->node_id,
6226 pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL,
6227 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6228 TOK2LOC(parser, opening),
6229 TOK2LOC(parser, content),
6230 TOK2LOC(parser, closing),
6231 *unescaped
6232 );
6233}
6234
6238static PRISM_INLINE pm_regular_expression_node_t *
6239pm_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6240 return pm_regular_expression_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6241}
6242
6246static pm_required_parameter_node_t *
6247pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
6248 return pm_required_parameter_node_new(
6249 parser->arena,
6250 ++parser->node_id,
6251 0,
6252 PM_LOCATION_INIT_TOKEN(parser, token),
6253 pm_parser_constant_id_token(parser, token)
6254 );
6255}
6256
6260static pm_rescue_modifier_node_t *
6261pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *keyword, pm_node_t *rescue_expression) {
6262 return pm_rescue_modifier_node_new(
6263 parser->arena,
6264 ++parser->node_id,
6265 0,
6266 PM_LOCATION_INIT_NODES(expression, rescue_expression),
6267 expression,
6268 TOK2LOC(parser, keyword),
6269 rescue_expression
6270 );
6271}
6272
6276static pm_rescue_node_t *
6277pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
6278 return pm_rescue_node_new(
6279 parser->arena,
6280 ++parser->node_id,
6281 0,
6282 PM_LOCATION_INIT_TOKEN(parser, keyword),
6283 TOK2LOC(parser, keyword),
6284 ((pm_node_list_t) { 0 }),
6285 ((pm_location_t) { 0 }),
6286 NULL,
6287 ((pm_location_t) { 0 }),
6288 NULL,
6289 NULL
6290 );
6291}
6292
6293static PRISM_INLINE void
6294pm_rescue_node_operator_set(const pm_parser_t *parser, pm_rescue_node_t *node, const pm_token_t *operator) {
6295 node->operator_loc = TOK2LOC(parser, operator);
6296}
6297
6301static void
6302pm_rescue_node_reference_set(pm_rescue_node_t *node, pm_node_t *reference) {
6303 node->reference = reference;
6304 PM_NODE_LENGTH_SET_NODE(node, reference);
6305}
6306
6310static void
6311pm_rescue_node_statements_set(pm_rescue_node_t *node, pm_statements_node_t *statements) {
6312 node->statements = statements;
6313 if (pm_statements_node_body_length(statements) > 0) {
6314 PM_NODE_LENGTH_SET_NODE(node, statements);
6315 }
6316}
6317
6321static void
6322pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subsequent) {
6323 node->subsequent = subsequent;
6324 PM_NODE_LENGTH_SET_NODE(node, subsequent);
6325}
6326
6330static void
6331pm_rescue_node_exceptions_append(pm_arena_t *arena, pm_rescue_node_t *node, pm_node_t *exception) {
6332 pm_node_list_append(arena, &node->exceptions, exception);
6333 PM_NODE_LENGTH_SET_NODE(node, exception);
6334}
6335
6339static pm_rest_parameter_node_t *
6340pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
6341 return pm_rest_parameter_node_new(
6342 parser->arena,
6343 ++parser->node_id,
6344 0,
6345 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
6346 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
6347 NTOK2LOC(parser, name),
6348 TOK2LOC(parser, operator)
6349 );
6350}
6351
6355static pm_retry_node_t *
6356pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) {
6357 assert(token->type == PM_TOKEN_KEYWORD_RETRY);
6358
6359 return pm_retry_node_new(
6360 parser->arena,
6361 ++parser->node_id,
6362 0,
6363 PM_LOCATION_INIT_TOKEN(parser, token)
6364 );
6365}
6366
6370static pm_return_node_t *
6371pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
6372 return pm_return_node_new(
6373 parser->arena,
6374 ++parser->node_id,
6375 0,
6376 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
6377 TOK2LOC(parser, keyword),
6378 arguments
6379 );
6380}
6381
6385static pm_self_node_t *
6386pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) {
6387 assert(token->type == PM_TOKEN_KEYWORD_SELF);
6388
6389 return pm_self_node_new(
6390 parser->arena,
6391 ++parser->node_id,
6392 0,
6393 PM_LOCATION_INIT_TOKEN(parser, token)
6394 );
6395}
6396
6400static pm_shareable_constant_node_t *
6401pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shareable_constant_value_t value) {
6402 return pm_shareable_constant_node_new(
6403 parser->arena,
6404 ++parser->node_id,
6405 (pm_node_flags_t) value,
6406 PM_LOCATION_INIT_NODE(write),
6407 write
6408 );
6409}
6410
6414static pm_singleton_class_node_t *
6415pm_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) {
6416 return pm_singleton_class_node_new(
6417 parser->arena,
6418 ++parser->node_id,
6419 0,
6420 PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword),
6421 *locals,
6422 TOK2LOC(parser, class_keyword),
6423 TOK2LOC(parser, operator),
6424 expression,
6425 body,
6426 TOK2LOC(parser, end_keyword)
6427 );
6428}
6429
6433static pm_source_encoding_node_t *
6434pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) {
6435 assert(token->type == PM_TOKEN_KEYWORD___ENCODING__);
6436
6437 return pm_source_encoding_node_new(
6438 parser->arena,
6439 ++parser->node_id,
6440 PM_NODE_FLAG_STATIC_LITERAL,
6441 PM_LOCATION_INIT_TOKEN(parser, token)
6442 );
6443}
6444
6448static pm_source_file_node_t*
6449pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) {
6450 assert(file_keyword->type == PM_TOKEN_KEYWORD___FILE__);
6451
6452 pm_node_flags_t flags = 0;
6453
6454 switch (parser->frozen_string_literal) {
6455 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6456 flags |= PM_STRING_FLAGS_MUTABLE;
6457 break;
6458 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6459 flags |= PM_STRING_FLAGS_FROZEN;
6460 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
6461 flags |= PM_NODE_FLAG_STATIC_LITERAL;
6462 }
6463 break;
6464 }
6465
6466 return pm_source_file_node_new(
6467 parser->arena,
6468 ++parser->node_id,
6469 flags,
6470 PM_LOCATION_INIT_TOKEN(parser, file_keyword),
6471 parser->filepath
6472 );
6473}
6474
6478static pm_source_line_node_t *
6479pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) {
6480 assert(token->type == PM_TOKEN_KEYWORD___LINE__);
6481
6482 return pm_source_line_node_new(
6483 parser->arena,
6484 ++parser->node_id,
6485 PM_NODE_FLAG_STATIC_LITERAL,
6486 PM_LOCATION_INIT_TOKEN(parser, token)
6487 );
6488}
6489
6493static pm_splat_node_t *
6494pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
6495 return pm_splat_node_new(
6496 parser->arena,
6497 ++parser->node_id,
6498 0,
6499 (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression),
6500 TOK2LOC(parser, operator),
6501 expression
6502 );
6503}
6504
6508static pm_statements_node_t *
6509pm_statements_node_create(pm_parser_t *parser) {
6510 return pm_statements_node_new(
6511 parser->arena,
6512 ++parser->node_id,
6513 0,
6514 PM_LOCATION_INIT_UNSET,
6515 ((pm_node_list_t) { 0 })
6516 );
6517}
6518
6522static size_t
6523pm_statements_node_body_length(pm_statements_node_t *node) {
6524 return node && node->body.size;
6525}
6526
6531static PRISM_INLINE void
6532pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement) {
6533 if (pm_statements_node_body_length(node) == 0 || PM_NODE_START(statement) < PM_NODE_START(node)) {
6534 PM_NODE_START_SET_NODE(node, statement);
6535 }
6536
6537 if (PM_NODE_END(statement) > PM_NODE_END(node)) {
6538 PM_NODE_LENGTH_SET_NODE(node, statement);
6539 }
6540}
6541
6545static void
6546pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline) {
6547 pm_statements_node_body_update(node, statement);
6548
6549 if (node->body.size > 0) {
6550 const pm_node_t *previous = node->body.nodes[node->body.size - 1];
6551
6552 switch (PM_NODE_TYPE(previous)) {
6553 case PM_BREAK_NODE:
6554 case PM_NEXT_NODE:
6555 case PM_REDO_NODE:
6556 case PM_RETRY_NODE:
6557 case PM_RETURN_NODE:
6558 pm_parser_warn_node(parser, statement, PM_WARN_UNREACHABLE_STATEMENT);
6559 break;
6560 default:
6561 break;
6562 }
6563 }
6564
6565 pm_node_list_append(parser->arena, &node->body, statement);
6566 if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6567}
6568
6572static void
6573pm_statements_node_body_prepend(pm_arena_t *arena, pm_statements_node_t *node, pm_node_t *statement) {
6574 pm_statements_node_body_update(node, statement);
6575 pm_node_list_prepend(arena, &node->body, statement);
6576 pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6577}
6578
6582static PRISM_INLINE pm_string_node_t *
6583pm_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) {
6584 pm_node_flags_t flags = 0;
6585
6586 switch (parser->frozen_string_literal) {
6587 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6588 flags = PM_STRING_FLAGS_MUTABLE;
6589 break;
6590 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6591 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6592 break;
6593 }
6594
6595 uint32_t start = PM_TOKEN_START(parser, opening == NULL ? content : opening);
6596 uint32_t end = PM_TOKEN_END(parser, closing == NULL ? content : closing);
6597
6598 return pm_string_node_new(
6599 parser->arena,
6600 ++parser->node_id,
6601 flags,
6602 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6603 NTOK2LOC(parser, opening),
6604 TOK2LOC(parser, content),
6605 NTOK2LOC(parser, closing),
6606 *string
6607 );
6608}
6609
6613static pm_string_node_t *
6614pm_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6615 return pm_string_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6616}
6617
6622static pm_string_node_t *
6623pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6624 pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, &parser->current_string);
6625 parser->current_string = PM_STRING_EMPTY;
6626 return node;
6627}
6628
6632static pm_super_node_t *
6633pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_t *arguments) {
6634 assert(keyword->type == PM_TOKEN_KEYWORD_SUPER);
6635
6636 const pm_location_t *end = pm_arguments_end(arguments);
6637 assert(end != NULL && "unreachable");
6638
6639 return pm_super_node_new(
6640 parser->arena,
6641 ++parser->node_id,
6642 0,
6643 ((pm_location_t) { .start = PM_TOKEN_START(parser, keyword), .length = PM_LOCATION_END(end) - PM_TOKEN_START(parser, keyword) }),
6644 TOK2LOC(parser, keyword),
6645 arguments->opening_loc,
6646 arguments->arguments,
6647 arguments->closing_loc,
6648 arguments->block
6649 );
6650}
6651
6656static bool
6657pm_ascii_only_p(const pm_string_t *contents) {
6658 const size_t length = pm_string_length(contents);
6659 const uint8_t *source = pm_string_source(contents);
6660
6661 for (size_t index = 0; index < length; index++) {
6662 if (source[index] & 0x80) return false;
6663 }
6664
6665 return true;
6666}
6667
6671static void
6672parse_symbol_encoding_validate_utf8(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6673 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6674 size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor);
6675
6676 if (width == 0) {
6677 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6678 break;
6679 }
6680
6681 cursor += width;
6682 }
6683}
6684
6689static void
6690parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6691 const pm_encoding_t *encoding = parser->encoding;
6692
6693 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6694 size_t width = encoding->char_width(cursor, end - cursor);
6695
6696 if (width == 0) {
6697 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6698 break;
6699 }
6700
6701 cursor += width;
6702 }
6703}
6704
6714static PRISM_INLINE pm_node_flags_t
6715parse_symbol_encoding(pm_parser_t *parser, const pm_encoding_t *explicit_encoding, const pm_token_t *location, const pm_string_t *contents, bool validate) {
6716 if (explicit_encoding != NULL) {
6717 // A Symbol may optionally have its encoding explicitly set. This will
6718 // happen if an escape sequence results in a non-ASCII code point.
6719 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
6720 if (validate) parse_symbol_encoding_validate_utf8(parser, location, contents);
6721 return PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
6722 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
6723 return PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING;
6724 } else if (validate) {
6725 parse_symbol_encoding_validate_other(parser, location, contents);
6726 }
6727 } else if (pm_ascii_only_p(contents)) {
6728 // Ruby stipulates that all source files must use an ASCII-compatible
6729 // encoding. Thus, all symbols appearing in source are eligible for
6730 // "downgrading" to US-ASCII.
6731 return PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
6732 } else if (validate) {
6733 parse_symbol_encoding_validate_other(parser, location, contents);
6734 }
6735
6736 return 0;
6737}
6738
6743static pm_symbol_node_t *
6744pm_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) {
6745 uint32_t start = opening == NULL ? PM_TOKEN_START(parser, value) : PM_TOKEN_START(parser, opening);
6746 uint32_t end = closing == NULL ? PM_TOKEN_END(parser, value) : PM_TOKEN_END(parser, closing);
6747
6748 return pm_symbol_node_new(
6749 parser->arena,
6750 ++parser->node_id,
6751 PM_NODE_FLAG_STATIC_LITERAL | flags,
6752 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6753 NTOK2LOC(parser, opening),
6754 NTOK2LOC(parser, value),
6755 NTOK2LOC(parser, closing),
6756 *unescaped
6757 );
6758}
6759
6763static PRISM_INLINE pm_symbol_node_t *
6764pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6765 return pm_symbol_node_create_unescaped(parser, opening, value, closing, &PM_STRING_EMPTY, 0);
6766}
6767
6771static pm_symbol_node_t *
6772pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6773 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));
6774 parser->current_string = PM_STRING_EMPTY;
6775 return node;
6776}
6777
6781static pm_symbol_node_t *
6782pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) {
6783 assert(token->type == PM_TOKEN_LABEL);
6784
6785 pm_token_t closing = { .type = PM_TOKEN_LABEL_END, .start = token->end - 1, .end = token->end };
6786 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end - 1 };
6787 pm_symbol_node_t *node = pm_symbol_node_create(parser, NULL, &label, &closing);
6788
6789 assert((label.end - label.start) >= 0);
6790 pm_string_shared_init(&node->unescaped, label.start, label.end);
6791 pm_node_flag_set(UP(node), parse_symbol_encoding(parser, parser->explicit_encoding, &label, &node->unescaped, false));
6792
6793 return node;
6794}
6795
6799static pm_symbol_node_t *
6800pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
6801 pm_symbol_node_t *node = pm_symbol_node_new(
6802 parser->arena,
6803 ++parser->node_id,
6804 PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING,
6805 PM_LOCATION_INIT_UNSET,
6806 ((pm_location_t) { 0 }),
6807 ((pm_location_t) { 0 }),
6808 ((pm_location_t) { 0 }),
6809 ((pm_string_t) { 0 })
6810 );
6811
6812 pm_string_constant_init(&node->unescaped, content, strlen(content));
6813 return node;
6814}
6815
6819static bool
6820pm_symbol_node_label_p(const pm_parser_t *parser, const pm_node_t *node) {
6821 const pm_location_t *location = NULL;
6822
6823 switch (PM_NODE_TYPE(node)) {
6824 case PM_SYMBOL_NODE: {
6825 const pm_symbol_node_t *cast = (pm_symbol_node_t *) node;
6826 if (cast->closing_loc.length > 0) {
6827 location = &cast->closing_loc;
6828 }
6829 break;
6830 }
6831 case PM_INTERPOLATED_SYMBOL_NODE: {
6832 const pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node;
6833 if (cast->closing_loc.length > 0) {
6834 location = &cast->closing_loc;
6835 }
6836 break;
6837 }
6838 default:
6839 return false;
6840 }
6841
6842 return (location != NULL) && (parser->start[PM_LOCATION_END(location) - 1] == ':');
6843}
6844
6848static pm_symbol_node_t *
6849pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t *opening, const pm_token_t *closing) {
6850 pm_symbol_node_t *new_node = pm_symbol_node_new(
6851 parser->arena,
6852 ++parser->node_id,
6853 PM_NODE_FLAG_STATIC_LITERAL,
6854 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6855 TOK2LOC(parser, opening),
6856 node->content_loc,
6857 TOK2LOC(parser, closing),
6858 node->unescaped
6859 );
6860
6861 pm_token_t content = {
6862 .type = PM_TOKEN_IDENTIFIER,
6863 .start = parser->start + node->content_loc.start,
6864 .end = parser->start + node->content_loc.start + node->content_loc.length
6865 };
6866
6867 pm_node_flag_set(UP(new_node), parse_symbol_encoding(parser, parser->explicit_encoding, &content, &node->unescaped, true));
6868
6869 /* The old node is arena-allocated so no explicit free is needed. */
6870 return new_node;
6871}
6872
6876static pm_string_node_t *
6877pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) {
6878 pm_node_flags_t flags = 0;
6879
6880 switch (parser->frozen_string_literal) {
6881 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6882 flags = PM_STRING_FLAGS_MUTABLE;
6883 break;
6884 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6885 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6886 break;
6887 }
6888
6889 pm_string_node_t *new_node = pm_string_node_new(
6890 parser->arena,
6891 ++parser->node_id,
6892 flags,
6893 PM_LOCATION_INIT_NODE(node),
6894 node->opening_loc,
6895 node->content_loc,
6896 node->closing_loc,
6897 node->unescaped
6898 );
6899
6900 /* The old node is arena-allocated so no explicit free is needed. */
6901 return new_node;
6902}
6903
6907static pm_true_node_t *
6908pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) {
6909 assert(token->type == PM_TOKEN_KEYWORD_TRUE);
6910
6911 return pm_true_node_new(
6912 parser->arena,
6913 ++parser->node_id,
6914 PM_NODE_FLAG_STATIC_LITERAL,
6915 PM_LOCATION_INIT_TOKEN(parser, token)
6916 );
6917}
6918
6922static pm_true_node_t *
6923pm_true_node_synthesized_create(pm_parser_t *parser) {
6924 return pm_true_node_new(
6925 parser->arena,
6926 ++parser->node_id,
6927 PM_NODE_FLAG_STATIC_LITERAL,
6928 PM_LOCATION_INIT_UNSET
6929 );
6930}
6931
6935static pm_undef_node_t *
6936pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
6937 assert(token->type == PM_TOKEN_KEYWORD_UNDEF);
6938
6939 return pm_undef_node_new(
6940 parser->arena,
6941 ++parser->node_id,
6942 0,
6943 PM_LOCATION_INIT_TOKEN(parser, token),
6944 ((pm_node_list_t) { 0 }),
6945 TOK2LOC(parser, token)
6946 );
6947}
6948
6952static void
6953pm_undef_node_append(pm_arena_t *arena, pm_undef_node_t *node, pm_node_t *name) {
6954 PM_NODE_LENGTH_SET_NODE(node, name);
6955 pm_node_list_append(arena, &node->names, name);
6956}
6957
6961static pm_unless_node_t *
6962pm_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) {
6963 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6964 pm_node_t *end = statements == NULL ? predicate : UP(statements);
6965
6966 return pm_unless_node_new(
6967 parser->arena,
6968 ++parser->node_id,
6969 PM_NODE_FLAG_NEWLINE,
6970 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, end),
6971 TOK2LOC(parser, keyword),
6972 predicate,
6973 NTOK2LOC(parser, then_keyword),
6974 statements,
6975 NULL,
6976 ((pm_location_t) { 0 })
6977 );
6978}
6979
6983static pm_unless_node_t *
6984pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) {
6985 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6986
6987 pm_statements_node_t *statements = pm_statements_node_create(parser);
6988 pm_statements_node_body_append(parser, statements, statement, true);
6989
6990 return pm_unless_node_new(
6991 parser->arena,
6992 ++parser->node_id,
6993 PM_NODE_FLAG_NEWLINE,
6994 PM_LOCATION_INIT_NODES(statement, predicate),
6995 TOK2LOC(parser, unless_keyword),
6996 predicate,
6997 ((pm_location_t) { 0 }),
6998 statements,
6999 NULL,
7000 ((pm_location_t) { 0 })
7001 );
7002}
7003
7004static PRISM_INLINE void
7005pm_unless_node_end_keyword_loc_set(const pm_parser_t *parser, pm_unless_node_t *node, const pm_token_t *end_keyword) {
7006 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
7007 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
7008}
7009
7015static void
7016pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statements) {
7017 assert(parser->current_block_exits != NULL);
7018
7019 // All of the block exits that we want to remove should be within the
7020 // statements, and since we are modifying the statements, we shouldn't have
7021 // to check the end location.
7022 uint32_t start = statements->base.location.start;
7023
7024 for (size_t index = parser->current_block_exits->size; index > 0; index--) {
7025 pm_node_t *block_exit = parser->current_block_exits->nodes[index - 1];
7026 if (block_exit->location.start < start) break;
7027
7028 // Implicitly remove from the list by lowering the size.
7029 parser->current_block_exits->size--;
7030 }
7031}
7032
7036static pm_until_node_t *
7037pm_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) {
7038 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7039
7040 return pm_until_node_new(
7041 parser->arena,
7042 ++parser->node_id,
7043 flags,
7044 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7045 TOK2LOC(parser, keyword),
7046 NTOK2LOC(parser, do_keyword),
7047 TOK2LOC(parser, closing),
7048 predicate,
7049 statements
7050 );
7051}
7052
7056static pm_until_node_t *
7057pm_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) {
7058 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7059 pm_loop_modifier_block_exits(parser, statements);
7060
7061 return pm_until_node_new(
7062 parser->arena,
7063 ++parser->node_id,
7064 flags,
7065 PM_LOCATION_INIT_NODES(statements, predicate),
7066 TOK2LOC(parser, keyword),
7067 ((pm_location_t) { 0 }),
7068 ((pm_location_t) { 0 }),
7069 predicate,
7070 statements
7071 );
7072}
7073
7077static pm_when_node_t *
7078pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
7079 return pm_when_node_new(
7080 parser->arena,
7081 ++parser->node_id,
7082 0,
7083 PM_LOCATION_INIT_TOKEN(parser, keyword),
7084 TOK2LOC(parser, keyword),
7085 ((pm_node_list_t) { 0 }),
7086 ((pm_location_t) { 0 }),
7087 NULL
7088 );
7089}
7090
7094static void
7095pm_when_node_conditions_append(pm_arena_t *arena, pm_when_node_t *node, pm_node_t *condition) {
7096 PM_NODE_LENGTH_SET_NODE(node, condition);
7097 pm_node_list_append(arena, &node->conditions, condition);
7098}
7099
7103static PRISM_INLINE void
7104pm_when_node_then_keyword_loc_set(const pm_parser_t *parser, pm_when_node_t *node, const pm_token_t *then_keyword) {
7105 PM_NODE_LENGTH_SET_TOKEN(parser, node, then_keyword);
7106 node->then_keyword_loc = TOK2LOC(parser, then_keyword);
7107}
7108
7112static void
7113pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statements) {
7114 if (PM_NODE_END(statements) > PM_NODE_END(node)) {
7115 PM_NODE_LENGTH_SET_NODE(node, statements);
7116 }
7117
7118 node->statements = statements;
7119}
7120
7124static pm_while_node_t *
7125pm_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) {
7126 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7127
7128 return pm_while_node_new(
7129 parser->arena,
7130 ++parser->node_id,
7131 flags,
7132 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7133 TOK2LOC(parser, keyword),
7134 NTOK2LOC(parser, do_keyword),
7135 TOK2LOC(parser, closing),
7136 predicate,
7137 statements
7138 );
7139}
7140
7144static pm_while_node_t *
7145pm_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) {
7146 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7147 pm_loop_modifier_block_exits(parser, statements);
7148
7149 return pm_while_node_new(
7150 parser->arena,
7151 ++parser->node_id,
7152 flags,
7153 PM_LOCATION_INIT_NODES(statements, predicate),
7154 TOK2LOC(parser, keyword),
7155 ((pm_location_t) { 0 }),
7156 ((pm_location_t) { 0 }),
7157 predicate,
7158 statements
7159 );
7160}
7161
7165static pm_while_node_t *
7166pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_statements_node_t *statements) {
7167 return pm_while_node_new(
7168 parser->arena,
7169 ++parser->node_id,
7170 0,
7171 PM_LOCATION_INIT_UNSET,
7172 ((pm_location_t) { 0 }),
7173 ((pm_location_t) { 0 }),
7174 ((pm_location_t) { 0 }),
7175 predicate,
7176 statements
7177 );
7178}
7179
7184static pm_x_string_node_t *
7185pm_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) {
7186 return pm_x_string_node_new(
7187 parser->arena,
7188 ++parser->node_id,
7189 PM_STRING_FLAGS_FROZEN,
7190 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
7191 TOK2LOC(parser, opening),
7192 TOK2LOC(parser, content),
7193 TOK2LOC(parser, closing),
7194 *unescaped
7195 );
7196}
7197
7201static PRISM_INLINE pm_x_string_node_t *
7202pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
7203 return pm_xstring_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
7204}
7205
7209static pm_yield_node_t *
7210pm_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) {
7211 uint32_t start = PM_TOKEN_START(parser, keyword);
7212 uint32_t end;
7213
7214 if (rparen_loc->length > 0) {
7215 end = PM_LOCATION_END(rparen_loc);
7216 } else if (arguments != NULL) {
7217 end = PM_NODE_END(arguments);
7218 } else if (lparen_loc->length > 0) {
7219 end = PM_LOCATION_END(lparen_loc);
7220 } else {
7221 end = PM_TOKEN_END(parser, keyword);
7222 }
7223
7224 return pm_yield_node_new(
7225 parser->arena,
7226 ++parser->node_id,
7227 0,
7228 ((pm_location_t) { .start = start, .length = U32(end - start) }),
7229 TOK2LOC(parser, keyword),
7230 *lparen_loc,
7231 arguments,
7232 *rparen_loc
7233 );
7234}
7235
7240static int
7241pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant_id) {
7242 pm_scope_t *scope = parser->current_scope;
7243 int depth = 0;
7244
7245 while (scope != NULL) {
7246 if (pm_locals_find(&scope->locals, constant_id) != UINT32_MAX) return depth;
7247 if (scope->closed) break;
7248
7249 scope = scope->previous;
7250 depth++;
7251 }
7252
7253 return -1;
7254}
7255
7261static PRISM_INLINE int
7262pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
7263 return pm_parser_local_depth_constant_id(parser, pm_parser_constant_id_token(parser, token));
7264}
7265
7269static PRISM_INLINE void
7270pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7271 pm_locals_write(&parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
7272}
7273
7277static pm_constant_id_t
7278pm_parser_local_add_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7279 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
7280 if (constant_id != 0) pm_parser_local_add(parser, constant_id, start, end, reads);
7281 return constant_id;
7282}
7283
7287static PRISM_INLINE pm_constant_id_t
7288pm_parser_local_add_location(pm_parser_t *parser, pm_location_t *location, uint32_t reads) {
7289 return pm_parser_local_add_raw(parser, parser->start + location->start, parser->start + location->start + location->length, reads);
7290}
7291
7295static PRISM_INLINE pm_constant_id_t
7296pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token, uint32_t reads) {
7297 return pm_parser_local_add_raw(parser, token->start, token->end, reads);
7298}
7299
7303static pm_constant_id_t
7304pm_parser_local_add_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
7305 pm_constant_id_t constant_id = pm_parser_constant_id_owned(parser, start, length);
7306 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7307 return constant_id;
7308}
7309
7313static pm_constant_id_t
7314pm_parser_local_add_constant(pm_parser_t *parser, const char *start, size_t length) {
7315 pm_constant_id_t constant_id = pm_parser_constant_id_constant(parser, start, length);
7316 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7317 return constant_id;
7318}
7319
7327static bool
7328pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) {
7329 // We want to check whether the parameter name is a numbered parameter or
7330 // not.
7331 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, name), PM_TOKEN_LENGTH(name));
7332
7333 // Otherwise we'll fetch the constant id for the parameter name and check
7334 // whether it's already in the current scope.
7335 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name);
7336
7337 if (pm_locals_find(&parser->current_scope->locals, constant_id) != UINT32_MAX) {
7338 // Add an error if the parameter doesn't start with _ and has been seen before
7339 if ((name->start < name->end) && (*name->start != '_')) {
7340 pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_DUPLICATED);
7341 }
7342 return true;
7343 }
7344 return false;
7345}
7346
7350static void
7351pm_parser_scope_pop(pm_parser_t *parser) {
7352 pm_scope_t *scope = parser->current_scope;
7353 parser->current_scope = scope->previous;
7354 pm_locals_free(&scope->locals);
7355 xfree_sized(scope, sizeof(pm_scope_t));
7356}
7357
7358/******************************************************************************/
7359/* Stack helpers */
7360/******************************************************************************/
7361
7365static PRISM_INLINE void
7366pm_state_stack_push(pm_state_stack_t *stack, bool value) {
7367 *stack = (*stack << 1) | (value & 1);
7368}
7369
7373static PRISM_INLINE void
7374pm_state_stack_pop(pm_state_stack_t *stack) {
7375 *stack >>= 1;
7376}
7377
7381static PRISM_INLINE bool
7382pm_state_stack_p(const pm_state_stack_t *stack) {
7383 return *stack & 1;
7384}
7385
7386static PRISM_INLINE void
7387pm_accepts_block_stack_push(pm_parser_t *parser, bool value) {
7388 // Use the negation of the value to prevent stack overflow.
7389 pm_state_stack_push(&parser->accepts_block_stack, !value);
7390}
7391
7392static PRISM_INLINE void
7393pm_accepts_block_stack_pop(pm_parser_t *parser) {
7394 pm_state_stack_pop(&parser->accepts_block_stack);
7395}
7396
7397static PRISM_INLINE bool
7398pm_accepts_block_stack_p(pm_parser_t *parser) {
7399 return !pm_state_stack_p(&parser->accepts_block_stack);
7400}
7401
7402static PRISM_INLINE void
7403pm_do_loop_stack_push(pm_parser_t *parser, bool value) {
7404 pm_state_stack_push(&parser->do_loop_stack, value);
7405}
7406
7407static PRISM_INLINE void
7408pm_do_loop_stack_pop(pm_parser_t *parser) {
7409 pm_state_stack_pop(&parser->do_loop_stack);
7410}
7411
7412static PRISM_INLINE bool
7413pm_do_loop_stack_p(pm_parser_t *parser) {
7414 return pm_state_stack_p(&parser->do_loop_stack);
7415}
7416
7441static PRISM_INLINE void
7442pm_enclosure_frame_push(pm_parser_t *parser) {
7443 pm_do_loop_stack_push(parser, false);
7444 pm_accepts_block_stack_push(parser, true);
7445}
7446
7447static PRISM_INLINE void
7448pm_enclosure_frame_pop(pm_parser_t *parser) {
7449 pm_do_loop_stack_pop(parser);
7450 pm_accepts_block_stack_pop(parser);
7451}
7452
7453/******************************************************************************/
7454/* Lexer check helpers */
7455/******************************************************************************/
7456
7461static PRISM_INLINE uint8_t
7462peek_at(const pm_parser_t *parser, const uint8_t *cursor) {
7463 if (cursor < parser->end) {
7464 return *cursor;
7465 } else {
7466 return '\0';
7467 }
7468}
7469
7475static PRISM_INLINE uint8_t
7476peek_offset(pm_parser_t *parser, ptrdiff_t offset) {
7477 return peek_at(parser, parser->current.end + offset);
7478}
7479
7484static PRISM_INLINE uint8_t
7485peek(const pm_parser_t *parser) {
7486 return peek_at(parser, parser->current.end);
7487}
7488
7493static PRISM_INLINE bool
7494match(pm_parser_t *parser, uint8_t value) {
7495 if (peek(parser) == value) {
7496 parser->current.end++;
7497 return true;
7498 }
7499 return false;
7500}
7501
7506static PRISM_INLINE size_t
7507match_eol_at(pm_parser_t *parser, const uint8_t *cursor) {
7508 if (peek_at(parser, cursor) == '\n') {
7509 return 1;
7510 }
7511 if (peek_at(parser, cursor) == '\r' && peek_at(parser, cursor + 1) == '\n') {
7512 return 2;
7513 }
7514 return 0;
7515}
7516
7522static PRISM_INLINE size_t
7523match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) {
7524 return match_eol_at(parser, parser->current.end + offset);
7525}
7526
7532static PRISM_INLINE size_t
7533match_eol(pm_parser_t *parser) {
7534 return match_eol_at(parser, parser->current.end);
7535}
7536
7540static PRISM_INLINE const uint8_t *
7541next_newline(const uint8_t *cursor, ptrdiff_t length) {
7542 assert(length >= 0);
7543
7544 // Note that it's okay for us to use memchr here to look for \n because none
7545 // of the encodings that we support have \n as a component of a multi-byte
7546 // character.
7547 return memchr(cursor, '\n', (size_t) length);
7548}
7549
7553static PRISM_INLINE bool
7554ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) {
7555 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));
7556}
7557
7562static bool
7563parser_lex_magic_comment_encoding_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
7564 const pm_encoding_t *encoding = pm_encoding_find(start, end);
7565
7566 if (encoding != NULL) {
7567 if (parser->encoding != encoding) {
7568 parser->encoding = encoding;
7569 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
7570 }
7571
7572 parser->encoding_changed = (encoding != PM_ENCODING_UTF_8_ENTRY);
7573 return true;
7574 }
7575
7576 return false;
7577}
7578
7583static void
7584parser_lex_magic_comment_encoding(pm_parser_t *parser) {
7585 const uint8_t *cursor = parser->current.start + 1;
7586 const uint8_t *end = parser->current.end;
7587
7588 bool separator = false;
7589 while (true) {
7590 if (end - cursor <= 6) return;
7591 switch (cursor[6]) {
7592 case 'C': case 'c': cursor += 6; continue;
7593 case 'O': case 'o': cursor += 5; continue;
7594 case 'D': case 'd': cursor += 4; continue;
7595 case 'I': case 'i': cursor += 3; continue;
7596 case 'N': case 'n': cursor += 2; continue;
7597 case 'G': case 'g': cursor += 1; continue;
7598 case '=': case ':':
7599 separator = true;
7600 cursor += 6;
7601 break;
7602 default:
7603 cursor += 6;
7604 if (pm_char_is_whitespace(*cursor)) break;
7605 continue;
7606 }
7607 if (pm_strncasecmp(cursor - 6, (const uint8_t *) "coding", 6) == 0) break;
7608 separator = false;
7609 }
7610
7611 while (true) {
7612 do {
7613 if (++cursor >= end) return;
7614 } while (pm_char_is_whitespace(*cursor));
7615
7616 if (separator) break;
7617 if (*cursor != '=' && *cursor != ':') return;
7618
7619 separator = true;
7620 cursor++;
7621 }
7622
7623 const uint8_t *value_start = cursor;
7624 while ((*cursor == '-' || *cursor == '_' || parser->encoding->alnum_char(cursor, 1)) && ++cursor < end);
7625
7626 if (!parser_lex_magic_comment_encoding_value(parser, value_start, cursor)) {
7627 // If we were unable to parse the encoding value, then we've got an
7628 // issue because we didn't understand the encoding that the user was
7629 // trying to use. In this case we'll keep using the default encoding but
7630 // add an error to the parser to indicate an unsuccessful parse.
7631 pm_parser_err(parser, U32(value_start - parser->start), U32(cursor - value_start), PM_ERR_INVALID_ENCODING_MAGIC_COMMENT);
7632 }
7633}
7634
7635typedef enum {
7636 PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE,
7637 PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE,
7638 PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID
7639} pm_magic_comment_boolean_value_t;
7640
7645static pm_magic_comment_boolean_value_t
7646parser_lex_magic_comment_boolean_value(const uint8_t *value_start, uint32_t value_length) {
7647 if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "true", 4) == 0) {
7648 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE;
7649 } else if (value_length == 5 && pm_strncasecmp(value_start, (const uint8_t *) "false", 5) == 0) {
7650 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE;
7651 } else {
7652 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID;
7653 }
7654}
7655
7656static PRISM_INLINE bool
7657pm_char_is_magic_comment_key_delimiter(const uint8_t b) {
7658 return b == '\'' || b == '"' || b == ':' || b == ';';
7659}
7660
7666static PRISM_INLINE const uint8_t *
7667parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor, const uint8_t *end) {
7668 // Scan for '*' as the middle character, since it is rarer than '-' in
7669 // typical comments and avoids repeated memchr calls for '-' that hit
7670 // dashes in words like "foo-bar".
7671 while ((cursor + 3 <= end) && (cursor = pm_memchr(cursor + 1, '*', (size_t) (end - cursor - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7672 if (cursor[-1] == '-' && cursor + 1 < end && cursor[1] == '-') {
7673 return cursor - 1;
7674 }
7675 }
7676 return NULL;
7677}
7678
7689static PRISM_INLINE bool
7690parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
7691 bool result = true;
7692
7693 const uint8_t *start = parser->current.start + 1;
7694 const uint8_t *end = parser->current.end;
7695 if (end - start <= 7) return false;
7696
7697 const uint8_t *cursor;
7698 bool indicator = false;
7699
7700 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7701 start = cursor + 3;
7702
7703 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7704 end = cursor;
7705 indicator = true;
7706 } else {
7707 // If we have a start marker but not an end marker, then we cannot
7708 // have a magic comment.
7709 return false;
7710 }
7711 } else {
7712 // Non-emacs magic comments must contain a colon for `key: value`.
7713 // Reject early if there is no colon to avoid scanning the entire
7714 // comment character-by-character.
7715 if (pm_memchr(start, ':', (size_t) (end - start), parser->encoding_changed, parser->encoding) == NULL) {
7716 return false;
7717 }
7718
7719 // Advance start past leading whitespace so the main loop begins
7720 // directly at the key, avoiding a redundant whitespace scan.
7721 start += pm_strspn_whitespace(start, end - start);
7722 }
7723
7724 cursor = start;
7725 while (cursor < end) {
7726 if (indicator) {
7727 while (cursor < end && (pm_char_is_magic_comment_key_delimiter(*cursor) || pm_char_is_whitespace(*cursor))) cursor++;
7728 }
7729
7730 const uint8_t *key_start = cursor;
7731 while (cursor < end && (!pm_char_is_magic_comment_key_delimiter(*cursor) && !pm_char_is_whitespace(*cursor))) cursor++;
7732
7733 const uint8_t *key_end = cursor;
7734 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7735 if (cursor == end) break;
7736
7737 if (*cursor == ':') {
7738 cursor++;
7739 } else {
7740 if (!indicator) return false;
7741 continue;
7742 }
7743
7744 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7745 if (cursor == end) break;
7746
7747 const uint8_t *value_start;
7748 const uint8_t *value_end;
7749
7750 if (*cursor == '"') {
7751 value_start = ++cursor;
7752 for (; cursor < end && *cursor != '"'; cursor++) {
7753 if (*cursor == '\\' && (cursor + 1 < end)) cursor++;
7754 }
7755 value_end = cursor;
7756 if (cursor < end && *cursor == '"') cursor++;
7757 } else {
7758 value_start = cursor;
7759 while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++;
7760 value_end = cursor;
7761 }
7762
7763 if (indicator) {
7764 while (cursor < end && (*cursor == ';' || pm_char_is_whitespace(*cursor))) cursor++;
7765 } else {
7766 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7767 if (cursor != end) return false;
7768 }
7769
7770 // Here, we need to do some processing on the key to swap out dashes for
7771 // underscores. We only need to do this if there _is_ a dash in the key.
7772 pm_string_t key;
7773 const size_t key_length = (size_t) (key_end - key_start);
7774 const uint8_t *dash = pm_memchr(key_start, '-', key_length, parser->encoding_changed, parser->encoding);
7775
7776 if (dash == NULL) {
7777 pm_string_shared_init(&key, key_start, key_end);
7778 } else {
7779 uint8_t *buffer = xmalloc(key_length);
7780 if (buffer == NULL) break;
7781
7782 memcpy(buffer, key_start, key_length);
7783 buffer[dash - key_start] = '_';
7784
7785 while ((dash = pm_memchr(dash + 1, '-', (size_t) (key_end - dash - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7786 buffer[dash - key_start] = '_';
7787 }
7788
7789 pm_string_owned_init(&key, buffer, key_length);
7790 }
7791
7792 // Finally, we can start checking the key against the list of known
7793 // magic comment keys, and potentially change state based on that.
7794 const uint8_t *key_source = pm_string_source(&key);
7795 uint32_t value_length = (uint32_t) (value_end - value_start);
7796
7797 // We only want to attempt to compare against encoding comments if it's
7798 // the first line in the file (or the second in the case of a shebang).
7799 if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7800 if (
7801 (key_length == 8 && pm_strncasecmp(key_source, (const uint8_t *) "encoding", 8) == 0) ||
7802 (key_length == 6 && pm_strncasecmp(key_source, (const uint8_t *) "coding", 6) == 0)
7803 ) {
7804 result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7805 }
7806 }
7807
7808 if (key_length == 11) {
7809 if (pm_strncasecmp(key_source, (const uint8_t *) "warn_indent", 11) == 0) {
7810 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7811 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7812 PM_PARSER_WARN_TOKEN_FORMAT(
7813 parser,
7814 &parser->current,
7815 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7816 (int) key_length,
7817 (const char *) key_source,
7818 (int) value_length,
7819 (const char *) value_start
7820 );
7821 break;
7822 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7823 parser->warn_mismatched_indentation = false;
7824 break;
7825 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7826 parser->warn_mismatched_indentation = true;
7827 break;
7828 }
7829 }
7830 } else if (key_length == 21) {
7831 if (pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) {
7832 // We only want to handle frozen string literal comments if it's
7833 // before any semantic tokens have been seen.
7834 if (semantic_token_seen) {
7835 pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
7836 } else {
7837 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7838 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7839 PM_PARSER_WARN_TOKEN_FORMAT(
7840 parser,
7841 &parser->current,
7842 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7843 (int) key_length,
7844 (const char *) key_source,
7845 (int) value_length,
7846 (const char *) value_start
7847 );
7848 break;
7849 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7850 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
7851 break;
7852 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7853 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
7854 break;
7855 }
7856 }
7857 }
7858 } else if (key_length == 24) {
7859 if (pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) {
7860 const uint8_t *cursor = parser->current.start;
7861 while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
7862
7863 if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
7864 pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
7865 } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
7866 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
7867 } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
7868 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
7869 } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
7870 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
7871 } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
7872 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
7873 } else {
7874 PM_PARSER_WARN_TOKEN_FORMAT(
7875 parser,
7876 &parser->current,
7877 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7878 (int) key_length,
7879 (const char *) key_source,
7880 (int) value_length,
7881 (const char *) value_start
7882 );
7883 }
7884 }
7885 }
7886
7887 // When we're done, we want to free the string in case we had to
7888 // allocate memory for it.
7889 pm_string_cleanup(&key);
7890
7891 // Allocate a new magic comment node to append to the parser's list.
7892 pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_magic_comment_t), PRISM_ALIGNOF(pm_magic_comment_t));
7893 magic_comment->node.next = NULL;
7894 magic_comment->key = (pm_location_t) { .start = U32(key_start - parser->start), .length = U32(key_length) };
7895 magic_comment->value = (pm_location_t) { .start = U32(value_start - parser->start), .length = value_length };
7896 pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment);
7897 }
7898
7899 return result;
7900}
7901
7902/******************************************************************************/
7903/* Context manipulations */
7904/******************************************************************************/
7905
7906static const uint32_t context_terminators[] = {
7907 [PM_CONTEXT_NONE] = 0,
7908 [PM_CONTEXT_BEGIN] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7909 [PM_CONTEXT_BEGIN_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7910 [PM_CONTEXT_BEGIN_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7911 [PM_CONTEXT_BEGIN_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7912 [PM_CONTEXT_BLOCK_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7913 [PM_CONTEXT_BLOCK_KEYWORDS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7914 [PM_CONTEXT_BLOCK_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7915 [PM_CONTEXT_BLOCK_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7916 [PM_CONTEXT_BLOCK_PARAMETERS] = (1U << PM_TOKEN_PIPE),
7917 [PM_CONTEXT_BLOCK_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7918 [PM_CONTEXT_CASE_WHEN] = (1U << PM_TOKEN_KEYWORD_WHEN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7919 [PM_CONTEXT_CASE_IN] = (1U << PM_TOKEN_KEYWORD_IN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7920 [PM_CONTEXT_CLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7921 [PM_CONTEXT_CLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7922 [PM_CONTEXT_CLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7923 [PM_CONTEXT_CLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7924 [PM_CONTEXT_DEF] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7925 [PM_CONTEXT_DEF_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7926 [PM_CONTEXT_DEF_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7927 [PM_CONTEXT_DEF_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7928 [PM_CONTEXT_DEF_PARAMS] = (1U << PM_TOKEN_EOF),
7929 [PM_CONTEXT_DEFINED] = (1U << PM_TOKEN_EOF),
7930 [PM_CONTEXT_DEFAULT_PARAMS] = (1U << PM_TOKEN_COMMA) | (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7931 [PM_CONTEXT_ELSE] = (1U << PM_TOKEN_KEYWORD_END),
7932 [PM_CONTEXT_ELSIF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7933 [PM_CONTEXT_EMBEXPR] = (1U << PM_TOKEN_EMBEXPR_END),
7934 [PM_CONTEXT_FOR] = (1U << PM_TOKEN_KEYWORD_END),
7935 [PM_CONTEXT_FOR_INDEX] = (1U << PM_TOKEN_KEYWORD_IN),
7936 [PM_CONTEXT_IF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7937 [PM_CONTEXT_LAMBDA_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7938 [PM_CONTEXT_LAMBDA_DO_END] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7939 [PM_CONTEXT_LAMBDA_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7940 [PM_CONTEXT_LAMBDA_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7941 [PM_CONTEXT_LAMBDA_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7942 [PM_CONTEXT_LOOP_PREDICATE] = (1U << PM_TOKEN_KEYWORD_DO) | (1U << PM_TOKEN_KEYWORD_THEN),
7943 [PM_CONTEXT_MAIN] = (1U << PM_TOKEN_EOF),
7944 [PM_CONTEXT_MODULE] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7945 [PM_CONTEXT_MODULE_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7946 [PM_CONTEXT_MODULE_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7947 [PM_CONTEXT_MODULE_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7948 [PM_CONTEXT_MULTI_TARGET] = (1U << PM_TOKEN_EOF),
7949 [PM_CONTEXT_PARENS] = (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7950 [PM_CONTEXT_POSTEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7951 [PM_CONTEXT_PREDICATE] = (1U << PM_TOKEN_KEYWORD_THEN) | (1U << PM_TOKEN_NEWLINE) | (1U << PM_TOKEN_SEMICOLON),
7952 [PM_CONTEXT_PREEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7953 [PM_CONTEXT_RESCUE_MODIFIER] = (1U << PM_TOKEN_EOF),
7954 [PM_CONTEXT_SCLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7955 [PM_CONTEXT_SCLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7956 [PM_CONTEXT_SCLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7957 [PM_CONTEXT_SCLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7958 [PM_CONTEXT_TERNARY] = (1U << PM_TOKEN_EOF),
7959 [PM_CONTEXT_UNLESS] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7960 [PM_CONTEXT_UNTIL] = (1U << PM_TOKEN_KEYWORD_END),
7961 [PM_CONTEXT_WHILE] = (1U << PM_TOKEN_KEYWORD_END),
7962};
7963
7964static PRISM_INLINE bool
7965context_terminator(pm_context_t context, pm_token_t *token) {
7966 return token->type < 32 && (context_terminators[context] & (1U << token->type));
7967}
7968
7973static pm_context_t
7974context_recoverable(const pm_parser_t *parser, pm_token_t *token) {
7975 pm_context_node_t *context_node = parser->current_context;
7976
7977 while (context_node != NULL) {
7978 if (context_terminator(context_node->context, token)) return context_node->context;
7979 context_node = context_node->prev;
7980 }
7981
7982 return PM_CONTEXT_NONE;
7983}
7984
7985PM_STATIC_ASSERT(__LINE__, PM_CONTEXT_MAXIMUM <= 64, "Expected every context to fit in the context mask.");
7986
7987static bool
7988context_push(pm_parser_t *parser, pm_context_t context) {
7989 pm_context_node_t *context_node = (pm_context_node_t *) xmalloc(sizeof(pm_context_node_t));
7990 if (context_node == NULL) return false;
7991
7992 pm_context_node_t *previous = parser->current_context;
7993 *context_node = (pm_context_node_t) {
7994 .context = context,
7995 .prev = previous,
7996 .mask = (previous == NULL ? 0 : previous->mask) | ((uint64_t) 1 << context)
7997 };
7998
7999 parser->current_context = context_node;
8000 return true;
8001}
8002
8003static void
8004context_pop(pm_parser_t *parser) {
8005 pm_context_node_t *prev = parser->current_context->prev;
8006 xfree_sized(parser->current_context, sizeof(pm_context_node_t));
8007 parser->current_context = prev;
8008}
8009
8010static bool
8011context_p(const pm_parser_t *parser, pm_context_t context) {
8012 return parser->current_context != NULL && (parser->current_context->mask & ((uint64_t) 1 << context)) != 0;
8013}
8014
8015static bool
8016context_def_p(const pm_parser_t *parser) {
8017 pm_context_node_t *context_node = parser->current_context;
8018
8019 while (context_node != NULL) {
8020 switch (context_node->context) {
8021 case PM_CONTEXT_DEF:
8022 case PM_CONTEXT_DEF_PARAMS:
8023 case PM_CONTEXT_DEF_ENSURE:
8024 case PM_CONTEXT_DEF_RESCUE:
8025 case PM_CONTEXT_DEF_ELSE:
8026 return true;
8027 case PM_CONTEXT_CLASS:
8028 case PM_CONTEXT_CLASS_ENSURE:
8029 case PM_CONTEXT_CLASS_RESCUE:
8030 case PM_CONTEXT_CLASS_ELSE:
8031 case PM_CONTEXT_MODULE:
8032 case PM_CONTEXT_MODULE_ENSURE:
8033 case PM_CONTEXT_MODULE_RESCUE:
8034 case PM_CONTEXT_MODULE_ELSE:
8035 case PM_CONTEXT_SCLASS:
8036 case PM_CONTEXT_SCLASS_ENSURE:
8037 case PM_CONTEXT_SCLASS_RESCUE:
8038 case PM_CONTEXT_SCLASS_ELSE:
8039 return false;
8040 default:
8041 context_node = context_node->prev;
8042 }
8043 }
8044
8045 return false;
8046}
8047
8052static const char *
8053context_human(pm_context_t context) {
8054 switch (context) {
8055 case PM_CONTEXT_NONE:
8056 case PM_CONTEXT_MAXIMUM:
8057 assert(false && "unreachable");
8058 return "";
8059 case PM_CONTEXT_BEGIN: return "begin statement";
8060 case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block";
8061 case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block";
8062 case PM_CONTEXT_BLOCK_PARAMETERS: return "'|'..'|' block parameter";
8063 case PM_CONTEXT_CASE_WHEN: return "'when' clause";
8064 case PM_CONTEXT_CASE_IN: return "'in' clause";
8065 case PM_CONTEXT_CLASS: return "class definition";
8066 case PM_CONTEXT_DEF: return "method definition";
8067 case PM_CONTEXT_DEF_PARAMS: return "method parameters";
8068 case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value";
8069 case PM_CONTEXT_DEFINED: return "'defined?' expression";
8070 case PM_CONTEXT_ELSE:
8071 case PM_CONTEXT_BEGIN_ELSE:
8072 case PM_CONTEXT_BLOCK_ELSE:
8073 case PM_CONTEXT_CLASS_ELSE:
8074 case PM_CONTEXT_DEF_ELSE:
8075 case PM_CONTEXT_LAMBDA_ELSE:
8076 case PM_CONTEXT_MODULE_ELSE:
8077 case PM_CONTEXT_SCLASS_ELSE: return "'else' clause";
8078 case PM_CONTEXT_ELSIF: return "'elsif' clause";
8079 case PM_CONTEXT_EMBEXPR: return "embedded expression";
8080 case PM_CONTEXT_BEGIN_ENSURE:
8081 case PM_CONTEXT_BLOCK_ENSURE:
8082 case PM_CONTEXT_CLASS_ENSURE:
8083 case PM_CONTEXT_DEF_ENSURE:
8084 case PM_CONTEXT_LAMBDA_ENSURE:
8085 case PM_CONTEXT_MODULE_ENSURE:
8086 case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause";
8087 case PM_CONTEXT_FOR: return "for loop";
8088 case PM_CONTEXT_FOR_INDEX: return "for loop index";
8089 case PM_CONTEXT_IF: return "if statement";
8090 case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block";
8091 case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block";
8092 case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate";
8093 case PM_CONTEXT_MAIN: return "top level context";
8094 case PM_CONTEXT_MODULE: return "module definition";
8095 case PM_CONTEXT_MULTI_TARGET: return "multiple targets";
8096 case PM_CONTEXT_PARENS: return "parentheses";
8097 case PM_CONTEXT_POSTEXE: return "'END' block";
8098 case PM_CONTEXT_PREDICATE: return "predicate";
8099 case PM_CONTEXT_PREEXE: return "'BEGIN' block";
8100 case PM_CONTEXT_BEGIN_RESCUE:
8101 case PM_CONTEXT_BLOCK_RESCUE:
8102 case PM_CONTEXT_CLASS_RESCUE:
8103 case PM_CONTEXT_DEF_RESCUE:
8104 case PM_CONTEXT_LAMBDA_RESCUE:
8105 case PM_CONTEXT_MODULE_RESCUE:
8106 case PM_CONTEXT_RESCUE_MODIFIER:
8107 case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause";
8108 case PM_CONTEXT_SCLASS: return "singleton class definition";
8109 case PM_CONTEXT_TERNARY: return "ternary expression";
8110 case PM_CONTEXT_UNLESS: return "unless statement";
8111 case PM_CONTEXT_UNTIL: return "until statement";
8112 case PM_CONTEXT_WHILE: return "while statement";
8113 }
8114
8115 assert(false && "unreachable");
8116 return "";
8117}
8118
8119/******************************************************************************/
8120/* Specific token lexers */
8121/******************************************************************************/
8122
8123static PRISM_INLINE void
8124pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *string, size_t length, const uint8_t *invalid) {
8125 if (invalid != NULL) {
8126 pm_diagnostic_id_t diag_id = (invalid == (string + length - 1)) ? PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING : PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER;
8127 pm_parser_err(parser, U32(invalid - parser->start), 1, diag_id);
8128 }
8129}
8130
8131static size_t
8132pm_strspn_binary_number_validate(pm_parser_t *parser, const uint8_t *string) {
8133 const uint8_t *invalid = NULL;
8134 size_t length = pm_strspn_binary_number(string, parser->end - string, &invalid);
8135 pm_strspn_number_validate(parser, string, length, invalid);
8136 return length;
8137}
8138
8139static size_t
8140pm_strspn_octal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8141 const uint8_t *invalid = NULL;
8142 size_t length = pm_strspn_octal_number(string, parser->end - string, &invalid);
8143 pm_strspn_number_validate(parser, string, length, invalid);
8144 return length;
8145}
8146
8147static size_t
8148pm_strspn_decimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8149 const uint8_t *invalid = NULL;
8150 size_t length = pm_strspn_decimal_number(string, parser->end - string, &invalid);
8151 pm_strspn_number_validate(parser, string, length, invalid);
8152 return length;
8153}
8154
8155static size_t
8156pm_strspn_hexadecimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8157 const uint8_t *invalid = NULL;
8158 size_t length = pm_strspn_hexadecimal_number(string, parser->end - string, &invalid);
8159 pm_strspn_number_validate(parser, string, length, invalid);
8160 return length;
8161}
8162
8163static pm_token_type_t
8164lex_optional_float_suffix(pm_parser_t *parser, bool* seen_e) {
8165 pm_token_type_t type = PM_TOKEN_INTEGER;
8166
8167 // Here we're going to attempt to parse the optional decimal portion of a
8168 // float. If it's not there, then it's okay and we'll just continue on.
8169 if (peek(parser) == '.') {
8170 if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8171 parser->current.end += 2;
8172 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8173 type = PM_TOKEN_FLOAT;
8174 } else {
8175 // If we had a . and then something else, then it's not a float
8176 // suffix on a number it's a method call or something else.
8177 return type;
8178 }
8179 }
8180
8181 // Here we're going to attempt to parse the optional exponent portion of a
8182 // float. If it's not there, it's okay and we'll just continue on.
8183 if ((peek(parser) == 'e') || (peek(parser) == 'E')) {
8184 if ((peek_offset(parser, 1) == '+') || (peek_offset(parser, 1) == '-')) {
8185 parser->current.end += 2;
8186
8187 if (pm_char_is_decimal_digit(peek(parser))) {
8188 parser->current.end++;
8189 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8190 } else {
8191 pm_parser_err_current(parser, PM_ERR_INVALID_FLOAT_EXPONENT);
8192 }
8193 } else if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8194 parser->current.end++;
8195 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8196 } else {
8197 return type;
8198 }
8199
8200 *seen_e = true;
8201 type = PM_TOKEN_FLOAT;
8202 }
8203
8204 return type;
8205}
8206
8207static pm_token_type_t
8208lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) {
8209 pm_token_type_t type = PM_TOKEN_INTEGER;
8210 *seen_e = false;
8211
8212 if (peek_offset(parser, -1) == '0') {
8213 switch (*parser->current.end) {
8214 // 0d1111 is a decimal number
8215 case 'd':
8216 case 'D':
8217 parser->current.end++;
8218 if (pm_char_is_decimal_digit(peek(parser))) {
8219 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8220 } else {
8221 match(parser, '_');
8222 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_DECIMAL);
8223 }
8224
8225 break;
8226
8227 // 0b1111 is a binary number
8228 case 'b':
8229 case 'B':
8230 parser->current.end++;
8231 if (pm_char_is_binary_digit(peek(parser))) {
8232 parser->current.end += pm_strspn_binary_number_validate(parser, parser->current.end);
8233 } else {
8234 match(parser, '_');
8235 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY);
8236 }
8237
8238 parser->integer.base = PM_INTEGER_BASE_FLAGS_BINARY;
8239 break;
8240
8241 // 0o1111 is an octal number
8242 case 'o':
8243 case 'O':
8244 parser->current.end++;
8245 if (pm_char_is_octal_digit(peek(parser))) {
8246 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8247 } else {
8248 match(parser, '_');
8249 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL);
8250 }
8251
8252 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8253 break;
8254
8255 // 01111 is an octal number
8256 case '_':
8257 case '0':
8258 case '1':
8259 case '2':
8260 case '3':
8261 case '4':
8262 case '5':
8263 case '6':
8264 case '7':
8265 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8266 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8267 break;
8268
8269 // 0x1111 is a hexadecimal number
8270 case 'x':
8271 case 'X':
8272 parser->current.end++;
8273 if (pm_char_is_hexadecimal_digit(peek(parser))) {
8274 parser->current.end += pm_strspn_hexadecimal_number_validate(parser, parser->current.end);
8275 } else {
8276 match(parser, '_');
8277 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL);
8278 }
8279
8280 parser->integer.base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL;
8281 break;
8282
8283 // 0.xxx is a float
8284 case '.': {
8285 type = lex_optional_float_suffix(parser, seen_e);
8286 break;
8287 }
8288
8289 // 0exxx is a float
8290 case 'e':
8291 case 'E': {
8292 type = lex_optional_float_suffix(parser, seen_e);
8293 break;
8294 }
8295 }
8296 } else {
8297 // If it didn't start with a 0, then we'll lex as far as we can into a
8298 // decimal number. We compute the integer value inline to avoid
8299 // re-scanning the digits later in pm_integer_parse.
8300 {
8301 const uint8_t *cursor = parser->current.end;
8302 const uint8_t *end = parser->end;
8303 uint64_t value = (uint64_t) (cursor[-1] - '0');
8304
8305 bool has_underscore = false;
8306 bool prev_underscore = false;
8307 const uint8_t *invalid = NULL;
8308
8309 while (cursor < end) {
8310 uint8_t c = *cursor;
8311 if (c >= '0' && c <= '9') {
8312 if (value <= UINT32_MAX) value = value * 10 + (uint64_t) (c - '0');
8313 prev_underscore = false;
8314 cursor++;
8315 } else if (c == '_') {
8316 has_underscore = true;
8317 if (prev_underscore && invalid == NULL) invalid = cursor;
8318 prev_underscore = true;
8319 cursor++;
8320 } else {
8321 break;
8322 }
8323 }
8324
8325 if (has_underscore) {
8326 if (prev_underscore && invalid == NULL) invalid = cursor - 1;
8327 pm_strspn_number_validate(parser, parser->current.end, (size_t) (cursor - parser->current.end), invalid);
8328 }
8329
8330 if (value <= UINT32_MAX) {
8331 parser->integer.value = (uint32_t) value;
8332 parser->integer.lexed = true;
8333 }
8334
8335 parser->current.end = cursor;
8336 }
8337
8338 // Afterward, we'll lex as far as we can into an optional float suffix.
8339 // Guard the function call: the vast majority of decimal numbers are
8340 // plain integers, so avoid the call when the next byte cannot start a
8341 // float suffix.
8342 {
8343 uint8_t next = peek(parser);
8344 if (next == '.' || next == 'e' || next == 'E') {
8345 type = lex_optional_float_suffix(parser, seen_e);
8346
8347 // If it turned out to be a float, the cached integer value is
8348 // invalid.
8349 if (type != PM_TOKEN_INTEGER) {
8350 parser->integer.lexed = false;
8351 }
8352 }
8353 }
8354 }
8355
8356 // At this point we have a completed number, but we want to provide the user
8357 // with a good experience if they put an additional .xxx fractional
8358 // component on the end, so we'll check for that here.
8359 if (peek_offset(parser, 0) == '.' && pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8360 const uint8_t *fraction_start = parser->current.end;
8361 const uint8_t *fraction_end = parser->current.end + 2;
8362 fraction_end += pm_strspn_decimal_digit(fraction_end, parser->end - fraction_end);
8363 pm_parser_err(parser, U32(fraction_start - parser->start), U32(fraction_end - fraction_start), PM_ERR_INVALID_NUMBER_FRACTION);
8364 }
8365
8366 return type;
8367}
8368
8369static pm_token_type_t
8370lex_numeric(pm_parser_t *parser) {
8371 pm_token_type_t type = PM_TOKEN_INTEGER;
8372 parser->integer.base = PM_INTEGER_BASE_FLAGS_DECIMAL;
8373 parser->integer.lexed = false;
8374
8375 if (parser->current.end < parser->end) {
8376 bool seen_e = false;
8377 type = lex_numeric_prefix(parser, &seen_e);
8378
8379 const uint8_t *end = parser->current.end;
8380 pm_token_type_t suffix_type = type;
8381
8382 if (type == PM_TOKEN_INTEGER) {
8383 if (match(parser, 'r')) {
8384 suffix_type = PM_TOKEN_INTEGER_RATIONAL;
8385
8386 if (match(parser, 'i')) {
8387 suffix_type = PM_TOKEN_INTEGER_RATIONAL_IMAGINARY;
8388 }
8389 } else if (match(parser, 'i')) {
8390 suffix_type = PM_TOKEN_INTEGER_IMAGINARY;
8391 }
8392 } else {
8393 if (!seen_e && match(parser, 'r')) {
8394 suffix_type = PM_TOKEN_FLOAT_RATIONAL;
8395
8396 if (match(parser, 'i')) {
8397 suffix_type = PM_TOKEN_FLOAT_RATIONAL_IMAGINARY;
8398 }
8399 } else if (match(parser, 'i')) {
8400 suffix_type = PM_TOKEN_FLOAT_IMAGINARY;
8401 }
8402 }
8403
8404 const uint8_t b = peek(parser);
8405 if (b != '\0' && (b >= 0x80 || ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) || b == '_')) {
8406 parser->current.end = end;
8407 } else {
8408 type = suffix_type;
8409 }
8410 }
8411
8412 return type;
8413}
8414
8415static pm_token_type_t
8416lex_global_variable(pm_parser_t *parser) {
8417 if (parser->current.end >= parser->end) {
8418 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8419 return PM_TOKEN_GLOBAL_VARIABLE;
8420 }
8421
8422 // True if multiple characters are allowed after the declaration of the
8423 // global variable. Not true when it starts with "$-".
8424 bool allow_multiple = true;
8425
8426 switch (*parser->current.end) {
8427 case '~': // $~: match-data
8428 case '*': // $*: argv
8429 case '$': // $$: pid
8430 case '?': // $?: last status
8431 case '!': // $!: error string
8432 case '@': // $@: error position
8433 case '/': // $/: input record separator
8434 case '\\': // $\: output record separator
8435 case ';': // $;: field separator
8436 case ',': // $,: output field separator
8437 case '.': // $.: last read line number
8438 case '=': // $=: ignorecase
8439 case ':': // $:: load path
8440 case '<': // $<: reading filename
8441 case '>': // $>: default output handle
8442 case '\"': // $": already loaded files
8443 parser->current.end++;
8444 return PM_TOKEN_GLOBAL_VARIABLE;
8445
8446 case '&': // $&: last match
8447 case '`': // $`: string before last match
8448 case '\'': // $': string after last match
8449 case '+': // $+: string matches last paren.
8450 parser->current.end++;
8451 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_BACK_REFERENCE;
8452
8453 case '0': {
8454 parser->current.end++;
8455 size_t width;
8456
8457 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8458 do {
8459 parser->current.end += width;
8460 } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8461
8462 // $0 isn't allowed to be followed by anything.
8463 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;
8464 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, diag_id);
8465 }
8466
8467 return PM_TOKEN_GLOBAL_VARIABLE;
8468 }
8469
8470 case '1':
8471 case '2':
8472 case '3':
8473 case '4':
8474 case '5':
8475 case '6':
8476 case '7':
8477 case '8':
8478 case '9':
8479 parser->current.end += pm_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end);
8480 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_NUMBERED_REFERENCE;
8481
8482 case '-':
8483 parser->current.end++;
8484 allow_multiple = false;
8486 default: {
8487 size_t width;
8488
8489 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8490 do {
8491 parser->current.end += width;
8492 } while (allow_multiple && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8493 } else if (pm_char_is_whitespace(peek(parser))) {
8494 // If we get here, then we have a $ followed by whitespace,
8495 // which is not allowed.
8496 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8497 } else {
8498 // If we get here, then we have a $ followed by something that
8499 // isn't recognized as a global variable.
8500 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;
8501 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
8502 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);
8503 }
8504
8505 return PM_TOKEN_GLOBAL_VARIABLE;
8506 }
8507 }
8508}
8509
8522static PRISM_INLINE pm_token_type_t
8523lex_keyword(pm_parser_t *parser, const uint8_t *current_start, const char *value, size_t vlen, pm_lex_state_t state, pm_token_type_t type, pm_token_type_t modifier_type) {
8524 if (memcmp(current_start, value, vlen) == 0) {
8525 pm_lex_state_t last_state = parser->lex_state;
8526
8527 if (parser->lex_state & PM_LEX_STATE_FNAME) {
8528 lex_state_set(parser, PM_LEX_STATE_ENDFN);
8529 } else {
8530 lex_state_set(parser, state);
8531 if (state == PM_LEX_STATE_BEG) {
8532 parser->command_start = true;
8533 }
8534
8535 if ((modifier_type != PM_TOKEN_EOF) && !(last_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
8536 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
8537 return modifier_type;
8538 }
8539 }
8540
8541 return type;
8542 }
8543
8544 return PM_TOKEN_EOF;
8545}
8546
8547static pm_token_type_t
8548lex_identifier(pm_parser_t *parser, bool previous_command_start) {
8549 // Lex as far as we can into the current identifier.
8550 size_t width;
8551 const uint8_t *end = parser->end;
8552 const uint8_t *current_start = parser->current.start;
8553 const uint8_t *current_end = parser->current.end;
8554 bool encoding_changed = parser->encoding_changed;
8555
8556 if (encoding_changed) {
8557 while ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) {
8558 current_end += width;
8559 }
8560 } else {
8561 // Fast path: scan ASCII identifier bytes using wide operations.
8562 current_end += scan_identifier_ascii(current_end, end);
8563
8564 // Byte-at-a-time fallback for the tail and any UTF-8 sequences.
8565 while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) {
8566 current_end += width;
8567 }
8568 }
8569 parser->current.end = current_end;
8570
8571 // Now cache the length of the identifier so that we can quickly compare it
8572 // against known keywords.
8573 width = (size_t) (current_end - current_start);
8574
8575 if (current_end < end) {
8576 if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
8577 // First we'll attempt to extend the identifier by a ! or ?. Then we'll
8578 // check if we're returning the defined? keyword or just an identifier.
8579 width++;
8580
8581 if (
8582 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8583 (peek(parser) == ':') && (peek_offset(parser, 1) != ':')
8584 ) {
8585 // If we're in a position where we can accept a : at the end of an
8586 // identifier, then we'll optionally accept it.
8587 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8588 (void) match(parser, ':');
8589
8590 /* A label is a symbol lexed inline rather than through a lex
8591 * mode, so it clears the encoding here. */
8592 parser->explicit_encoding = NULL;
8593 return PM_TOKEN_LABEL;
8594 }
8595
8596 if (parser->lex_state != PM_LEX_STATE_DOT) {
8597 if (width == 8 && (lex_keyword(parser, current_start, "defined?", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF) != PM_TOKEN_EOF)) {
8598 return PM_TOKEN_KEYWORD_DEFINED;
8599 }
8600 }
8601
8602 return PM_TOKEN_METHOD_NAME;
8603 }
8604
8605 if (lex_state_p(parser, PM_LEX_STATE_FNAME) && peek_offset(parser, 1) != '~' && peek_offset(parser, 1) != '>' && (peek_offset(parser, 1) != '=' || peek_offset(parser, 2) == '>') && match(parser, '=')) {
8606 // If we're in a position where we can accept a = at the end of an
8607 // identifier, then we'll optionally accept it.
8608 return PM_TOKEN_IDENTIFIER;
8609 }
8610
8611 if (
8612 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8613 peek(parser) == ':' && peek_offset(parser, 1) != ':'
8614 ) {
8615 // If we're in a position where we can accept a : at the end of an
8616 // identifier, then we'll optionally accept it.
8617 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8618 (void) match(parser, ':');
8619
8620 /* A label is a symbol lexed inline rather than through a lex
8621 * mode, so it clears the encoding here. */
8622 parser->explicit_encoding = NULL;
8623 return PM_TOKEN_LABEL;
8624 }
8625 }
8626
8627 if (parser->lex_state != PM_LEX_STATE_DOT) {
8628 pm_token_type_t type;
8629
8630 /* The lex state from before lex_keyword transitions it, mirroring the
8631 * `state = p->lex.state` capture in parse.y's keyword handling. */
8632 pm_lex_state_t previous_lex_state = parser->lex_state;
8633
8634 switch (width) {
8635 case 2:
8636 if (lex_keyword(parser, current_start, "do", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF) != PM_TOKEN_EOF) {
8637 /* In FNAME position (a symbol like `:do` or a method name
8638 * like `def do`), `do` is a plain name rather than a
8639 * block, loop, or lambda opener, so none of the
8640 * discrimination below applies. This mirrors parse.y,
8641 * whose EXPR_FNAME early-return precedes all of the
8642 * keyword_do special-casing (and never touches
8643 * lpar_beg). */
8644 if (previous_lex_state & PM_LEX_STATE_FNAME) {
8645 return PM_TOKEN_KEYWORD_DO;
8646 }
8647 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
8648 // At the bare nesting level of a lambda literal (no
8649 // delimiter opened since `->`), a `do` opens the lambda
8650 // body. This is a distinct token so that a command in a
8651 // parameter default cannot consume it as its own block
8652 // (`-> a = foo do end` is `->(a = foo) do end`). It
8653 // mirrors CRuby's keyword_do_LAMBDA.
8654 //
8655 // Clear the nesting so that no token within the
8656 // `do`/`end` body is considered to be at the beginning
8657 // of a lambda; the parser restores the enclosing value
8658 // once the lambda has been fully parsed. This mirrors
8659 // parse.y setting `p->lex.lpar_beg = -1` when lexing
8660 // keyword_do_LAMBDA.
8661 parser->lambda_enclosure_nesting = -1;
8662 return PM_TOKEN_KEYWORD_DO_LAMBDA;
8663 }
8664 if (pm_do_loop_stack_p(parser)) {
8665 return PM_TOKEN_KEYWORD_DO_LOOP;
8666 }
8667 if (!pm_accepts_block_stack_p(parser)) {
8668 return PM_TOKEN_KEYWORD_DO_BLOCK;
8669 }
8670 return PM_TOKEN_KEYWORD_DO;
8671 }
8672
8673 if ((type = lex_keyword(parser, current_start, "if", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IF, PM_TOKEN_KEYWORD_IF_MODIFIER)) != PM_TOKEN_EOF) return type;
8674 if ((type = lex_keyword(parser, current_start, "in", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_IN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8675 if ((type = lex_keyword(parser, current_start, "or", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_OR, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8676 break;
8677 case 3:
8678 if ((type = lex_keyword(parser, current_start, "and", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_AND, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8679 if ((type = lex_keyword(parser, current_start, "def", width, PM_LEX_STATE_FNAME, PM_TOKEN_KEYWORD_DEF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8680 if ((type = lex_keyword(parser, current_start, "end", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8681 if ((type = lex_keyword(parser, current_start, "END", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_END_UPCASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8682 if ((type = lex_keyword(parser, current_start, "for", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_FOR, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8683 if ((type = lex_keyword(parser, current_start, "nil", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_NIL, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8684 if ((type = lex_keyword(parser, current_start, "not", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_NOT, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8685 break;
8686 case 4:
8687 if ((type = lex_keyword(parser, current_start, "case", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_CASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8688 if ((type = lex_keyword(parser, current_start, "else", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8689 if ((type = lex_keyword(parser, current_start, "next", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_NEXT, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8690 if ((type = lex_keyword(parser, current_start, "redo", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_REDO, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8691 if ((type = lex_keyword(parser, current_start, "self", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_SELF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8692 if ((type = lex_keyword(parser, current_start, "then", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8693 if ((type = lex_keyword(parser, current_start, "true", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_TRUE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8694 if ((type = lex_keyword(parser, current_start, "when", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8695 break;
8696 case 5:
8697 if ((type = lex_keyword(parser, current_start, "alias", width, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_ALIAS, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8698 if ((type = lex_keyword(parser, current_start, "begin", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_BEGIN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8699 if ((type = lex_keyword(parser, current_start, "BEGIN", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_BEGIN_UPCASE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8700 if ((type = lex_keyword(parser, current_start, "break", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_BREAK, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8701 if ((type = lex_keyword(parser, current_start, "class", width, PM_LEX_STATE_CLASS, PM_TOKEN_KEYWORD_CLASS, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8702 if ((type = lex_keyword(parser, current_start, "elsif", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8703 if ((type = lex_keyword(parser, current_start, "false", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_FALSE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8704 if ((type = lex_keyword(parser, current_start, "retry", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD_RETRY, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8705 if ((type = lex_keyword(parser, current_start, "super", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_SUPER, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8706 if ((type = lex_keyword(parser, current_start, "undef", width, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM, PM_TOKEN_KEYWORD_UNDEF, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8707 if ((type = lex_keyword(parser, current_start, "until", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNTIL, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) != PM_TOKEN_EOF) return type;
8708 if ((type = lex_keyword(parser, current_start, "while", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_WHILE, PM_TOKEN_KEYWORD_WHILE_MODIFIER)) != PM_TOKEN_EOF) return type;
8709 if ((type = lex_keyword(parser, current_start, "yield", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_YIELD, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8710 break;
8711 case 6:
8712 if ((type = lex_keyword(parser, current_start, "ensure", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8713 if ((type = lex_keyword(parser, current_start, "module", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_MODULE, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8714 if ((type = lex_keyword(parser, current_start, "rescue", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) != PM_TOKEN_EOF) return type;
8715 if ((type = lex_keyword(parser, current_start, "return", width, PM_LEX_STATE_MID, PM_TOKEN_KEYWORD_RETURN, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8716 if ((type = lex_keyword(parser, current_start, "unless", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_UNLESS, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) != PM_TOKEN_EOF) return type;
8717 break;
8718 case 8:
8719 if ((type = lex_keyword(parser, current_start, "__LINE__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___LINE__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8720 if ((type = lex_keyword(parser, current_start, "__FILE__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___FILE__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8721 break;
8722 case 12:
8723 if ((type = lex_keyword(parser, current_start, "__ENCODING__", width, PM_LEX_STATE_END, PM_TOKEN_KEYWORD___ENCODING__, PM_TOKEN_EOF)) != PM_TOKEN_EOF) return type;
8724 break;
8725 }
8726 }
8727
8728 if (encoding_changed) {
8729 return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8730 }
8731
8732 /* Identifiers usually start with an ASCII byte, for which the uppercase
8733 * check is a simple range comparison. This avoids the call into the
8734 * encoding module for every identifier. */
8735 if (*current_start < 0x80) {
8736 return (*current_start >= 'A' && *current_start <= 'Z') ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8737 }
8738 return pm_encoding_utf_8_isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8739}
8740
8745static bool
8746current_token_starts_line(pm_parser_t *parser) {
8747 return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n');
8748}
8749
8764static pm_token_type_t
8765lex_interpolation(pm_parser_t *parser, const uint8_t *pound) {
8766 // If there is no content following this #, then we're at the end of
8767 // the string and we can safely return string content.
8768 if (pound + 1 >= parser->end) {
8769 parser->current.end = pound + 1;
8770 return PM_TOKEN_STRING_CONTENT;
8771 }
8772
8773 // Now we'll check against the character that follows the #. If it
8774 // constitutes valid interplation, we'll handle that, otherwise we'll return
8775 // 0.
8776 switch (pound[1]) {
8777 case '@': {
8778 // In this case we may have hit an embedded instance or class variable.
8779 if (pound + 2 >= parser->end) {
8780 parser->current.end = pound + 1;
8781 return PM_TOKEN_STRING_CONTENT;
8782 }
8783
8784 // If we're looking at a @ and there's another @, then we'll skip past the
8785 // second @.
8786 const uint8_t *variable = pound + 2;
8787 if (*variable == '@' && pound + 3 < parser->end) variable++;
8788
8789 if (char_is_identifier_start(parser, variable, parser->end - variable)) {
8790 // At this point we're sure that we've either hit an embedded instance
8791 // or class variable. In this case we'll first need to check if we've
8792 // already consumed content.
8793 if (pound > parser->current.start) {
8794 parser->current.end = pound;
8795 return PM_TOKEN_STRING_CONTENT;
8796 }
8797
8798 // Otherwise we need to return the embedded variable token
8799 // and then switch to the embedded variable lex mode.
8800 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8801 parser->current.end = pound + 1;
8802 return PM_TOKEN_EMBVAR;
8803 }
8804
8805 // If we didn't get a valid interpolation, then this is just regular
8806 // string content. This is like if we get "#@-". In this case the caller
8807 // should keep lexing.
8808 parser->current.end = pound + 1;
8809 return 0;
8810 }
8811 case '$':
8812 // In this case we may have hit an embedded global variable. If there's
8813 // not enough room, then we'll just return string content.
8814 if (pound + 2 >= parser->end) {
8815 parser->current.end = pound + 1;
8816 return PM_TOKEN_STRING_CONTENT;
8817 }
8818
8819 // This is the character that we're going to check to see if it is the
8820 // start of an identifier that would indicate that this is a global
8821 // variable.
8822 const uint8_t *check = pound + 2;
8823
8824 if (pound[2] == '-') {
8825 if (pound + 3 >= parser->end) {
8826 parser->current.end = pound + 2;
8827 return PM_TOKEN_STRING_CONTENT;
8828 }
8829
8830 check++;
8831 }
8832
8833 // If the character that we're going to check is the start of an
8834 // identifier, or we don't have a - and the character is a decimal number
8835 // or a global name punctuation character, then we've hit an embedded
8836 // global variable.
8837 if (
8838 char_is_identifier_start(parser, check, parser->end - check) ||
8839 (pound[2] != '-' && (pm_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2])))
8840 ) {
8841 // In this case we've hit an embedded global variable. First check to
8842 // see if we've already consumed content. If we have, then we need to
8843 // return that content as string content first.
8844 if (pound > parser->current.start) {
8845 parser->current.end = pound;
8846 return PM_TOKEN_STRING_CONTENT;
8847 }
8848
8849 // Otherwise, we need to return the embedded variable token and switch
8850 // to the embedded variable lex mode.
8851 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8852 parser->current.end = pound + 1;
8853 return PM_TOKEN_EMBVAR;
8854 }
8855
8856 // In this case we've hit a #$ that does not indicate a global variable.
8857 // In this case we'll continue lexing past it.
8858 parser->current.end = pound + 1;
8859 return 0;
8860 case '{':
8861 // In this case it's the start of an embedded expression. If we have
8862 // already consumed content, then we need to return that content as string
8863 // content first.
8864 if (pound > parser->current.start) {
8865 parser->current.end = pound;
8866 return PM_TOKEN_STRING_CONTENT;
8867 }
8868
8869 parser->enclosure_nesting++;
8870
8871 // Otherwise we'll skip past the #{ and begin lexing the embedded
8872 // expression.
8873 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBEXPR });
8874 parser->current.end = pound + 2;
8875 parser->command_start = true;
8876 pm_enclosure_frame_push(parser);
8877 return PM_TOKEN_EMBEXPR_BEGIN;
8878 default:
8879 // In this case we've hit a # that doesn't constitute interpolation. We'll
8880 // mark that by returning the not provided token type. This tells the
8881 // consumer to keep lexing forward.
8882 parser->current.end = pound + 1;
8883 return 0;
8884 }
8885}
8886
8887static const uint8_t PM_ESCAPE_FLAG_NONE = 0x0;
8888static const uint8_t PM_ESCAPE_FLAG_CONTROL = 0x1;
8889static const uint8_t PM_ESCAPE_FLAG_META = 0x2;
8890static const uint8_t PM_ESCAPE_FLAG_SINGLE = 0x4;
8891static const uint8_t PM_ESCAPE_FLAG_REGEXP = 0x8;
8892
8896static const bool ascii_printable_chars[] = {
8897 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
8898 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8899 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8900 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8901 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8902 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
8903 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8904 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
8905};
8906
8907static PRISM_INLINE bool
8908char_is_ascii_printable(const uint8_t b) {
8909 return (b < 0x80) && ascii_printable_chars[b];
8910}
8911
8916static PRISM_INLINE uint8_t
8917escape_hexadecimal_digit(const uint8_t value) {
8918 return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9);
8919}
8920
8926static PRISM_INLINE uint32_t
8927escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location, const uint8_t flags) {
8928 uint32_t value = 0;
8929 for (size_t index = 0; index < length; index++) {
8930 if (index != 0) value <<= 4;
8931 value |= escape_hexadecimal_digit(string[index]);
8932 }
8933
8934 // Here we're going to verify that the value is actually a valid Unicode
8935 // codepoint and not a surrogate pair.
8936 if (value >= 0xD800 && value <= 0xDFFF) {
8937 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8938 // In regexp context, defer the error to regexp encoding
8939 // validation where we can produce a regexp-specific message.
8940 } else if (error_location != NULL) {
8941 pm_parser_err(parser, error_location->start, error_location->length, PM_ERR_ESCAPE_INVALID_UNICODE);
8942 } else {
8943 pm_parser_err(parser, U32(string - parser->start), U32(length), PM_ERR_ESCAPE_INVALID_UNICODE);
8944 }
8945 return 0xFFFD;
8946 }
8947
8948 return value;
8949}
8950
8954static PRISM_INLINE uint8_t
8955escape_byte(uint8_t value, const uint8_t flags) {
8956 if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x9f;
8957 if (flags & PM_ESCAPE_FLAG_META) value |= 0x80;
8958 return value;
8959}
8960
8964static PRISM_INLINE void
8965escape_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) {
8966 // \u escape sequences in string-like structures implicitly change the
8967 // encoding to UTF-8 if they are >= 0x80 or if they are used in a character
8968 // literal.
8969 if (value >= 0x80 || flags & PM_ESCAPE_FLAG_SINGLE) {
8970 if (parser->explicit_encoding != NULL && parser->explicit_encoding != PM_ENCODING_UTF_8_ENTRY) {
8971 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8972 // In regexp context, suppress this error — the regexp encoding
8973 // validation will produce a more specific error message.
8974 } else {
8975 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(end - start), PM_ERR_MIXED_ENCODING, parser->explicit_encoding->name);
8976 }
8977 }
8978
8979 parser->explicit_encoding = PM_ENCODING_UTF_8_ENTRY;
8980 }
8981
8982 if (!pm_buffer_append_unicode_codepoint(buffer, value)) {
8983 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8984 // In regexp context, defer the error to the regexp encoding
8985 // validation which produces a regexp-specific message.
8986 } else {
8987 pm_parser_err(parser, U32(start - parser->start), U32(end - start), PM_ERR_ESCAPE_INVALID_UNICODE);
8988 }
8989
8990 pm_buffer_append_byte(buffer, 0xEF);
8991 pm_buffer_append_byte(buffer, 0xBF);
8992 pm_buffer_append_byte(buffer, 0xBD);
8993 }
8994}
8995
9000static PRISM_INLINE void
9001escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t flags, uint8_t byte) {
9002 if (byte >= 0x80) {
9003 if (parser->explicit_encoding != NULL && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
9004 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9005 // In regexp context, suppress this error — the regexp encoding
9006 // validation will produce a more specific error message.
9007 } else {
9008 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_MIXED_ENCODING, parser->encoding->name);
9009 }
9010 }
9011
9012 parser->explicit_encoding = parser->encoding;
9013 }
9014
9015 pm_buffer_append_byte(buffer, byte);
9016}
9017
9033static PRISM_INLINE void
9034escape_write_byte(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags, uint8_t byte) {
9035 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9036 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", byte);
9037 }
9038
9039 escape_write_byte_encoded(parser, buffer, flags, byte);
9040}
9041
9045static PRISM_INLINE void
9046escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9047 size_t width;
9048 if (parser->encoding_changed) {
9049 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9050 } else {
9051 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9052 }
9053
9054 if (width == 1) {
9055 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);
9056 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(*parser->current.end++, flags));
9057 } else if (width > 1) {
9058 // Valid multibyte character. Just ignore escape.
9059 pm_buffer_t *b = (flags & PM_ESCAPE_FLAG_REGEXP) ? regular_expression_buffer : buffer;
9060 pm_buffer_append_bytes(b, parser->current.end, width);
9061 parser->current.end += width;
9062 } else {
9063 // Assume the next character wasn't meant to be part of this escape
9064 // sequence since it is invalid. Add an error and move on.
9065 parser->current.end++;
9066 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9067 }
9068}
9069
9075static void
9076escape_read_warn(pm_parser_t *parser, uint8_t flags, uint8_t flag, const char *type) {
9077#define FLAG(value) ((value & PM_ESCAPE_FLAG_CONTROL) ? "\\C-" : (value & PM_ESCAPE_FLAG_META) ? "\\M-" : "")
9078
9079 PM_PARSER_WARN_TOKEN_FORMAT(
9080 parser,
9081 &parser->current,
9082 PM_WARN_INVALID_CHARACTER,
9083 FLAG(flags),
9084 FLAG(flag),
9085 type
9086 );
9087
9088#undef FLAG
9089}
9090
9094static void
9095escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9096 uint8_t peeked = peek(parser);
9097 switch (peeked) {
9098 case '\\': {
9099 parser->current.end++;
9100 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\\', flags));
9101 return;
9102 }
9103 case '\'': {
9104 parser->current.end++;
9105 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\'', flags));
9106 return;
9107 }
9108 case 'a': {
9109 parser->current.end++;
9110 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\a', flags));
9111 return;
9112 }
9113 case 'b': {
9114 parser->current.end++;
9115 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\b', flags));
9116 return;
9117 }
9118 case 'e': {
9119 parser->current.end++;
9120 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\033', flags));
9121 return;
9122 }
9123 case 'f': {
9124 parser->current.end++;
9125 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\f', flags));
9126 return;
9127 }
9128 case 'n': {
9129 parser->current.end++;
9130 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\n', flags));
9131 return;
9132 }
9133 case 'r': {
9134 parser->current.end++;
9135 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\r', flags));
9136 return;
9137 }
9138 case 's': {
9139 parser->current.end++;
9140 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(' ', flags));
9141 return;
9142 }
9143 case 't': {
9144 parser->current.end++;
9145 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\t', flags));
9146 return;
9147 }
9148 case 'v': {
9149 parser->current.end++;
9150 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\v', flags));
9151 return;
9152 }
9153 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': {
9154 uint8_t value = (uint8_t) (*parser->current.end - '0');
9155 parser->current.end++;
9156
9157 if (pm_char_is_octal_digit(peek(parser))) {
9158 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9159 parser->current.end++;
9160
9161 if (pm_char_is_octal_digit(peek(parser))) {
9162 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9163 parser->current.end++;
9164 }
9165 }
9166
9167 value = escape_byte(value, flags);
9168 escape_write_byte(parser, buffer, regular_expression_buffer, flags, value);
9169 return;
9170 }
9171 case 'x': {
9172 const uint8_t *start = parser->current.end - 1;
9173
9174 parser->current.end++;
9175 uint8_t byte = peek(parser);
9176
9177 if (pm_char_is_hexadecimal_digit(byte)) {
9178 uint8_t value = escape_hexadecimal_digit(byte);
9179 parser->current.end++;
9180
9181 byte = peek(parser);
9182 if (pm_char_is_hexadecimal_digit(byte)) {
9183 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(byte));
9184 parser->current.end++;
9185 }
9186
9187 value = escape_byte(value, flags);
9188 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9189 if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) {
9190 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", value);
9191 } else {
9192 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9193 }
9194 }
9195
9196 escape_write_byte_encoded(parser, buffer, flags, value);
9197 } else {
9198 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL);
9199 }
9200
9201 return;
9202 }
9203 case 'u': {
9204 const uint8_t *start = parser->current.end - 1;
9205 parser->current.end++;
9206
9207 if (parser->current.end == parser->end) {
9208 const uint8_t *start = parser->current.end - 2;
9209 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9210 } else if (peek(parser) == '{') {
9211 const uint8_t *unicode_codepoints_start = parser->current.end - 2;
9212 parser->current.end++;
9213
9214 size_t whitespace;
9215 while (true) {
9216 if ((whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end)) > 0) {
9217 parser->current.end += whitespace;
9218 } else if (peek(parser) == '\\' && peek_offset(parser, 1) == 'n') {
9219 // This is super hacky, but it gets us nicer error
9220 // messages because we can still pass it off to the
9221 // regular expression engine even if we hit an
9222 // unterminated regular expression.
9223 parser->current.end += 2;
9224 } else {
9225 break;
9226 }
9227 }
9228
9229 const uint8_t *extra_codepoints_start = NULL;
9230 int codepoints_count = 0;
9231
9232 while ((parser->current.end < parser->end) && (*parser->current.end != '}')) {
9233 const uint8_t *unicode_start = parser->current.end;
9234 size_t hexadecimal_length = pm_strspn_hexadecimal_digit(parser->current.end, parser->end - parser->current.end);
9235
9236 if (hexadecimal_length > 6) {
9237 // \u{nnnn} character literal allows only 1-6 hexadecimal digits
9238 pm_parser_err(parser, U32(unicode_start - parser->start), U32(hexadecimal_length), PM_ERR_ESCAPE_INVALID_UNICODE_LONG);
9239 } else if (hexadecimal_length == 0) {
9240 // there are not hexadecimal characters
9241
9242 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9243 // If this is a regular expression, we are going to
9244 // let the regular expression engine handle this
9245 // error instead of us because we don't know at this
9246 // point if we're inside a comment in /x mode.
9247 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9248 } else {
9249 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE);
9250 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9251 }
9252
9253 return;
9254 }
9255
9256 parser->current.end += hexadecimal_length;
9257 codepoints_count++;
9258 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count == 2) {
9259 extra_codepoints_start = unicode_start;
9260 }
9261
9262 uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL, flags);
9263 escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value);
9264
9265 parser->current.end += pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
9266 }
9267
9268 // ?\u{nnnn} character literal should contain only one codepoint
9269 // and cannot be like ?\u{nnnn mmmm}.
9270 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count > 1) {
9271 pm_parser_err(parser, U32(extra_codepoints_start - parser->start), U32(parser->current.end - 1 - extra_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL);
9272 }
9273
9274 if (parser->current.end == parser->end) {
9275 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);
9276 } else if (peek(parser) == '}') {
9277 parser->current.end++;
9278 } else {
9279 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9280 // If this is a regular expression, we are going to let
9281 // the regular expression engine handle this error
9282 // instead of us because we don't know at this point if
9283 // we're inside a comment in /x mode.
9284 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9285 } else {
9286 pm_parser_err(parser, U32(unicode_codepoints_start - parser->start), U32(parser->current.end - unicode_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9287 }
9288 }
9289
9290 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9291 pm_buffer_append_bytes(regular_expression_buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start));
9292 }
9293 } else {
9294 size_t length = pm_strspn_hexadecimal_digit(parser->current.end, MIN(parser->end - parser->current.end, 4));
9295
9296 if (length == 0) {
9297 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9298 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9299 } else {
9300 const uint8_t *start = parser->current.end - 2;
9301 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9302 }
9303 } else if (length == 4) {
9304 uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL, flags);
9305
9306 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9307 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start));
9308 }
9309
9310 escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value);
9311 parser->current.end += 4;
9312 } else {
9313 parser->current.end += length;
9314
9315 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9316 // If this is a regular expression, we are going to let
9317 // the regular expression engine handle this error
9318 // instead of us.
9319 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9320 } else {
9321 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE);
9322 }
9323 }
9324 }
9325
9326 return;
9327 }
9328 case 'c': {
9329 parser->current.end++;
9330 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9331 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9332 }
9333
9334 if (parser->current.end == parser->end) {
9335 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9336 return;
9337 }
9338
9339 uint8_t peeked = peek(parser);
9340 switch (peeked) {
9341 case '?': {
9342 parser->current.end++;
9343 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9344 return;
9345 }
9346 case '\\':
9347 parser->current.end++;
9348
9349 if (match(parser, 'u') || match(parser, 'U')) {
9350 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9351 return;
9352 }
9353
9354 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9355 return;
9356 case ' ':
9357 parser->current.end++;
9358 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9359 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9360 return;
9361 case '\t':
9362 parser->current.end++;
9363 escape_read_warn(parser, flags, 0, "\\t");
9364 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9365 return;
9366 default: {
9367 if (!char_is_ascii_printable(peeked)) {
9368 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9369 return;
9370 }
9371
9372 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);
9373 parser->current.end++;
9374 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9375 return;
9376 }
9377 }
9378 }
9379 case 'C': {
9380 parser->current.end++;
9381 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9382 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9383 }
9384
9385 if (peek(parser) != '-') {
9386 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9387 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9388 return;
9389 }
9390
9391 parser->current.end++;
9392 if (parser->current.end == parser->end) {
9393 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9394 return;
9395 }
9396
9397 uint8_t peeked = peek(parser);
9398 switch (peeked) {
9399 case '?': {
9400 parser->current.end++;
9401 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9402 return;
9403 }
9404 case '\\':
9405 parser->current.end++;
9406
9407 if (match(parser, 'u') || match(parser, 'U')) {
9408 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9409 return;
9410 }
9411
9412 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9413 return;
9414 case ' ':
9415 parser->current.end++;
9416 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9417 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9418 return;
9419 case '\t':
9420 parser->current.end++;
9421 escape_read_warn(parser, flags, 0, "\\t");
9422 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9423 return;
9424 default: {
9425 if (!char_is_ascii_printable(peeked)) {
9426 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9427 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9428 return;
9429 }
9430
9431 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);
9432 parser->current.end++;
9433 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9434 return;
9435 }
9436 }
9437 }
9438 case 'M': {
9439 parser->current.end++;
9440 if (flags & PM_ESCAPE_FLAG_META) {
9441 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
9442 }
9443
9444 if (peek(parser) != '-') {
9445 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9446 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9447 return;
9448 }
9449
9450 parser->current.end++;
9451 if (parser->current.end == parser->end) {
9452 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META);
9453 return;
9454 }
9455
9456 uint8_t peeked = peek(parser);
9457 switch (peeked) {
9458 case '\\':
9459 parser->current.end++;
9460
9461 if (match(parser, 'u') || match(parser, 'U')) {
9462 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9463 return;
9464 }
9465
9466 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_META);
9467 return;
9468 case ' ':
9469 parser->current.end++;
9470 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_META, "\\s");
9471 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9472 return;
9473 case '\t':
9474 parser->current.end++;
9475 escape_read_warn(parser, flags & ((uint8_t) ~PM_ESCAPE_FLAG_CONTROL), PM_ESCAPE_FLAG_META, "\\t");
9476 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9477 return;
9478 default:
9479 if (!char_is_ascii_printable(peeked)) {
9480 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9481 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9482 return;
9483 }
9484
9485 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);
9486 parser->current.end++;
9487 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9488 return;
9489 }
9490 }
9491 case '\r': {
9492 if (peek_offset(parser, 1) == '\n') {
9493 if (parser->heredoc_end == NULL) pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 2);
9494 parser->current.end += 2;
9495 escape_write_byte_encoded(parser, buffer, flags, escape_byte('\n', flags));
9496 return;
9497 }
9499 }
9500 default: {
9501 if ((flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) && !char_is_ascii_printable(peeked)) {
9502 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9503 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9504 return;
9505 }
9506 if (parser->current.end < parser->end) {
9507 escape_write_escape_encoded(parser, buffer, regular_expression_buffer, flags);
9508 } else {
9509 pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER);
9510 }
9511 return;
9512 }
9513 }
9514}
9515
9541static pm_token_type_t
9542lex_question_mark(pm_parser_t *parser) {
9543 if (lex_state_end_p(parser)) {
9544 lex_state_set(parser, PM_LEX_STATE_BEG);
9545 return PM_TOKEN_QUESTION_MARK;
9546 }
9547
9548 /*
9549 * A literal takes its encoding from its own contents. Literals that push a
9550 * lex mode clear this in lex_mode_push_*; a character literal is lexed
9551 * inline, so it clears the encoding here.
9552 */
9553 parser->explicit_encoding = NULL;
9554
9555 if (parser->current.end >= parser->end) {
9556 pm_parser_err_current(parser, PM_ERR_INCOMPLETE_QUESTION_MARK);
9557 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9558 return PM_TOKEN_CHARACTER_LITERAL;
9559 }
9560
9561 if (pm_char_is_whitespace(*parser->current.end)) {
9562 lex_state_set(parser, PM_LEX_STATE_BEG);
9563 return PM_TOKEN_QUESTION_MARK;
9564 }
9565
9566 lex_state_set(parser, PM_LEX_STATE_BEG);
9567
9568 if (match(parser, '\\')) {
9569 lex_state_set(parser, PM_LEX_STATE_END);
9570
9571 pm_buffer_t buffer;
9572 pm_buffer_init(&buffer, 3);
9573
9574 escape_read(parser, &buffer, NULL, PM_ESCAPE_FLAG_SINGLE);
9575
9576 // Copy buffer data into the arena and free the heap buffer.
9577 void *arena_data = pm_arena_memdup(parser->arena, buffer.value, buffer.length, PRISM_ALIGNOF(uint8_t));
9578 pm_string_constant_init(&parser->current_string, (const char *) arena_data, buffer.length);
9579 pm_buffer_cleanup(&buffer);
9580
9581 return PM_TOKEN_CHARACTER_LITERAL;
9582 } else {
9583 size_t encoding_width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9584
9585 // Ternary operators can have a ? immediately followed by an identifier
9586 // which starts with an underscore. We check for this case here.
9587 if (
9588 !(parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end) || peek(parser) == '_') ||
9589 (
9590 (parser->current.end + encoding_width >= parser->end) ||
9591 !char_is_identifier(parser, parser->current.end + encoding_width, parser->end - (parser->current.end + encoding_width))
9592 )
9593 ) {
9594 lex_state_set(parser, PM_LEX_STATE_END);
9595 parser->current.end += encoding_width;
9596 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9597 return PM_TOKEN_CHARACTER_LITERAL;
9598 }
9599 }
9600
9601 return PM_TOKEN_QUESTION_MARK;
9602}
9603
9608static pm_token_type_t
9609lex_at_variable(pm_parser_t *parser) {
9610 pm_token_type_t type = match(parser, '@') ? PM_TOKEN_CLASS_VARIABLE : PM_TOKEN_INSTANCE_VARIABLE;
9611 const uint8_t *end = parser->end;
9612
9613 size_t width;
9614 if ((width = char_is_identifier_start(parser, parser->current.end, end - parser->current.end)) > 0) {
9615 parser->current.end += width;
9616
9617 while ((width = char_is_identifier(parser, parser->current.end, end - parser->current.end)) > 0) {
9618 parser->current.end += width;
9619 }
9620 } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) {
9621 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
9622 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) {
9623 diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3;
9624 }
9625
9626 size_t width = parser->encoding->char_width(parser->current.end, end - parser->current.end);
9627 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start);
9628 } else {
9629 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_CLASS_VARIABLE_BARE : PM_ERR_INSTANCE_VARIABLE_BARE;
9630 pm_parser_err_token(parser, &parser->current, diag_id);
9631 }
9632
9633 // If we're lexing an embedded variable, then we need to pop back into the
9634 // parent lex context.
9635 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
9636 lex_mode_pop(parser);
9637 }
9638
9639 return type;
9640}
9641
9645static PRISM_INLINE void
9646parser_lex_callback(pm_parser_t *parser) {
9647 if (parser->lex_callback.callback) {
9648 parser->lex_callback.callback(parser, &parser->current, parser->lex_callback.data);
9649 }
9650}
9651
9656parser_comment(pm_parser_t *parser, pm_comment_type_t type) {
9657 pm_comment_t *comment = (pm_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_comment_t), PRISM_ALIGNOF(pm_comment_t));
9658
9659 *comment = (pm_comment_t) {
9660 .type = type,
9661 .location = TOK2LOC(parser, &parser->current)
9662 };
9663
9664 return comment;
9665}
9666
9672static pm_token_type_t
9673lex_embdoc(pm_parser_t *parser) {
9674 // First, lex out the EMBDOC_BEGIN token.
9675 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9676
9677 if (newline == NULL) {
9678 parser->current.end = parser->end;
9679 } else {
9680 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9681 parser->current.end = newline + 1;
9682 }
9683
9684 parser->current.type = PM_TOKEN_EMBDOC_BEGIN;
9685 parser_lex_callback(parser);
9686
9687 // Now, create a comment that is going to be attached to the parser.
9688 const uint8_t *comment_start = parser->current.start;
9689 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC);
9690
9691 // Now, loop until we find the end of the embedded documentation or the end
9692 // of the file.
9693 while (parser->current.end + 4 <= parser->end) {
9694 parser->current.start = parser->current.end;
9695
9696 // If we've hit the end of the embedded documentation then we'll return
9697 // that token here.
9698 if (
9699 (memcmp(parser->current.end, "=end", 4) == 0) &&
9700 (
9701 (parser->current.end + 4 == parser->end) || // end of file
9702 pm_char_is_whitespace(parser->current.end[4]) || // whitespace
9703 (parser->current.end[4] == '\0') || // NUL or end of script
9704 (parser->current.end[4] == '\004') || // ^D
9705 (parser->current.end[4] == '\032') // ^Z
9706 )
9707 ) {
9708 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9709
9710 if (newline == NULL) {
9711 parser->current.end = parser->end;
9712 } else {
9713 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9714 parser->current.end = newline + 1;
9715 }
9716
9717 parser->current.type = PM_TOKEN_EMBDOC_END;
9718 parser_lex_callback(parser);
9719
9720 comment->location.length = (uint32_t) (parser->current.end - comment_start);
9721 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9722
9723 return PM_TOKEN_EMBDOC_END;
9724 }
9725
9726 // Otherwise, we'll parse until the end of the line and return a line of
9727 // embedded documentation.
9728 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9729
9730 if (newline == NULL) {
9731 parser->current.end = parser->end;
9732 } else {
9733 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9734 parser->current.end = newline + 1;
9735 }
9736
9737 parser->current.type = PM_TOKEN_EMBDOC_LINE;
9738 parser_lex_callback(parser);
9739 }
9740
9741 pm_parser_err_current(parser, PM_ERR_EMBDOC_TERM);
9742
9743 comment->location.length = (uint32_t) (parser->current.end - comment_start);
9744 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9745
9746 return PM_TOKEN_EOF;
9747}
9748
9754static PRISM_INLINE void
9755parser_lex_ignored_newline(pm_parser_t *parser) {
9756 parser->current.type = PM_TOKEN_IGNORED_NEWLINE;
9757 parser_lex_callback(parser);
9758}
9759
9769static PRISM_INLINE void
9770parser_flush_heredoc_end(pm_parser_t *parser) {
9771 assert(parser->heredoc_end <= parser->end);
9772 parser->next_start = parser->heredoc_end;
9773 parser->heredoc_end = NULL;
9774}
9775
9779static bool
9780parser_end_of_line_p(const pm_parser_t *parser) {
9781 const uint8_t *cursor = parser->current.end;
9782
9783 while (cursor < parser->end && *cursor != '\n' && *cursor != '#') {
9784 if (!pm_char_is_inline_whitespace(*cursor++)) return false;
9785 }
9786
9787 return true;
9788}
9789
9808typedef struct {
9814
9819 const uint8_t *cursor;
9821
9841
9845static PRISM_INLINE void
9846pm_token_buffer_push_byte(pm_token_buffer_t *token_buffer, uint8_t byte) {
9847 pm_buffer_append_byte(&token_buffer->buffer, byte);
9848}
9849
9850static PRISM_INLINE void
9851pm_regexp_token_buffer_push_byte(pm_regexp_token_buffer_t *token_buffer, uint8_t byte) {
9852 pm_buffer_append_byte(&token_buffer->regexp_buffer, byte);
9853}
9854
9858static PRISM_INLINE size_t
9859parser_char_width(const pm_parser_t *parser) {
9860 size_t width;
9861 if (parser->encoding_changed) {
9862 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9863 } else {
9864 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9865 }
9866
9867 // TODO: If the character is invalid in the given encoding, then we'll just
9868 // push one byte into the buffer. This should actually be an error.
9869 return (width == 0 ? 1 : width);
9870}
9871
9875static void
9876pm_token_buffer_push_escaped(pm_token_buffer_t *token_buffer, pm_parser_t *parser) {
9877 size_t width = parser_char_width(parser);
9878 pm_buffer_append_bytes(&token_buffer->buffer, parser->current.end, width);
9879 parser->current.end += width;
9880}
9881
9882static void
9883pm_regexp_token_buffer_push_escaped(pm_regexp_token_buffer_t *token_buffer, pm_parser_t *parser) {
9884 size_t width = parser_char_width(parser);
9885 const uint8_t *start = parser->current.end;
9886 pm_buffer_append_bytes(&token_buffer->base.buffer, start, width);
9887 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, width);
9888 parser->current.end += width;
9889}
9890
9897static PRISM_INLINE void
9898pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9899 // Copy buffer data into the arena and free the heap buffer.
9900 size_t len = pm_buffer_length(&token_buffer->buffer);
9901 void *arena_data = pm_arena_memdup(parser->arena, pm_buffer_value(&token_buffer->buffer), len, PRISM_ALIGNOF(uint8_t));
9902 pm_string_constant_init(&parser->current_string, (const char *) arena_data, len);
9903 pm_buffer_cleanup(&token_buffer->buffer);
9904}
9905
9906static PRISM_INLINE void
9907pm_regexp_token_buffer_copy(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9908 pm_token_buffer_copy(parser, &token_buffer->base);
9909 pm_buffer_cleanup(&token_buffer->regexp_buffer);
9910}
9911
9921static void
9922pm_token_buffer_flush(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9923 if (token_buffer->cursor == NULL) {
9924 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9925 } else {
9926 pm_buffer_append_bytes(&token_buffer->buffer, token_buffer->cursor, (size_t) (parser->current.end - token_buffer->cursor));
9927 pm_token_buffer_copy(parser, token_buffer);
9928 }
9929}
9930
9931static void
9932pm_regexp_token_buffer_flush(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9933 if (token_buffer->base.cursor == NULL) {
9934 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9935 } else {
9936 const uint8_t *cursor = token_buffer->base.cursor;
9937 size_t length = (size_t) (parser->current.end - cursor);
9938 pm_buffer_append_bytes(&token_buffer->base.buffer, cursor, length);
9939 pm_buffer_append_bytes(&token_buffer->regexp_buffer, cursor, length);
9940 pm_regexp_token_buffer_copy(parser, token_buffer);
9941 }
9942}
9943
9944#define PM_TOKEN_BUFFER_DEFAULT_SIZE 16
9945
9954static void
9955pm_token_buffer_escape(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9956 const uint8_t *start;
9957 if (token_buffer->cursor == NULL) {
9958 pm_buffer_init(&token_buffer->buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9959 start = parser->current.start;
9960 } else {
9961 start = token_buffer->cursor;
9962 }
9963
9964 const uint8_t *end = parser->current.end - 1;
9965 assert(end >= start);
9966 pm_buffer_append_bytes(&token_buffer->buffer, start, (size_t) (end - start));
9967
9968 token_buffer->cursor = end;
9969}
9970
9971static void
9972pm_regexp_token_buffer_escape(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9973 const uint8_t *start;
9974 if (token_buffer->base.cursor == NULL) {
9975 pm_buffer_init(&token_buffer->base.buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9976 pm_buffer_init(&token_buffer->regexp_buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9977 start = parser->current.start;
9978 } else {
9979 start = token_buffer->base.cursor;
9980 }
9981
9982 const uint8_t *end = parser->current.end - 1;
9983 pm_buffer_append_bytes(&token_buffer->base.buffer, start, (size_t) (end - start));
9984 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, (size_t) (end - start));
9985
9986 token_buffer->base.cursor = end;
9987}
9988
9989#undef PM_TOKEN_BUFFER_DEFAULT_SIZE
9990
9995static PRISM_INLINE size_t
9996pm_heredoc_strspn_inline_whitespace(pm_parser_t *parser, const uint8_t **cursor, pm_heredoc_indent_t indent) {
9997 size_t whitespace = 0;
9998
9999 switch (indent) {
10000 case PM_HEREDOC_INDENT_NONE:
10001 // Do nothing, we can't match a terminator with
10002 // indentation and there's no need to calculate common
10003 // whitespace.
10004 break;
10005 case PM_HEREDOC_INDENT_DASH:
10006 // Skip past inline whitespace.
10007 *cursor += pm_strspn_inline_whitespace(*cursor, parser->end - *cursor);
10008 break;
10009 case PM_HEREDOC_INDENT_TILDE:
10010 // Skip past inline whitespace and calculate common
10011 // whitespace.
10012 while (*cursor < parser->end && pm_char_is_inline_whitespace(**cursor)) {
10013 if (**cursor == '\t') {
10014 whitespace = (whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
10015 } else {
10016 whitespace++;
10017 }
10018 (*cursor)++;
10019 }
10020
10021 break;
10022 }
10023
10024 return whitespace;
10025}
10026
10031static uint8_t
10032pm_lex_percent_delimiter(pm_parser_t *parser) {
10033 size_t eol_length = match_eol(parser);
10034
10035 if (eol_length) {
10036 if (parser->heredoc_end) {
10037 // If we have already lexed a heredoc, then the newline has already
10038 // been added to the list. In this case we want to just flush the
10039 // heredoc end.
10040 parser_flush_heredoc_end(parser);
10041 } else {
10042 // Otherwise, we'll add the newline to the list of newlines.
10043 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + U32(eol_length));
10044 }
10045
10046 uint8_t delimiter = *parser->current.end;
10047
10048 // If our delimiter is \r\n, we want to treat it as if it's \n.
10049 // For example, %\r\nfoo\r\n should be "foo"
10050 if (eol_length == 2) {
10051 delimiter = *(parser->current.end + 1);
10052 }
10053
10054 parser->current.end += eol_length;
10055 return delimiter;
10056 }
10057
10058 return *parser->current.end++;
10059}
10060
10065#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return
10066
10073static void
10074parser_lex(pm_parser_t *parser) {
10075 assert(parser->current.end <= parser->end);
10076 parser->previous = parser->current;
10077
10078 // This value mirrors cmd_state from CRuby.
10079 bool previous_command_start = parser->command_start;
10080 parser->command_start = false;
10081
10082 // This is used to communicate to the newline lexing function that we've
10083 // already seen a comment.
10084 bool lexed_comment = false;
10085
10086 // Here we cache the current value of the semantic token seen flag. This is
10087 // used to reset it in case we find a token that shouldn't flip this flag.
10088 unsigned int semantic_token_seen = parser->semantic_token_seen;
10089 parser->semantic_token_seen = true;
10090
10091 // We'll jump to this label when we are about to encounter an EOF.
10092 // If we still have lex_modes on the stack, we pop them so that cleanup
10093 // can happen. For example, we should still continue parsing after a heredoc
10094 // identifier, even if the heredoc body was syntax invalid.
10095 switch_lex_modes:
10096
10097 switch (parser->lex_modes.current->mode) {
10098 case PM_LEX_DEFAULT:
10099 case PM_LEX_EMBEXPR:
10100 case PM_LEX_EMBVAR:
10101
10102 // We have a specific named label here because we are going to jump back to
10103 // this location in the event that we have lexed a token that should not be
10104 // returned to the parser. This includes comments, ignored newlines, and
10105 // invalid tokens of some form.
10106 lex_next_token: {
10107 // If we have the special next_start pointer set, then we're going to jump
10108 // to that location and start lexing from there.
10109 if (parser->next_start != NULL) {
10110 parser->current.end = parser->next_start;
10111 parser->next_start = NULL;
10112 }
10113
10114 // This value mirrors space_seen from CRuby. It tracks whether or not
10115 // space has been eaten before the start of the next token.
10116 bool space_seen = false;
10117
10118 // First, we're going to skip past any whitespace at the front of the next
10119 // token. Skip runs of inline whitespace in bulk to avoid per-character
10120 // stores back to parser->current.end.
10121 bool chomping = true;
10122 while (chomping) {
10123 /* Skip the run of inline whitespace in bulk, then decide what
10124 * to do based on the first byte after it. Handling both in a
10125 * single pass avoids re-entering the scan when the run was
10126 * non-empty, which is the common case. */
10127 {
10128 static const uint8_t inline_whitespace[256] = {
10129 [' '] = 1, ['\t'] = 1, ['\f'] = 1, ['\v'] = 1
10130 };
10131 const uint8_t *scan = parser->current.end;
10132 while (scan < parser->end && inline_whitespace[*scan]) scan++;
10133 if (scan > parser->current.end) {
10134 parser->current.end = scan;
10135 space_seen = true;
10136 }
10137 if (scan >= parser->end) break;
10138 }
10139
10140 switch (*parser->current.end) {
10141 case '\r':
10142 if (match_eol_offset(parser, 1)) {
10143 chomping = false;
10144 } else {
10145 pm_parser_warn(parser, PM_TOKEN_END(parser, &parser->current), 1, PM_WARN_UNEXPECTED_CARRIAGE_RETURN);
10146 parser->current.end++;
10147 space_seen = true;
10148 }
10149 break;
10150 case '\\': {
10151 size_t eol_length = match_eol_offset(parser, 1);
10152 if (eol_length) {
10153 if (parser->heredoc_end) {
10154 parser->current.end = parser->heredoc_end;
10155 parser->heredoc_end = NULL;
10156 } else {
10157 parser->current.end += eol_length + 1;
10158 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10159 space_seen = true;
10160 }
10161 } else if (pm_char_is_inline_whitespace(*parser->current.end)) {
10162 parser->current.end += 2;
10163 } else {
10164 chomping = false;
10165 }
10166
10167 break;
10168 }
10169 default:
10170 chomping = false;
10171 break;
10172 }
10173 }
10174
10175 // Next, we'll set to start of this token to be the current end.
10176 parser->current.start = parser->current.end;
10177
10178 // We'll check if we're at the end of the file. If we are, then we
10179 // need to return the EOF token.
10180 if (parser->current.end >= parser->end) {
10181 // We may be missing closing tokens. We should pop modes one by one
10182 // to do the appropriate cleanup like moving next_start for heredocs.
10183 // Only when no mode is remaining will we actually emit the EOF token.
10184 if (parser->lex_modes.current->mode != PM_LEX_DEFAULT) {
10185 lex_mode_pop(parser);
10186 goto switch_lex_modes;
10187 }
10188
10189 // If we hit EOF, but the EOF came immediately after a newline,
10190 // set the start of the token to the newline. This way any EOF
10191 // errors will be reported as happening on that line rather than
10192 // a line after. For example "foo(\n" should report an error
10193 // on line 1 even though EOF technically occurs on line 2.
10194 if (parser->current.start > parser->start && (*(parser->current.start - 1) == '\n')) {
10195 parser->current.start -= 1;
10196 }
10197 LEX(PM_TOKEN_EOF);
10198 }
10199
10200 // Finally, we'll check the current character to determine the next
10201 // token.
10202 switch (*parser->current.end++) {
10203 case '\0': // NUL or end of script
10204 case '\004': // ^D
10205 case '\032': // ^Z
10206 parser->current.end--;
10207 LEX(PM_TOKEN_EOF);
10208
10209 case '#': { // comments
10210 const uint8_t *ending = next_newline(parser->current.end, parser->end - parser->current.end);
10211 parser->current.end = ending == NULL ? parser->end : ending;
10212
10213 // If we found a comment while lexing, then we're going to
10214 // add it to the list of comments in the file and keep
10215 // lexing.
10216 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE);
10217 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
10218
10219 parser->current.type = PM_TOKEN_COMMENT;
10220 parser_lex_callback(parser);
10221
10222 // Here, parse the comment to see if it's a magic comment
10223 // and potentially change state on the parser.
10224 if (!parser_lex_magic_comment(parser, semantic_token_seen) && (parser->current.start == parser->encoding_comment_start)) {
10225 ptrdiff_t length = parser->current.end - parser->current.start;
10226
10227 // If we didn't find a magic comment within the first
10228 // pass and we're at the start of the file, then we need
10229 // to do another pass to potentially find other patterns
10230 // for encoding comments.
10231 if (length >= 10 && !parser->encoding_locked) {
10232 parser_lex_magic_comment_encoding(parser);
10233 }
10234 }
10235
10236 /* The comment does not include its terminating newline,
10237 * which lexes through the newline handling below as its
10238 * own token. A comment that ends the file has no newline,
10239 * so the newline handling runs without one to emit. */
10240 if (ending == NULL) {
10241 lexed_comment = true;
10242 } else {
10243 parser->current.start = ending;
10244 parser->current.end = ending + 1;
10245 }
10246 }
10248 case '\r':
10249 case '\n': {
10250 parser->semantic_token_seen = semantic_token_seen & 0x1;
10251 size_t eol_length = match_eol_at(parser, parser->current.end - 1);
10252
10253 if (eol_length) {
10254 // The only way you can have carriage returns in this
10255 // particular loop is if you have a carriage return
10256 // followed by a newline. In that case we'll just skip
10257 // over the carriage return and continue lexing, in
10258 // order to make it so that the newline token
10259 // encapsulates both the carriage return and the
10260 // newline. Note that we need to check that we haven't
10261 // already lexed a comment here because that falls
10262 // through into here as well.
10263 if (!lexed_comment) {
10264 parser->current.end += eol_length - 1; // skip CR
10265 }
10266
10267 if (parser->heredoc_end == NULL) {
10268 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10269 }
10270 }
10271
10272 if (parser->heredoc_end) {
10273 parser_flush_heredoc_end(parser);
10274 }
10275
10276 // If this is an ignored newline, then we can continue lexing after
10277 // calling the callback with the ignored newline token.
10278 switch (lex_state_ignored_p(parser)) {
10279 case PM_IGNORED_NEWLINE_NONE:
10280 break;
10281 case PM_IGNORED_NEWLINE_PATTERN:
10282 if (parser->pattern_matching_newlines || parser->in_keyword_arg) {
10283 if (!lexed_comment) {
10284 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10285 parser_lex_callback(parser);
10286 }
10287
10288 lex_state_set(parser, PM_LEX_STATE_BEG);
10289 parser->command_start = true;
10290 parser->current.type = PM_TOKEN_NEWLINE;
10291 return;
10292 }
10294 case PM_IGNORED_NEWLINE_ALL:
10295 if (!lexed_comment) parser_lex_ignored_newline(parser);
10296 lexed_comment = false;
10297 goto lex_next_token;
10298 }
10299
10300 // Here we need to look ahead and see if there is a call operator
10301 // (either . or &.) that starts the next line. If there is, then this
10302 // is going to become an ignored newline and we're going to instead
10303 // return the call operator.
10304 const uint8_t *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start;
10305 next_content += pm_strspn_inline_whitespace(next_content, parser->end - next_content);
10306
10307 if (next_content < parser->end) {
10308 // If we hit a comment after a newline, then we're going to check
10309 // if it's ignored or if it's followed by a method call ('.').
10310 // If it is, then we're going to call the
10311 // callback with an ignored newline and then continue lexing.
10312 // Otherwise we'll return a regular newline.
10313 if (next_content[0] == '#') {
10314 // Here we look for a "." or "&." following a "\n".
10315 const uint8_t *following = next_newline(next_content, parser->end - next_content);
10316
10317 while (following && (following + 1 < parser->end)) {
10318 following++;
10319 following += pm_strspn_inline_whitespace(following, parser->end - following);
10320
10321 // If this is not followed by a comment, then we can break out
10322 // of this loop.
10323 if (peek_at(parser, following) != '#') break;
10324
10325 // If there is a comment, then we need to find the end of the
10326 // comment and continue searching from there.
10327 following = next_newline(following, parser->end - following);
10328 }
10329
10330 // If the lex state was ignored, we will lex the
10331 // ignored newline.
10332 if (lex_state_ignored_p(parser)) {
10333 if (!lexed_comment) parser_lex_ignored_newline(parser);
10334 lexed_comment = false;
10335 goto lex_next_token;
10336 }
10337
10338 // If we hit a '.' or a '&.' we will lex the ignored
10339 // newline.
10340 if (following && (
10341 (peek_at(parser, following) == '.') ||
10342 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10343 )) {
10344 if (!lexed_comment) parser_lex_ignored_newline(parser);
10345 lexed_comment = false;
10346 goto lex_next_token;
10347 }
10348
10349
10350 // If we are parsing as CRuby 4.0 or later and we
10351 // hit a '&&' or a '||' then we will lex the ignored
10352 // newline.
10353 if (
10354 (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) &&
10355 following && (
10356 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
10357 (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
10358 (
10359 peek_at(parser, following) == 'a' &&
10360 peek_at(parser, following + 1) == 'n' &&
10361 peek_at(parser, following + 2) == 'd' &&
10362 peek_at(parser, next_content + 3) != '!' &&
10363 peek_at(parser, next_content + 3) != '?' &&
10364 !char_is_identifier(parser, following + 3, parser->end - (following + 3))
10365 ) ||
10366 (
10367 peek_at(parser, following) == 'o' &&
10368 peek_at(parser, following + 1) == 'r' &&
10369 peek_at(parser, next_content + 2) != '!' &&
10370 peek_at(parser, next_content + 2) != '?' &&
10371 !char_is_identifier(parser, following + 2, parser->end - (following + 2))
10372 )
10373 )
10374 ) {
10375 if (!lexed_comment) parser_lex_ignored_newline(parser);
10376 lexed_comment = false;
10377 goto lex_next_token;
10378 }
10379 }
10380
10381 // If we hit a . after a newline, then we're in a call chain and
10382 // we need to return the call operator.
10383 if (next_content[0] == '.') {
10384 /* A beginless range on the next line means this
10385 * newline terminates the statement rather than
10386 * continuing a method chain. */
10387 if (peek_at(parser, next_content + 1) == '.') {
10388 if (!lexed_comment) {
10389 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10390 parser_lex_callback(parser);
10391 }
10392
10393 lex_state_set(parser, PM_LEX_STATE_BEG);
10394 parser->command_start = true;
10395 parser->current.type = PM_TOKEN_NEWLINE;
10396 return;
10397 }
10398
10399 if (!lexed_comment) parser_lex_ignored_newline(parser);
10400 lex_state_set(parser, PM_LEX_STATE_DOT);
10401 parser->current.start = next_content;
10402 parser->current.end = next_content + 1;
10403 parser->next_start = NULL;
10404 LEX(PM_TOKEN_DOT);
10405 }
10406
10407 // If we hit a &. after a newline, then we're in a call chain and
10408 // we need to return the call operator.
10409 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '.') {
10410 if (!lexed_comment) parser_lex_ignored_newline(parser);
10411 lex_state_set(parser, PM_LEX_STATE_DOT);
10412 parser->current.start = next_content;
10413 parser->current.end = next_content + 2;
10414 parser->next_start = NULL;
10415 LEX(PM_TOKEN_AMPERSAND_DOT);
10416 }
10417
10418 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
10419 // If we hit an && then we are in a logical chain
10420 // and we need to return the logical operator.
10421 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
10422 if (!lexed_comment) parser_lex_ignored_newline(parser);
10423 lex_state_set(parser, PM_LEX_STATE_BEG);
10424 parser->current.start = next_content;
10425 parser->current.end = next_content + 2;
10426 parser->next_start = NULL;
10427 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10428 }
10429
10430 // If we hit a || then we are in a logical chain and
10431 // we need to return the logical operator.
10432 if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
10433 if (!lexed_comment) parser_lex_ignored_newline(parser);
10434 lex_state_set(parser, PM_LEX_STATE_BEG);
10435 parser->current.start = next_content;
10436 parser->current.end = next_content + 2;
10437 parser->next_start = NULL;
10438 LEX(PM_TOKEN_PIPE_PIPE);
10439 }
10440
10441 // If we hit an 'and' then we are in a logical chain
10442 // and we need to return the logical operator.
10443 if (
10444 peek_at(parser, next_content) == 'a' &&
10445 peek_at(parser, next_content + 1) == 'n' &&
10446 peek_at(parser, next_content + 2) == 'd' &&
10447 peek_at(parser, next_content + 3) != '!' &&
10448 peek_at(parser, next_content + 3) != '?' &&
10449 !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
10450 ) {
10451 if (!lexed_comment) parser_lex_ignored_newline(parser);
10452 lex_state_set(parser, PM_LEX_STATE_BEG);
10453 parser->current.start = next_content;
10454 parser->current.end = next_content + 3;
10455 parser->next_start = NULL;
10456 parser->command_start = true;
10457 LEX(PM_TOKEN_KEYWORD_AND);
10458 }
10459
10460 // If we hit a 'or' then we are in a logical chain
10461 // and we need to return the logical operator.
10462 if (
10463 peek_at(parser, next_content) == 'o' &&
10464 peek_at(parser, next_content + 1) == 'r' &&
10465 peek_at(parser, next_content + 2) != '!' &&
10466 peek_at(parser, next_content + 2) != '?' &&
10467 !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
10468 ) {
10469 if (!lexed_comment) parser_lex_ignored_newline(parser);
10470 lex_state_set(parser, PM_LEX_STATE_BEG);
10471 parser->current.start = next_content;
10472 parser->current.end = next_content + 2;
10473 parser->next_start = NULL;
10474 parser->command_start = true;
10475 LEX(PM_TOKEN_KEYWORD_OR);
10476 }
10477 }
10478 }
10479
10480 // At this point we know this is a regular newline, and we can set the
10481 // necessary state and return the token.
10482 lex_state_set(parser, PM_LEX_STATE_BEG);
10483 parser->command_start = true;
10484 parser->current.type = PM_TOKEN_NEWLINE;
10485 if (!lexed_comment) parser_lex_callback(parser);
10486 return;
10487 }
10488
10489 // ,
10490 case ',':
10491 if ((parser->previous.type == PM_TOKEN_COMMA) && (parser->enclosure_nesting > 0)) {
10492 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
10493 }
10494
10495 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10496 LEX(PM_TOKEN_COMMA);
10497
10498 // (
10499 case '(': {
10500 /* A parenthesis scanned at the beginning of an expression
10501 * groups the expression it wraps, while one scanned in
10502 * argument position with a preceding space wraps a command
10503 * argument. Everything else opens an argument list. */
10504 pm_token_type_t type = PM_TOKEN_PARENTHESIS_LEFT;
10505
10506 if (lex_state_beg_p(parser)) {
10507 type = PM_TOKEN_PARENTHESIS_LEFT_GROUPING;
10508 } else if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (PM_LEX_STATE_END | PM_LEX_STATE_LABEL))) {
10509 type = PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES;
10510 }
10511
10512 parser->enclosure_nesting++;
10513 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10514 pm_enclosure_frame_push(parser);
10515 LEX(type);
10516 }
10517
10518 // )
10519 case ')':
10520 parser->enclosure_nesting--;
10521 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10522 pm_enclosure_frame_pop(parser);
10523 LEX(PM_TOKEN_PARENTHESIS_RIGHT);
10524
10525 // ;
10526 case ';':
10527 lex_state_set(parser, PM_LEX_STATE_BEG);
10528 parser->command_start = true;
10529 LEX(PM_TOKEN_SEMICOLON);
10530
10531 // [ [] []=
10532 case '[':
10533 parser->enclosure_nesting++;
10534 pm_token_type_t type = PM_TOKEN_BRACKET_LEFT;
10535
10536 if (lex_state_operator_p(parser)) {
10537 if (match(parser, ']')) {
10538 parser->enclosure_nesting--;
10539 lex_state_set(parser, PM_LEX_STATE_ARG);
10540 LEX(match(parser, '=') ? PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : PM_TOKEN_BRACKET_LEFT_RIGHT);
10541 }
10542
10543 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABEL);
10544 LEX(type);
10545 }
10546
10547 if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, PM_LEX_STATE_LABELED)))) {
10548 type = PM_TOKEN_BRACKET_LEFT_ARRAY;
10549 }
10550
10551 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10552 pm_enclosure_frame_push(parser);
10553 LEX(type);
10554
10555 // ]
10556 case ']':
10557 parser->enclosure_nesting--;
10558 lex_state_set(parser, PM_LEX_STATE_END);
10559 pm_enclosure_frame_pop(parser);
10560 LEX(PM_TOKEN_BRACKET_RIGHT);
10561
10562 // {
10563 case '{': {
10564 pm_token_type_t type = PM_TOKEN_BRACE_LEFT;
10565
10566 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
10567 /* This { begins a lambda */
10568 parser->command_start = true;
10569 lex_state_set(parser, PM_LEX_STATE_BEG);
10570 type = PM_TOKEN_LAMBDA_BEGIN;
10571 } else if (lex_state_p(parser, PM_LEX_STATE_LABELED)) {
10572 /* This { begins a hash literal */
10573 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10574 type = PM_TOKEN_BRACE_LEFT_HASH;
10575 } else if (lex_state_p(parser, PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_END | PM_LEX_STATE_ENDFN)) {
10576 /* This { begins a block */
10577 parser->command_start = true;
10578 lex_state_set(parser, PM_LEX_STATE_BEG);
10579 } else if (lex_state_p(parser, PM_LEX_STATE_ENDARG)) {
10580 /* This { begins a block following a parenthesized
10581 * command argument */
10582 parser->command_start = true;
10583 lex_state_set(parser, PM_LEX_STATE_BEG);
10584 type = PM_TOKEN_BRACE_LEFT_ARGUMENT;
10585 } else {
10586 /* This { begins a hash literal */
10587 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10588 type = PM_TOKEN_BRACE_LEFT_HASH;
10589 }
10590
10591 parser->enclosure_nesting++;
10592 parser->brace_nesting++;
10593 pm_enclosure_frame_push(parser);
10594
10595 LEX(type);
10596 }
10597
10598 // }
10599 case '}':
10600 parser->enclosure_nesting--;
10601 pm_enclosure_frame_pop(parser);
10602
10603 if ((parser->lex_modes.current->mode == PM_LEX_EMBEXPR) && (parser->brace_nesting == 0)) {
10604 lex_mode_pop(parser);
10605 LEX(PM_TOKEN_EMBEXPR_END);
10606 }
10607
10608 parser->brace_nesting--;
10609 lex_state_set(parser, PM_LEX_STATE_END);
10610 LEX(PM_TOKEN_BRACE_RIGHT);
10611
10612 // * ** **= *=
10613 case '*': {
10614 if (match(parser, '*')) {
10615 if (match(parser, '=')) {
10616 lex_state_set(parser, PM_LEX_STATE_BEG);
10617 LEX(PM_TOKEN_STAR_STAR_EQUAL);
10618 }
10619
10620 pm_token_type_t type = PM_TOKEN_STAR_STAR;
10621
10622 if (lex_state_spcarg_p(parser, space_seen)) {
10623 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR);
10624 type = PM_TOKEN_USTAR_STAR;
10625 } else if (lex_state_beg_p(parser)) {
10626 type = PM_TOKEN_USTAR_STAR;
10627 } else if (ambiguous_operator_p(parser, space_seen)) {
10628 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "**", "argument prefix");
10629 }
10630
10631 if (lex_state_operator_p(parser)) {
10632 lex_state_set(parser, PM_LEX_STATE_ARG);
10633 } else {
10634 lex_state_set(parser, PM_LEX_STATE_BEG);
10635 }
10636
10637 LEX(type);
10638 }
10639
10640 if (match(parser, '=')) {
10641 lex_state_set(parser, PM_LEX_STATE_BEG);
10642 LEX(PM_TOKEN_STAR_EQUAL);
10643 }
10644
10645 pm_token_type_t type = PM_TOKEN_STAR;
10646
10647 if (lex_state_spcarg_p(parser, space_seen)) {
10648 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR);
10649 type = PM_TOKEN_USTAR;
10650 } else if (lex_state_beg_p(parser)) {
10651 type = PM_TOKEN_USTAR;
10652 } else if (ambiguous_operator_p(parser, space_seen)) {
10653 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "*", "argument prefix");
10654 }
10655
10656 if (lex_state_operator_p(parser)) {
10657 lex_state_set(parser, PM_LEX_STATE_ARG);
10658 } else {
10659 lex_state_set(parser, PM_LEX_STATE_BEG);
10660 }
10661
10662 LEX(type);
10663 }
10664
10665 // ! != !~ !@
10666 case '!':
10667 if (lex_state_operator_p(parser)) {
10668 lex_state_set(parser, PM_LEX_STATE_ARG);
10669 if (match(parser, '@')) {
10670 LEX(PM_TOKEN_BANG);
10671 }
10672 } else {
10673 lex_state_set(parser, PM_LEX_STATE_BEG);
10674 }
10675
10676 if (match(parser, '=')) {
10677 LEX(PM_TOKEN_BANG_EQUAL);
10678 }
10679
10680 if (match(parser, '~')) {
10681 LEX(PM_TOKEN_BANG_TILDE);
10682 }
10683
10684 LEX(PM_TOKEN_BANG);
10685
10686 // = => =~ == === =begin
10687 case '=':
10688 if (
10689 current_token_starts_line(parser) &&
10690 (parser->current.end + 5 <= parser->end) &&
10691 memcmp(parser->current.end, "begin", 5) == 0 &&
10692 (pm_char_is_whitespace(peek_offset(parser, 5)) || (peek_offset(parser, 5) == '\0'))
10693 ) {
10694 pm_token_type_t type = lex_embdoc(parser);
10695 if (type == PM_TOKEN_EOF) {
10696 LEX(type);
10697 }
10698
10699 goto lex_next_token;
10700 }
10701
10702 if (lex_state_operator_p(parser)) {
10703 lex_state_set(parser, PM_LEX_STATE_ARG);
10704 } else {
10705 lex_state_set(parser, PM_LEX_STATE_BEG);
10706 }
10707
10708 if (match(parser, '>')) {
10709 LEX(PM_TOKEN_EQUAL_GREATER);
10710 }
10711
10712 if (match(parser, '~')) {
10713 LEX(PM_TOKEN_EQUAL_TILDE);
10714 }
10715
10716 if (match(parser, '=')) {
10717 LEX(match(parser, '=') ? PM_TOKEN_EQUAL_EQUAL_EQUAL : PM_TOKEN_EQUAL_EQUAL);
10718 }
10719
10720 LEX(PM_TOKEN_EQUAL);
10721
10722 // < << <<= <= <=>
10723 case '<':
10724 if (match(parser, '<')) {
10725 if (
10726 !lex_state_p(parser, PM_LEX_STATE_DOT | PM_LEX_STATE_CLASS) &&
10727 !lex_state_end_p(parser) &&
10728 (!lex_state_p(parser, PM_LEX_STATE_ARG_ANY) || lex_state_p(parser, PM_LEX_STATE_LABELED) || space_seen)
10729 ) {
10730 const uint8_t *end = parser->current.end;
10731
10732 pm_heredoc_quote_t quote = PM_HEREDOC_QUOTE_NONE;
10733 pm_heredoc_indent_t indent = PM_HEREDOC_INDENT_NONE;
10734
10735 if (match(parser, '-')) {
10736 indent = PM_HEREDOC_INDENT_DASH;
10737 }
10738 else if (match(parser, '~')) {
10739 indent = PM_HEREDOC_INDENT_TILDE;
10740 }
10741
10742 if (match(parser, '`')) {
10743 quote = PM_HEREDOC_QUOTE_BACKTICK;
10744 }
10745 else if (match(parser, '"')) {
10746 quote = PM_HEREDOC_QUOTE_DOUBLE;
10747 }
10748 else if (match(parser, '\'')) {
10749 quote = PM_HEREDOC_QUOTE_SINGLE;
10750 }
10751
10752 const uint8_t *ident_start = parser->current.end;
10753 size_t width = 0;
10754
10755 if (parser->current.end >= parser->end) {
10756 parser->current.end = end;
10757 } else if (quote == PM_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) == 0) {
10758 parser->current.end = end;
10759 } else {
10760 if (quote == PM_HEREDOC_QUOTE_NONE) {
10761 parser->current.end += width;
10762
10763 while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end))) {
10764 parser->current.end += width;
10765 }
10766 } else {
10767 // If we have quotes, then we're going to go until we find the
10768 // end quote.
10769 while ((parser->current.end < parser->end) && quote != (pm_heredoc_quote_t) (*parser->current.end)) {
10770 if (*parser->current.end == '\r' || *parser->current.end == '\n') break;
10771 parser->current.end++;
10772 }
10773 }
10774
10775 size_t ident_length = (size_t) (parser->current.end - ident_start);
10776 bool ident_error = false;
10777
10778 if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) {
10779 pm_parser_err(parser, U32(ident_start - parser->start), U32(ident_length), PM_ERR_HEREDOC_IDENTIFIER);
10780 ident_error = true;
10781 }
10782
10783 parser->explicit_encoding = NULL;
10784 lex_mode_push(parser, (pm_lex_mode_t) {
10785 .mode = PM_LEX_HEREDOC,
10786 .as.heredoc = {
10787 .base = {
10788 .ident_start = ident_start,
10789 .ident_length = ident_length,
10790 .quote = quote,
10791 .indent = indent
10792 },
10793 .next_start = parser->current.end,
10794 .common_whitespace = NULL,
10795 .line_continuation = false
10796 }
10797 });
10798
10799 if (parser->heredoc_end == NULL) {
10800 const uint8_t *body_start = next_newline(parser->current.end, parser->end - parser->current.end);
10801
10802 if (body_start == NULL) {
10803 // If there is no newline after the heredoc identifier, then
10804 // this is not a valid heredoc declaration. In this case we
10805 // will add an error, but we will still return a heredoc
10806 // start.
10807 if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length);
10808 body_start = parser->end;
10809 } else {
10810 // Otherwise, we want to indicate that the body of the
10811 // heredoc starts on the character after the next newline.
10812 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(body_start - parser->start + 1));
10813 body_start++;
10814 }
10815
10816 parser->next_start = body_start;
10817 } else {
10818 parser->next_start = parser->heredoc_end;
10819 }
10820
10821 LEX(PM_TOKEN_HEREDOC_START);
10822 }
10823 }
10824
10825 if (match(parser, '=')) {
10826 lex_state_set(parser, PM_LEX_STATE_BEG);
10827 LEX(PM_TOKEN_LESS_LESS_EQUAL);
10828 }
10829
10830 if (ambiguous_operator_p(parser, space_seen)) {
10831 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "<<", "here document");
10832 }
10833
10834 if (lex_state_operator_p(parser)) {
10835 lex_state_set(parser, PM_LEX_STATE_ARG);
10836 } else {
10837 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10838 lex_state_set(parser, PM_LEX_STATE_BEG);
10839 }
10840
10841 LEX(PM_TOKEN_LESS_LESS);
10842 }
10843
10844 if (lex_state_operator_p(parser)) {
10845 lex_state_set(parser, PM_LEX_STATE_ARG);
10846 } else {
10847 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10848 lex_state_set(parser, PM_LEX_STATE_BEG);
10849 }
10850
10851 if (match(parser, '=')) {
10852 if (match(parser, '>')) {
10853 LEX(PM_TOKEN_LESS_EQUAL_GREATER);
10854 }
10855
10856 LEX(PM_TOKEN_LESS_EQUAL);
10857 }
10858
10859 LEX(PM_TOKEN_LESS);
10860
10861 // > >> >>= >=
10862 case '>':
10863 if (match(parser, '>')) {
10864 if (lex_state_operator_p(parser)) {
10865 lex_state_set(parser, PM_LEX_STATE_ARG);
10866 } else {
10867 lex_state_set(parser, PM_LEX_STATE_BEG);
10868 }
10869 LEX(match(parser, '=') ? PM_TOKEN_GREATER_GREATER_EQUAL : PM_TOKEN_GREATER_GREATER);
10870 }
10871
10872 if (lex_state_operator_p(parser)) {
10873 lex_state_set(parser, PM_LEX_STATE_ARG);
10874 } else {
10875 lex_state_set(parser, PM_LEX_STATE_BEG);
10876 }
10877
10878 LEX(match(parser, '=') ? PM_TOKEN_GREATER_EQUAL : PM_TOKEN_GREATER);
10879
10880 // double-quoted string literal
10881 case '"': {
10882 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10883 lex_mode_push_string(parser, true, label_allowed, '\0', '"');
10884 LEX(PM_TOKEN_STRING_BEGIN);
10885 }
10886
10887 // xstring literal
10888 case '`': {
10889 if (lex_state_p(parser, PM_LEX_STATE_FNAME)) {
10890 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10891 LEX(PM_TOKEN_BACKTICK);
10892 }
10893
10894 if (lex_state_p(parser, PM_LEX_STATE_DOT)) {
10895 if (previous_command_start) {
10896 lex_state_set(parser, PM_LEX_STATE_CMDARG);
10897 } else {
10898 lex_state_set(parser, PM_LEX_STATE_ARG);
10899 }
10900
10901 LEX(PM_TOKEN_BACKTICK);
10902 }
10903
10904 lex_mode_push_string(parser, true, false, '\0', '`');
10905 LEX(PM_TOKEN_XSTRING_BEGIN);
10906 }
10907
10908 // single-quoted string literal
10909 case '\'': {
10910 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10911 lex_mode_push_string(parser, false, label_allowed, '\0', '\'');
10912 LEX(PM_TOKEN_STRING_BEGIN);
10913 }
10914
10915 // ? character literal
10916 case '?':
10917 LEX(lex_question_mark(parser));
10918
10919 // & && &&= &=
10920 case '&': {
10921 if (match(parser, '&')) {
10922 lex_state_set(parser, PM_LEX_STATE_BEG);
10923
10924 if (match(parser, '=')) {
10925 LEX(PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
10926 }
10927
10928 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10929 }
10930
10931 if (match(parser, '=')) {
10932 lex_state_set(parser, PM_LEX_STATE_BEG);
10933 LEX(PM_TOKEN_AMPERSAND_EQUAL);
10934 }
10935
10936 if (match(parser, '.')) {
10937 lex_state_set(parser, PM_LEX_STATE_DOT);
10938 LEX(PM_TOKEN_AMPERSAND_DOT);
10939 }
10940
10941 pm_token_type_t type = PM_TOKEN_AMPERSAND;
10942 if (lex_state_spcarg_p(parser, space_seen)) {
10943 if ((peek(parser) != ':') || (peek_offset(parser, 1) == '\0')) {
10944 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
10945 } else {
10946 const uint8_t delim = peek_offset(parser, 1);
10947
10948 if ((delim != '\'') && (delim != '"') && !char_is_identifier(parser, parser->current.end + 1, parser->end - (parser->current.end + 1))) {
10949 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
10950 }
10951 }
10952
10953 type = PM_TOKEN_UAMPERSAND;
10954 } else if (lex_state_beg_p(parser)) {
10955 type = PM_TOKEN_UAMPERSAND;
10956 } else if (ambiguous_operator_p(parser, space_seen)) {
10957 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "&", "argument prefix");
10958 }
10959
10960 if (lex_state_operator_p(parser)) {
10961 lex_state_set(parser, PM_LEX_STATE_ARG);
10962 } else {
10963 lex_state_set(parser, PM_LEX_STATE_BEG);
10964 }
10965
10966 LEX(type);
10967 }
10968
10969 // | || ||= |=
10970 case '|':
10971 if (match(parser, '|')) {
10972 if (match(parser, '=')) {
10973 lex_state_set(parser, PM_LEX_STATE_BEG);
10974 LEX(PM_TOKEN_PIPE_PIPE_EQUAL);
10975 }
10976
10977 if (lex_state_p(parser, PM_LEX_STATE_BEG)) {
10978 parser->current.end--;
10979 LEX(PM_TOKEN_PIPE);
10980 }
10981
10982 lex_state_set(parser, PM_LEX_STATE_BEG);
10983 LEX(PM_TOKEN_PIPE_PIPE);
10984 }
10985
10986 if (match(parser, '=')) {
10987 lex_state_set(parser, PM_LEX_STATE_BEG);
10988 LEX(PM_TOKEN_PIPE_EQUAL);
10989 }
10990
10991 if (lex_state_operator_p(parser)) {
10992 lex_state_set(parser, PM_LEX_STATE_ARG);
10993 } else {
10994 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10995 }
10996
10997 LEX(PM_TOKEN_PIPE);
10998
10999 // + += +@
11000 case '+': {
11001 if (lex_state_operator_p(parser)) {
11002 lex_state_set(parser, PM_LEX_STATE_ARG);
11003
11004 if (match(parser, '@')) {
11005 LEX(PM_TOKEN_UPLUS);
11006 }
11007
11008 LEX(PM_TOKEN_PLUS);
11009 }
11010
11011 if (match(parser, '=')) {
11012 lex_state_set(parser, PM_LEX_STATE_BEG);
11013 LEX(PM_TOKEN_PLUS_EQUAL);
11014 }
11015
11016 if (
11017 lex_state_beg_p(parser) ||
11018 (lex_state_spcarg_p(parser, space_seen) ? (pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS), true) : false)
11019 ) {
11020 lex_state_set(parser, PM_LEX_STATE_BEG);
11021
11022 if (pm_char_is_decimal_digit(peek(parser))) {
11023 parser->current.end++;
11024 pm_token_type_t type = lex_numeric(parser);
11025 lex_state_set(parser, PM_LEX_STATE_END);
11026 LEX(type);
11027 }
11028
11029 LEX(PM_TOKEN_UPLUS);
11030 }
11031
11032 if (ambiguous_operator_p(parser, space_seen)) {
11033 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "+", "unary operator");
11034 }
11035
11036 lex_state_set(parser, PM_LEX_STATE_BEG);
11037 LEX(PM_TOKEN_PLUS);
11038 }
11039
11040 // - -= -@
11041 case '-': {
11042 if (lex_state_operator_p(parser)) {
11043 lex_state_set(parser, PM_LEX_STATE_ARG);
11044
11045 if (match(parser, '@')) {
11046 LEX(PM_TOKEN_UMINUS);
11047 }
11048
11049 LEX(PM_TOKEN_MINUS);
11050 }
11051
11052 if (match(parser, '=')) {
11053 lex_state_set(parser, PM_LEX_STATE_BEG);
11054 LEX(PM_TOKEN_MINUS_EQUAL);
11055 }
11056
11057 if (match(parser, '>')) {
11058 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11059 LEX(PM_TOKEN_MINUS_GREATER);
11060 }
11061
11062 bool spcarg = lex_state_spcarg_p(parser, space_seen);
11063 bool is_beg = lex_state_beg_p(parser);
11064 if (!is_beg && spcarg) {
11065 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS);
11066 }
11067
11068 if (is_beg || spcarg) {
11069 lex_state_set(parser, PM_LEX_STATE_BEG);
11070 LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS);
11071 }
11072
11073 if (ambiguous_operator_p(parser, space_seen)) {
11074 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "-", "unary operator");
11075 }
11076
11077 lex_state_set(parser, PM_LEX_STATE_BEG);
11078 LEX(PM_TOKEN_MINUS);
11079 }
11080
11081 // . .. ...
11082 case '.': {
11083 bool beg_p = lex_state_beg_p(parser);
11084
11085 if (match(parser, '.')) {
11086 if (match(parser, '.')) {
11087 // If we're _not_ inside a range within default parameters
11088 if (!context_p(parser, PM_CONTEXT_DEFAULT_PARAMS) && context_p(parser, PM_CONTEXT_DEF_PARAMS)) {
11089 if (lex_state_p(parser, PM_LEX_STATE_END)) {
11090 lex_state_set(parser, PM_LEX_STATE_BEG);
11091 } else {
11092 lex_state_set(parser, PM_LEX_STATE_ENDARG);
11093 }
11094 LEX(PM_TOKEN_UDOT_DOT_DOT);
11095 }
11096
11097 if (parser->enclosure_nesting == 0 && parser_end_of_line_p(parser)) {
11098 pm_parser_warn_token(parser, &parser->current, PM_WARN_DOT_DOT_DOT_EOL);
11099 }
11100
11101 lex_state_set(parser, PM_LEX_STATE_BEG);
11102 LEX(beg_p ? PM_TOKEN_UDOT_DOT_DOT : PM_TOKEN_DOT_DOT_DOT);
11103 }
11104
11105 lex_state_set(parser, PM_LEX_STATE_BEG);
11106 LEX(beg_p ? PM_TOKEN_UDOT_DOT : PM_TOKEN_DOT_DOT);
11107 }
11108
11109 lex_state_set(parser, PM_LEX_STATE_DOT);
11110 LEX(PM_TOKEN_DOT);
11111 }
11112
11113 // integer
11114 case '0':
11115 case '1':
11116 case '2':
11117 case '3':
11118 case '4':
11119 case '5':
11120 case '6':
11121 case '7':
11122 case '8':
11123 case '9': {
11124 pm_token_type_t type = lex_numeric(parser);
11125 lex_state_set(parser, PM_LEX_STATE_END);
11126 LEX(type);
11127 }
11128
11129 // :: symbol
11130 case ':':
11131 if (match(parser, ':')) {
11132 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)) {
11133 lex_state_set(parser, PM_LEX_STATE_BEG);
11134 LEX(PM_TOKEN_UCOLON_COLON);
11135 }
11136
11137 lex_state_set(parser, PM_LEX_STATE_DOT);
11138 LEX(PM_TOKEN_COLON_COLON);
11139 }
11140
11141 if (lex_state_end_p(parser) || pm_char_is_whitespace(peek(parser)) || peek(parser) == '#') {
11142 lex_state_set(parser, PM_LEX_STATE_BEG);
11143 LEX(PM_TOKEN_COLON);
11144 }
11145
11146 if (peek(parser) == '"' || peek(parser) == '\'') {
11147 lex_mode_push_string(parser, peek(parser) == '"', false, '\0', *parser->current.end);
11148 parser->current.end++;
11149 } else {
11150 /*
11151 * A quoted symbol clears its encoding by pushing a lex
11152 * mode above. A bare symbol is lexed inline, so it
11153 * clears the encoding here.
11154 */
11155 parser->explicit_encoding = NULL;
11156 }
11157
11158 lex_state_set(parser, PM_LEX_STATE_FNAME);
11159 LEX(PM_TOKEN_SYMBOL_BEGIN);
11160
11161 // / /=
11162 case '/':
11163 if (lex_state_beg_p(parser)) {
11164 lex_mode_push_regexp(parser, '\0', '/');
11165 LEX(PM_TOKEN_REGEXP_BEGIN);
11166 }
11167
11168 if (match(parser, '=')) {
11169 lex_state_set(parser, PM_LEX_STATE_BEG);
11170 LEX(PM_TOKEN_SLASH_EQUAL);
11171 }
11172
11173 if (lex_state_spcarg_p(parser, space_seen)) {
11174 // https://bugs.ruby-lang.org/issues/21994
11175 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_4_0) {
11176 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_SLASH);
11177 }
11178 lex_mode_push_regexp(parser, '\0', '/');
11179 LEX(PM_TOKEN_REGEXP_BEGIN);
11180 }
11181
11182 if (ambiguous_operator_p(parser, space_seen)) {
11183 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "/", "regexp literal");
11184 }
11185
11186 if (lex_state_operator_p(parser)) {
11187 lex_state_set(parser, PM_LEX_STATE_ARG);
11188 } else {
11189 lex_state_set(parser, PM_LEX_STATE_BEG);
11190 }
11191
11192 LEX(PM_TOKEN_SLASH);
11193
11194 // ^ ^=
11195 case '^':
11196 if (lex_state_operator_p(parser)) {
11197 lex_state_set(parser, PM_LEX_STATE_ARG);
11198 } else {
11199 lex_state_set(parser, PM_LEX_STATE_BEG);
11200 }
11201 LEX(match(parser, '=') ? PM_TOKEN_CARET_EQUAL : PM_TOKEN_CARET);
11202
11203 // ~ ~@
11204 case '~':
11205 if (lex_state_operator_p(parser)) {
11206 (void) match(parser, '@');
11207 lex_state_set(parser, PM_LEX_STATE_ARG);
11208 } else {
11209 lex_state_set(parser, PM_LEX_STATE_BEG);
11210 }
11211
11212 LEX(PM_TOKEN_TILDE);
11213
11214 // % %= %i %I %q %Q %w %W
11215 case '%': {
11216 // If there is no subsequent character then we have an
11217 // invalid token. We're going to say it's the percent
11218 // operator because we don't want to move into the string
11219 // lex mode unnecessarily.
11220 if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) {
11221 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT_EOF);
11222 LEX(PM_TOKEN_PERCENT);
11223 }
11224
11225 if (!lex_state_beg_p(parser) && match(parser, '=')) {
11226 lex_state_set(parser, PM_LEX_STATE_BEG);
11227 LEX(PM_TOKEN_PERCENT_EQUAL);
11228 } else if (
11229 lex_state_beg_p(parser) ||
11230 (lex_state_p(parser, PM_LEX_STATE_FITEM) && (peek(parser) == 's')) ||
11231 lex_state_spcarg_p(parser, space_seen)
11232 ) {
11233 if (!parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end)) {
11234 if (*parser->current.end >= 0x80) {
11235 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11236 goto lex_next_token;
11237 }
11238
11239 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11240 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11241 LEX(PM_TOKEN_STRING_BEGIN);
11242 }
11243
11244 // Delimiters for %-literals cannot be alphanumeric. We
11245 // validate that here.
11246 uint8_t delimiter = peek_offset(parser, 1);
11247 if (delimiter >= 0x80 || parser->encoding->alnum_char(&delimiter, 1)) {
11248 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11249 goto lex_next_token;
11250 }
11251
11252 switch (peek(parser)) {
11253 case 'i': {
11254 parser->current.end++;
11255
11256 if (parser->current.end < parser->end) {
11257 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11258 } else {
11259 lex_mode_push_list_eof(parser);
11260 }
11261
11262 LEX(PM_TOKEN_PERCENT_LOWER_I);
11263 }
11264 case 'I': {
11265 parser->current.end++;
11266
11267 if (parser->current.end < parser->end) {
11268 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11269 } else {
11270 lex_mode_push_list_eof(parser);
11271 }
11272
11273 LEX(PM_TOKEN_PERCENT_UPPER_I);
11274 }
11275 case 'r': {
11276 parser->current.end++;
11277
11278 if (parser->current.end < parser->end) {
11279 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11280 lex_mode_push_regexp(parser, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11281 } else {
11282 lex_mode_push_regexp(parser, '\0', '\0');
11283 }
11284
11285 LEX(PM_TOKEN_REGEXP_BEGIN);
11286 }
11287 case 'q': {
11288 parser->current.end++;
11289
11290 if (parser->current.end < parser->end) {
11291 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11292 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11293 } else {
11294 lex_mode_push_string_eof(parser);
11295 }
11296
11297 LEX(PM_TOKEN_STRING_BEGIN);
11298 }
11299 case 'Q': {
11300 parser->current.end++;
11301
11302 if (parser->current.end < parser->end) {
11303 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11304 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11305 } else {
11306 lex_mode_push_string_eof(parser);
11307 }
11308
11309 LEX(PM_TOKEN_STRING_BEGIN);
11310 }
11311 case 's': {
11312 parser->current.end++;
11313
11314 if (parser->current.end < parser->end) {
11315 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11316 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11317 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
11318 } else {
11319 lex_mode_push_string_eof(parser);
11320 }
11321
11322 LEX(PM_TOKEN_SYMBOL_BEGIN);
11323 }
11324 case 'w': {
11325 parser->current.end++;
11326
11327 if (parser->current.end < parser->end) {
11328 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11329 } else {
11330 lex_mode_push_list_eof(parser);
11331 }
11332
11333 LEX(PM_TOKEN_PERCENT_LOWER_W);
11334 }
11335 case 'W': {
11336 parser->current.end++;
11337
11338 if (parser->current.end < parser->end) {
11339 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11340 } else {
11341 lex_mode_push_list_eof(parser);
11342 }
11343
11344 LEX(PM_TOKEN_PERCENT_UPPER_W);
11345 }
11346 case 'x': {
11347 parser->current.end++;
11348
11349 if (parser->current.end < parser->end) {
11350 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11351 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11352 } else {
11353 lex_mode_push_string_eof(parser);
11354 }
11355
11356 LEX(PM_TOKEN_PERCENT_LOWER_X);
11357 }
11358 default:
11359 // If we get to this point, then we have a % that is completely
11360 // unparsable. In this case we'll just drop it from the parser
11361 // and skip past it and hope that the next token is something
11362 // that we can parse.
11363 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11364 goto lex_next_token;
11365 }
11366 }
11367
11368 if (ambiguous_operator_p(parser, space_seen)) {
11369 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "%", "string literal");
11370 }
11371
11372 lex_state_set(parser, lex_state_operator_p(parser) ? PM_LEX_STATE_ARG : PM_LEX_STATE_BEG);
11373 LEX(PM_TOKEN_PERCENT);
11374 }
11375
11376 // global variable
11377 case '$': {
11378 pm_token_type_t type = lex_global_variable(parser);
11379
11380 // If we're lexing an embedded variable, then we need to pop back into
11381 // the parent lex context.
11382 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
11383 lex_mode_pop(parser);
11384 }
11385
11386 lex_state_set(parser, PM_LEX_STATE_END);
11387 LEX(type);
11388 }
11389
11390 // instance variable, class variable
11391 case '@':
11392 lex_state_set(parser, parser->lex_state & PM_LEX_STATE_FNAME ? PM_LEX_STATE_ENDFN : PM_LEX_STATE_END);
11393 LEX(lex_at_variable(parser));
11394
11395 default: {
11396 if (*parser->current.start != '_') {
11397 size_t width = char_is_identifier_start(parser, parser->current.start, parser->end - parser->current.start);
11398
11399 // If this isn't the beginning of an identifier, then
11400 // it's an invalid token as we've exhausted all of the
11401 // other options. We'll skip past it and return the next
11402 // token after adding an appropriate error message.
11403 if (!width) {
11404 if (*parser->current.start >= 0x80) {
11405 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *parser->current.start);
11406 } else if (*parser->current.start == '\\') {
11407 switch (peek_at(parser, parser->current.start + 1)) {
11408 case ' ':
11409 parser->current.end++;
11410 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped space");
11411 break;
11412 case '\f':
11413 parser->current.end++;
11414 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped form feed");
11415 break;
11416 case '\t':
11417 parser->current.end++;
11418 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped horizontal tab");
11419 break;
11420 case '\v':
11421 parser->current.end++;
11422 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped vertical tab");
11423 break;
11424 case '\r':
11425 if (peek_at(parser, parser->current.start + 2) != '\n') {
11426 parser->current.end++;
11427 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped carriage return");
11428 break;
11429 }
11431 default:
11432 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "backslash");
11433 break;
11434 }
11435 } else if (char_is_ascii_printable(*parser->current.start)) {
11436 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_PRINTABLE_CHARACTER, *parser->current.start);
11437 } else {
11438 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_CHARACTER, *parser->current.start);
11439 }
11440
11441 goto lex_next_token;
11442 }
11443
11444 parser->current.end = parser->current.start + width;
11445 }
11446
11447 pm_token_type_t type = lex_identifier(parser, previous_command_start);
11448
11449 // If we've hit a __END__ and it was at the start of the
11450 // line or the start of the file and it is followed by
11451 // either a \n or a \r\n, then this is the last token of the
11452 // file.
11453 if (
11454 ((parser->current.end - parser->current.start) == 7) &&
11455 current_token_starts_line(parser) &&
11456 (memcmp(parser->current.start, "__END__", 7) == 0) &&
11457 (parser->current.end == parser->end || match_eol(parser))
11458 ) {
11459 // Since we know we're about to add an __END__ comment,
11460 // we know we need to add all of the newlines to get the
11461 // correct column information for it.
11462 const uint8_t *cursor = parser->current.end;
11463 while ((cursor = next_newline(cursor, parser->end - cursor)) != NULL) {
11464 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(++cursor - parser->start));
11465 }
11466
11467 parser->current.end = parser->end;
11468 parser->current.type = PM_TOKEN___END__;
11469 parser_lex_callback(parser);
11470
11471 parser->data_loc.start = PM_TOKEN_START(parser, &parser->current);
11472 parser->data_loc.length = PM_TOKEN_LENGTH(&parser->current);
11473
11474 LEX(PM_TOKEN_EOF);
11475 }
11476
11477 pm_lex_state_t last_state = parser->lex_state;
11478
11479 if (type == PM_TOKEN_IDENTIFIER || type == PM_TOKEN_CONSTANT || type == PM_TOKEN_METHOD_NAME) {
11480 if (lex_state_p(parser, PM_LEX_STATE_BEG_ANY | PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_DOT)) {
11481 if (previous_command_start) {
11482 lex_state_set(parser, PM_LEX_STATE_CMDARG);
11483 } else {
11484 lex_state_set(parser, PM_LEX_STATE_ARG);
11485 }
11486 } else if (parser->lex_state == PM_LEX_STATE_FNAME) {
11487 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11488 } else {
11489 lex_state_set(parser, PM_LEX_STATE_END);
11490 }
11491 }
11492
11493 if (
11494 !(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) &&
11495 (type == PM_TOKEN_IDENTIFIER) &&
11496 ((pm_parser_local_depth(parser, &parser->current) != -1) ||
11497 pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)))
11498 ) {
11499 lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL);
11500 }
11501
11502 LEX(type);
11503 }
11504 }
11505 }
11506 case PM_LEX_LIST: {
11507 if (parser->next_start != NULL) {
11508 parser->current.end = parser->next_start;
11509 parser->next_start = NULL;
11510 }
11511
11512 // First we'll set the beginning of the token.
11513 parser->current.start = parser->current.end;
11514
11515 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11516
11517 // If there's any whitespace at the start of the list, then we're
11518 // going to trim it off the beginning and create a new token.
11519 size_t whitespace;
11520
11521 if (parser->heredoc_end) {
11522 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11523 if (peek_offset(parser, (ptrdiff_t)whitespace) == '\n') {
11524 whitespace += 1;
11525 }
11526 } else if (lex_mode->as.list.terminator == '\n') {
11527 // When the list delimiter is a newline (e.g. `%w` followed by a
11528 // newline), the newline is the terminator rather than a word
11529 // separator. We only trim inline whitespace here so that the
11530 // terminating newline is left for the terminator handling below.
11531 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11532 } else {
11533 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));
11534 }
11535
11536 if (whitespace > 0) {
11537 parser->current.end += whitespace;
11538 if (peek_offset(parser, -1) == '\n') {
11539 // mutates next_start
11540 parser_flush_heredoc_end(parser);
11541 }
11542
11543 lex_mode->as.list.started = true;
11544 lex_mode->as.list.separated = true;
11545 LEX(PM_TOKEN_WORDS_SEP);
11546 }
11547
11548 // We'll check if we're at the end of the file. If we are, then we
11549 // need to return the EOF token.
11550 if (parser->current.end >= parser->end) {
11551 LEX(PM_TOKEN_EOF);
11552 }
11553
11554 /* A word separator delimits the opener from the first word (or
11555 * from the terminator when the list is empty). The parser accepts
11556 * the words without it, so when the list does not start with
11557 * whitespace the implicit separator goes to the lex callback alone
11558 * and lexing continues on to the token the parser receives. */
11559 if (!lex_mode->as.list.started) {
11560 lex_mode->as.list.started = true;
11561 lex_mode->as.list.separated = true;
11562 parser->current.type = PM_TOKEN_WORDS_SEP_IMPLICIT;
11563 parser_lex_callback(parser);
11564 }
11565
11566 // Here we'll get a list of the places where strpbrk should break,
11567 // and then find the first one.
11568 const uint8_t *breakpoints = lex_mode->as.list.breakpoints;
11569 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11570
11571 // If we haven't found an escape yet, then this buffer will be
11572 // unallocated since we can refer directly to the source string.
11573 pm_token_buffer_t token_buffer = { 0 };
11574
11575 while (breakpoint != NULL) {
11576 // If we hit whitespace, then we must have received content by
11577 // now, so we can return an element of the list. A whitespace
11578 // character that is also the terminator (e.g. a newline
11579 // delimiter) is handled by the terminator check below, not here.
11580 if (pm_char_is_whitespace(*breakpoint) && *breakpoint != lex_mode->as.list.terminator) {
11581 parser->current.end = breakpoint;
11582 pm_token_buffer_flush(parser, &token_buffer);
11583 lex_mode->as.list.separated = false;
11584 LEX(PM_TOKEN_STRING_CONTENT);
11585 }
11586
11587 // If we hit the terminator, we need to check which token to
11588 // return.
11589 if (*breakpoint == lex_mode->as.list.terminator) {
11590 // If this terminator doesn't actually close the list, then
11591 // we need to continue on past it.
11592 if (lex_mode->as.list.nesting > 0) {
11593 parser->current.end = breakpoint + 1;
11594 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11595 lex_mode->as.list.nesting--;
11596 continue;
11597 }
11598
11599 // If we've hit the terminator and we've already skipped
11600 // past content, then we can return a list node.
11601 if (breakpoint > parser->current.start) {
11602 parser->current.end = breakpoint;
11603 pm_token_buffer_flush(parser, &token_buffer);
11604 lex_mode->as.list.separated = false;
11605 LEX(PM_TOKEN_STRING_CONTENT);
11606 }
11607
11608 /* A word separator delimits the last word from the
11609 * terminator. The parser accepts the terminator without
11610 * it, so when the list does not end with whitespace the
11611 * implicit separator goes to the lex callback alone and
11612 * lexing continues on to the terminator. */
11613 if (!lex_mode->as.list.separated) {
11614 lex_mode->as.list.separated = true;
11615 parser->current.end = breakpoint;
11616 parser->current.type = PM_TOKEN_WORDS_SEP_IMPLICIT;
11617 parser_lex_callback(parser);
11618 }
11619
11620 // Otherwise, switch back to the default state and return
11621 // the end of the list.
11622 parser->current.end = breakpoint + 1;
11623
11624 // If the terminator is a newline (i.e. the list delimiter
11625 // was a newline), then we need to record it so that line
11626 // numbers after the list remain accurate.
11627 if (*breakpoint == '\n') {
11628 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11629 }
11630
11631 lex_mode_pop(parser);
11632 lex_state_set(parser, PM_LEX_STATE_END);
11633 LEX(PM_TOKEN_STRING_END);
11634 }
11635
11636 // If we hit a null byte, skip directly past it.
11637 if (*breakpoint == '\0') {
11638 breakpoint = pm_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1), true);
11639 continue;
11640 }
11641
11642 // If we hit escapes, then we need to treat the next token
11643 // literally. In this case we'll skip past the next character
11644 // and find the next breakpoint.
11645 if (*breakpoint == '\\') {
11646 parser->current.end = breakpoint + 1;
11647
11648 // If we've hit the end of the file, then break out of the
11649 // loop by setting the breakpoint to NULL.
11650 if (parser->current.end == parser->end) {
11651 breakpoint = NULL;
11652 continue;
11653 }
11654
11655 pm_token_buffer_escape(parser, &token_buffer);
11656 uint8_t peeked = peek(parser);
11657
11658 switch (peeked) {
11659 case ' ':
11660 case '\f':
11661 case '\t':
11662 case '\v':
11663 case '\\':
11664 pm_token_buffer_push_byte(&token_buffer, peeked);
11665 parser->current.end++;
11666 break;
11667 case '\r':
11668 parser->current.end++;
11669 if (peek(parser) != '\n') {
11670 pm_token_buffer_push_byte(&token_buffer, '\r');
11671 break;
11672 }
11674 case '\n':
11675 pm_token_buffer_push_byte(&token_buffer, '\n');
11676
11677 if (parser->heredoc_end) {
11678 // ... if we are on the same line as a heredoc,
11679 // flush the heredoc and continue parsing after
11680 // heredoc_end.
11681 parser_flush_heredoc_end(parser);
11682 pm_token_buffer_copy(parser, &token_buffer);
11683 LEX(PM_TOKEN_STRING_CONTENT);
11684 } else {
11685 // ... else track the newline.
11686 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11687 }
11688
11689 parser->current.end++;
11690 break;
11691 default:
11692 if (peeked == lex_mode->as.list.incrementor || peeked == lex_mode->as.list.terminator) {
11693 pm_token_buffer_push_byte(&token_buffer, peeked);
11694 parser->current.end++;
11695 } else if (lex_mode->as.list.interpolation) {
11696 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
11697 } else {
11698 pm_token_buffer_push_byte(&token_buffer, '\\');
11699 pm_token_buffer_push_escaped(&token_buffer, parser);
11700 }
11701
11702 break;
11703 }
11704
11705 token_buffer.cursor = parser->current.end;
11706 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11707 continue;
11708 }
11709
11710 // If we hit a #, then we will attempt to lex interpolation.
11711 if (*breakpoint == '#') {
11712 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11713
11714 if (!type) {
11715 // If we haven't returned at this point then we had something
11716 // that looked like an interpolated class or instance variable
11717 // like "#@" but wasn't actually. In this case we'll just skip
11718 // to the next breakpoint.
11719 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11720 continue;
11721 }
11722
11723 if (type == PM_TOKEN_STRING_CONTENT) {
11724 pm_token_buffer_flush(parser, &token_buffer);
11725 }
11726
11727 lex_mode->as.list.separated = false;
11728 LEX(type);
11729 }
11730
11731 // If we've hit the incrementor, then we need to skip past it
11732 // and find the next breakpoint.
11733 assert(*breakpoint == lex_mode->as.list.incrementor);
11734 parser->current.end = breakpoint + 1;
11735 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11736 lex_mode->as.list.nesting++;
11737 continue;
11738 }
11739
11740 if (parser->current.end > parser->current.start) {
11741 pm_token_buffer_flush(parser, &token_buffer);
11742 LEX(PM_TOKEN_STRING_CONTENT);
11743 }
11744
11745 // If we were unable to find a breakpoint, then this token hits the
11746 // end of the file.
11747 parser->current.end = parser->end;
11748 pm_token_buffer_flush(parser, &token_buffer);
11749 LEX(PM_TOKEN_STRING_CONTENT);
11750 }
11751 case PM_LEX_REGEXP: {
11752 // First, we'll set to start of this token to be the current end.
11753 if (parser->next_start == NULL) {
11754 parser->current.start = parser->current.end;
11755 } else {
11756 parser->current.start = parser->next_start;
11757 parser->current.end = parser->next_start;
11758 parser->next_start = NULL;
11759 }
11760
11761 // We'll check if we're at the end of the file. If we are, then we
11762 // need to return the EOF token.
11763 if (parser->current.end >= parser->end) {
11764 LEX(PM_TOKEN_EOF);
11765 }
11766
11767 // Get a reference to the current mode.
11768 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11769
11770 // These are the places where we need to split up the content of the
11771 // regular expression. We'll use strpbrk to find the first of these
11772 // characters.
11773 const uint8_t *breakpoints = lex_mode->as.regexp.breakpoints;
11774 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11775 pm_regexp_token_buffer_t token_buffer = { 0 };
11776
11777 while (breakpoint != NULL) {
11778 uint8_t term = lex_mode->as.regexp.terminator;
11779 bool is_terminator = (*breakpoint == term);
11780
11781 // If the terminator is newline, we need to consider \r\n _also_ a newline
11782 // For example: `%\nfoo\r\n`
11783 // The string should be "foo", not "foo\r"
11784 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
11785 if (term == '\n') {
11786 is_terminator = true;
11787 }
11788
11789 // If the terminator is a CR, but we see a CRLF, we need to
11790 // treat the CRLF as a newline, meaning this is _not_ the
11791 // terminator
11792 if (term == '\r') {
11793 is_terminator = false;
11794 }
11795 }
11796
11797 // If we hit the terminator, we need to determine what kind of
11798 // token to return.
11799 if (is_terminator) {
11800 if (lex_mode->as.regexp.nesting > 0) {
11801 parser->current.end = breakpoint + 1;
11802 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11803 lex_mode->as.regexp.nesting--;
11804 continue;
11805 }
11806
11807 // Here we've hit the terminator. If we have already consumed
11808 // content then we need to return that content as string content
11809 // first.
11810 if (breakpoint > parser->current.start) {
11811 parser->current.end = breakpoint;
11812 pm_regexp_token_buffer_flush(parser, &token_buffer);
11813 LEX(PM_TOKEN_STRING_CONTENT);
11814 }
11815
11816 // Check here if we need to track the newline.
11817 size_t eol_length = match_eol_at(parser, breakpoint);
11818 if (eol_length) {
11819 parser->current.end = breakpoint + eol_length;
11820
11821 // Track the newline if we're not in a heredoc that
11822 // would have already have added the newline to the
11823 // list.
11824 if (parser->heredoc_end == NULL) {
11825 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11826 }
11827 } else {
11828 parser->current.end = breakpoint + 1;
11829 }
11830
11831 // Since we've hit the terminator of the regular expression,
11832 // we now need to parse the options.
11833 parser->current.end += pm_strspn_regexp_option(parser->current.end, parser->end - parser->current.end);
11834
11835 lex_mode_pop(parser);
11836 lex_state_set(parser, PM_LEX_STATE_END);
11837 LEX(PM_TOKEN_REGEXP_END);
11838 }
11839
11840 // If we've hit the incrementor, then we need to skip past it
11841 // and find the next breakpoint.
11842 if (*breakpoint && *breakpoint == lex_mode->as.regexp.incrementor) {
11843 parser->current.end = breakpoint + 1;
11844 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11845 lex_mode->as.regexp.nesting++;
11846 continue;
11847 }
11848
11849 switch (*breakpoint) {
11850 case '\0':
11851 // If we hit a null byte, skip directly past it.
11852 parser->current.end = breakpoint + 1;
11853 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11854 break;
11855 case '\r':
11856 if (peek_at(parser, breakpoint + 1) != '\n') {
11857 parser->current.end = breakpoint + 1;
11858 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11859 break;
11860 }
11861
11862 breakpoint++;
11863 parser->current.end = breakpoint;
11864 pm_regexp_token_buffer_escape(parser, &token_buffer);
11865 token_buffer.base.cursor = breakpoint;
11866
11868 case '\n':
11869 // If we've hit a newline, then we need to track that in
11870 // the list of newlines.
11871 if (parser->heredoc_end == NULL) {
11872 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
11873 parser->current.end = breakpoint + 1;
11874 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11875 break;
11876 }
11877
11878 parser->current.end = breakpoint + 1;
11879 parser_flush_heredoc_end(parser);
11880 pm_regexp_token_buffer_flush(parser, &token_buffer);
11881 LEX(PM_TOKEN_STRING_CONTENT);
11882 case '\\': {
11883 // If we hit escapes, then we need to treat the next
11884 // token literally. In this case we'll skip past the
11885 // next character and find the next breakpoint.
11886 parser->current.end = breakpoint + 1;
11887
11888 // If we've hit the end of the file, then break out of
11889 // the loop by setting the breakpoint to NULL.
11890 if (parser->current.end == parser->end) {
11891 breakpoint = NULL;
11892 break;
11893 }
11894
11895 pm_regexp_token_buffer_escape(parser, &token_buffer);
11896 uint8_t peeked = peek(parser);
11897
11898 switch (peeked) {
11899 case '\r':
11900 parser->current.end++;
11901 if (peek(parser) != '\n') {
11902 if (lex_mode->as.regexp.terminator != '\r') {
11903 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11904 }
11905 pm_regexp_token_buffer_push_byte(&token_buffer, '\r');
11906 pm_token_buffer_push_byte(&token_buffer.base, '\r');
11907 break;
11908 }
11910 case '\n':
11911 if (parser->heredoc_end) {
11912 // ... if we are on the same line as a heredoc,
11913 // flush the heredoc and continue parsing after
11914 // heredoc_end.
11915 parser_flush_heredoc_end(parser);
11916 pm_regexp_token_buffer_copy(parser, &token_buffer);
11917 LEX(PM_TOKEN_STRING_CONTENT);
11918 } else {
11919 // ... else track the newline.
11920 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11921 }
11922
11923 parser->current.end++;
11924 break;
11925 case 'c':
11926 case 'C':
11927 case 'M':
11928 case 'u':
11929 case 'x':
11930 escape_read(parser, &token_buffer.regexp_buffer, &token_buffer.base.buffer, PM_ESCAPE_FLAG_REGEXP);
11931 break;
11932 default:
11933 if (lex_mode->as.regexp.terminator == peeked) {
11934 // Some characters when they are used as the
11935 // terminator also receive an escape. They are
11936 // enumerated here.
11937 switch (peeked) {
11938 case '$': case ')': case '*': case '+':
11939 case '.': case '>': case '?': case ']':
11940 case '^': case '|': case '}':
11941 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11942 break;
11943 default:
11944 break;
11945 }
11946
11947 pm_regexp_token_buffer_push_byte(&token_buffer, peeked);
11948 pm_token_buffer_push_byte(&token_buffer.base, peeked);
11949 parser->current.end++;
11950 break;
11951 }
11952
11953 if (peeked < 0x80) pm_token_buffer_push_byte(&token_buffer.base, '\\');
11954 pm_regexp_token_buffer_push_escaped(&token_buffer, parser);
11955 break;
11956 }
11957
11958 token_buffer.base.cursor = parser->current.end;
11959 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11960 break;
11961 }
11962 case '#': {
11963 // If we hit a #, then we will attempt to lex
11964 // interpolation.
11965 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11966
11967 if (!type) {
11968 // If we haven't returned at this point then we had
11969 // something that looked like an interpolated class or
11970 // instance variable like "#@" but wasn't actually. In
11971 // this case we'll just skip to the next breakpoint.
11972 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11973 break;
11974 }
11975
11976 if (type == PM_TOKEN_STRING_CONTENT) {
11977 pm_regexp_token_buffer_flush(parser, &token_buffer);
11978 }
11979
11980 LEX(type);
11981 }
11982 default:
11983 assert(false && "unreachable");
11984 break;
11985 }
11986 }
11987
11988 if (parser->current.end > parser->current.start) {
11989 pm_regexp_token_buffer_flush(parser, &token_buffer);
11990 LEX(PM_TOKEN_STRING_CONTENT);
11991 }
11992
11993 // If we were unable to find a breakpoint, then this token hits the
11994 // end of the file.
11995 parser->current.end = parser->end;
11996 pm_regexp_token_buffer_flush(parser, &token_buffer);
11997 LEX(PM_TOKEN_STRING_CONTENT);
11998 }
11999 case PM_LEX_STRING: {
12000 // First, we'll set to start of this token to be the current end.
12001 if (parser->next_start == NULL) {
12002 parser->current.start = parser->current.end;
12003 } else {
12004 parser->current.start = parser->next_start;
12005 parser->current.end = parser->next_start;
12006 parser->next_start = NULL;
12007 }
12008
12009 // We'll check if we're at the end of the file. If we are, then we need to
12010 // return the EOF token.
12011 if (parser->current.end >= parser->end) {
12012 LEX(PM_TOKEN_EOF);
12013 }
12014
12015 // These are the places where we need to split up the content of the
12016 // string. We'll use strpbrk to find the first of these characters.
12017 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12018 const uint8_t *breakpoints = lex_mode->as.string.breakpoints;
12019 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12020
12021 // If we haven't found an escape yet, then this buffer will be
12022 // unallocated since we can refer directly to the source string.
12023 pm_token_buffer_t token_buffer = { 0 };
12024
12025 while (breakpoint != NULL) {
12026 // If we hit the incrementor, then we'll increment then nesting and
12027 // continue lexing.
12028 if (lex_mode->as.string.incrementor != '\0' && *breakpoint == lex_mode->as.string.incrementor) {
12029 lex_mode->as.string.nesting++;
12030 parser->current.end = breakpoint + 1;
12031 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12032 continue;
12033 }
12034
12035 uint8_t term = lex_mode->as.string.terminator;
12036 bool is_terminator = (*breakpoint == term);
12037
12038 // If the terminator is newline, we need to consider \r\n _also_ a newline
12039 // For example: `%r\nfoo\r\n`
12040 // The string should be /foo/, not /foo\r/
12041 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
12042 if (term == '\n') {
12043 is_terminator = true;
12044 }
12045
12046 // If the terminator is a CR, but we see a CRLF, we need to
12047 // treat the CRLF as a newline, meaning this is _not_ the
12048 // terminator
12049 if (term == '\r') {
12050 is_terminator = false;
12051 }
12052 }
12053
12054 // Note that we have to check the terminator here first because we could
12055 // potentially be parsing a % string that has a # character as the
12056 // terminator.
12057 if (is_terminator) {
12058 // If this terminator doesn't actually close the string, then we need
12059 // to continue on past it.
12060 if (lex_mode->as.string.nesting > 0) {
12061 parser->current.end = breakpoint + 1;
12062 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12063 lex_mode->as.string.nesting--;
12064 continue;
12065 }
12066
12067 // Here we've hit the terminator. If we have already consumed content
12068 // then we need to return that content as string content first.
12069 if (breakpoint > parser->current.start) {
12070 parser->current.end = breakpoint;
12071 pm_token_buffer_flush(parser, &token_buffer);
12072 LEX(PM_TOKEN_STRING_CONTENT);
12073 }
12074
12075 // Otherwise we need to switch back to the parent lex mode and
12076 // return the end of the string.
12077 size_t eol_length = match_eol_at(parser, breakpoint);
12078 if (eol_length) {
12079 parser->current.end = breakpoint + eol_length;
12080
12081 // Track the newline if we're not in a heredoc that
12082 // would have already have added the newline to the
12083 // list.
12084 if (parser->heredoc_end == NULL) {
12085 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
12086 }
12087 } else {
12088 parser->current.end = breakpoint + 1;
12089 }
12090
12091 if (lex_mode->as.string.label_allowed && (peek(parser) == ':') && (peek_offset(parser, 1) != ':')) {
12092 parser->current.end++;
12093 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
12094 lex_mode_pop(parser);
12095 LEX(PM_TOKEN_LABEL_END);
12096 }
12097
12098 // When the delimiter itself is a newline, we won't
12099 // get a chance to flush heredocs in the usual places since
12100 // the newline is already consumed.
12101 if (term == '\n' && parser->heredoc_end) {
12102 parser_flush_heredoc_end(parser);
12103 }
12104
12105 lex_state_set(parser, PM_LEX_STATE_END);
12106 lex_mode_pop(parser);
12107 LEX(PM_TOKEN_STRING_END);
12108 }
12109
12110 switch (*breakpoint) {
12111 case '\0':
12112 // Skip directly past the null character.
12113 parser->current.end = breakpoint + 1;
12114 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12115 break;
12116 case '\r':
12117 if (peek_at(parser, breakpoint + 1) != '\n') {
12118 parser->current.end = breakpoint + 1;
12119 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12120 break;
12121 }
12122
12123 // If we hit a \r\n sequence, then we need to treat it
12124 // as a newline.
12125 breakpoint++;
12126 parser->current.end = breakpoint;
12127 pm_token_buffer_escape(parser, &token_buffer);
12128 token_buffer.cursor = breakpoint;
12129
12131 case '\n':
12132 // When we hit a newline, we need to flush any potential
12133 // heredocs. Note that this has to happen after we check
12134 // for the terminator in case the terminator is a
12135 // newline character.
12136 if (parser->heredoc_end == NULL) {
12137 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12138 parser->current.end = breakpoint + 1;
12139 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12140 break;
12141 }
12142
12143 parser->current.end = breakpoint + 1;
12144 parser_flush_heredoc_end(parser);
12145 pm_token_buffer_flush(parser, &token_buffer);
12146 LEX(PM_TOKEN_STRING_CONTENT);
12147 case '\\': {
12148 // Here we hit escapes.
12149 parser->current.end = breakpoint + 1;
12150
12151 // If we've hit the end of the file, then break out of
12152 // the loop by setting the breakpoint to NULL.
12153 if (parser->current.end == parser->end) {
12154 breakpoint = NULL;
12155 continue;
12156 }
12157
12158 pm_token_buffer_escape(parser, &token_buffer);
12159 uint8_t peeked = peek(parser);
12160
12161 switch (peeked) {
12162 case '\\':
12163 pm_token_buffer_push_byte(&token_buffer, '\\');
12164 parser->current.end++;
12165 break;
12166 case '\r':
12167 parser->current.end++;
12168 if (peek(parser) != '\n') {
12169 if (!lex_mode->as.string.interpolation) {
12170 pm_token_buffer_push_byte(&token_buffer, '\\');
12171 }
12172 pm_token_buffer_push_byte(&token_buffer, '\r');
12173 break;
12174 }
12176 case '\n':
12177 if (!lex_mode->as.string.interpolation) {
12178 pm_token_buffer_push_byte(&token_buffer, '\\');
12179 pm_token_buffer_push_byte(&token_buffer, '\n');
12180 }
12181
12182 if (parser->heredoc_end) {
12183 // ... if we are on the same line as a heredoc,
12184 // flush the heredoc and continue parsing after
12185 // heredoc_end.
12186 parser_flush_heredoc_end(parser);
12187 pm_token_buffer_copy(parser, &token_buffer);
12188 LEX(PM_TOKEN_STRING_CONTENT);
12189 } else {
12190 // ... else track the newline.
12191 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
12192 }
12193
12194 parser->current.end++;
12195 break;
12196 default:
12197 if (lex_mode->as.string.incrementor != '\0' && peeked == lex_mode->as.string.incrementor) {
12198 pm_token_buffer_push_byte(&token_buffer, peeked);
12199 parser->current.end++;
12200 } else if (lex_mode->as.string.terminator != '\0' && peeked == lex_mode->as.string.terminator) {
12201 pm_token_buffer_push_byte(&token_buffer, peeked);
12202 parser->current.end++;
12203 } else if (lex_mode->as.string.interpolation) {
12204 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12205 } else {
12206 pm_token_buffer_push_byte(&token_buffer, '\\');
12207 pm_token_buffer_push_escaped(&token_buffer, parser);
12208 }
12209
12210 break;
12211 }
12212
12213 token_buffer.cursor = parser->current.end;
12214 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12215 break;
12216 }
12217 case '#': {
12218 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12219
12220 if (!type) {
12221 // If we haven't returned at this point then we had something that
12222 // looked like an interpolated class or instance variable like "#@"
12223 // but wasn't actually. In this case we'll just skip to the next
12224 // breakpoint.
12225 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12226 break;
12227 }
12228
12229 if (type == PM_TOKEN_STRING_CONTENT) {
12230 pm_token_buffer_flush(parser, &token_buffer);
12231 }
12232
12233 LEX(type);
12234 }
12235 default:
12236 assert(false && "unreachable");
12237 }
12238 }
12239
12240 if (parser->current.end > parser->current.start) {
12241 pm_token_buffer_flush(parser, &token_buffer);
12242 LEX(PM_TOKEN_STRING_CONTENT);
12243 }
12244
12245 // If we've hit the end of the string, then this is an unterminated
12246 // string. In that case we'll return a string content token.
12247 parser->current.end = parser->end;
12248 pm_token_buffer_flush(parser, &token_buffer);
12249 LEX(PM_TOKEN_STRING_CONTENT);
12250 }
12251 case PM_LEX_HEREDOC: {
12252 // First, we'll set to start of this token.
12253 if (parser->next_start == NULL) {
12254 parser->current.start = parser->current.end;
12255 } else {
12256 parser->current.start = parser->next_start;
12257 parser->current.end = parser->next_start;
12258 parser->heredoc_end = NULL;
12259 parser->next_start = NULL;
12260 }
12261
12262 // Now let's grab the information about the identifier off of the
12263 // current lex mode.
12264 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12265 pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base;
12266
12267 bool line_continuation = lex_mode->as.heredoc.line_continuation;
12268 lex_mode->as.heredoc.line_continuation = false;
12269
12270 // We'll check if we're at the end of the file. If we are, then we
12271 // will add an error (because we weren't able to find the
12272 // terminator) but still continue parsing so that content after the
12273 // declaration of the heredoc can be parsed.
12274 if (parser->current.end >= parser->end) {
12275 pm_parser_err_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length);
12276 parser->next_start = lex_mode->as.heredoc.next_start;
12277 parser->heredoc_end = parser->current.end;
12278 lex_state_set(parser, PM_LEX_STATE_END);
12279 lex_mode_pop(parser);
12280 LEX(PM_TOKEN_HEREDOC_END);
12281 }
12282
12283 const uint8_t *ident_start = heredoc_lex_mode->ident_start;
12284 size_t ident_length = heredoc_lex_mode->ident_length;
12285
12286 // If we are immediately following a newline and we have hit the
12287 // terminator, then we need to return the ending of the heredoc.
12288 if (current_token_starts_line(parser)) {
12289 const uint8_t *start = parser->current.start;
12290
12291 if (!line_continuation && (start + ident_length <= parser->end)) {
12292 const uint8_t *newline = next_newline(start, parser->end - start);
12293 const uint8_t *ident_end = newline;
12294 const uint8_t *terminator_end = newline;
12295
12296 if (newline == NULL) {
12297 terminator_end = parser->end;
12298 ident_end = parser->end;
12299 } else {
12300 terminator_end++;
12301 if (newline[-1] == '\r') {
12302 ident_end--; // Remove \r
12303 }
12304 }
12305
12306 /*
12307 * The line on which we are looking for the terminator runs
12308 * from `start` to `ident_end`. If that line is shorter than
12309 * the identifier, the terminator cannot fit and we skip the
12310 * match. This also keeps `terminator_start` from pointing
12311 * before `start`.
12312 */
12313 if ((size_t) (ident_end - start) >= ident_length) {
12314 const uint8_t *terminator_start = ident_end - ident_length;
12315 const uint8_t *cursor = start;
12316
12317 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12318 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12319 cursor++;
12320 }
12321 }
12322
12323 if (
12324 (cursor == terminator_start) &&
12325 (memcmp(terminator_start, ident_start, ident_length) == 0)
12326 ) {
12327 if (newline != NULL) {
12328 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
12329 }
12330
12331 parser->current.end = terminator_end;
12332 if (*lex_mode->as.heredoc.next_start == '\\') {
12333 parser->next_start = NULL;
12334 } else {
12335 parser->next_start = lex_mode->as.heredoc.next_start;
12336 parser->heredoc_end = parser->current.end;
12337 }
12338
12339 lex_state_set(parser, PM_LEX_STATE_END);
12340 lex_mode_pop(parser);
12341 LEX(PM_TOKEN_HEREDOC_END);
12342 }
12343 }
12344 }
12345
12346 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent);
12347 if (
12348 heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE &&
12349 lex_mode->as.heredoc.common_whitespace != NULL &&
12350 (*lex_mode->as.heredoc.common_whitespace > whitespace) &&
12351 peek_at(parser, start) != '\n'
12352 ) {
12353 *lex_mode->as.heredoc.common_whitespace = whitespace;
12354 }
12355 }
12356
12357 // Otherwise we'll be parsing string content. These are the places
12358 // where we need to split up the content of the heredoc. We'll use
12359 // strpbrk to find the first of these characters.
12360 uint8_t breakpoints[PM_STRPBRK_CACHE_SIZE] = "\r\n\\#";
12361
12362 pm_heredoc_quote_t quote = heredoc_lex_mode->quote;
12363 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12364 breakpoints[3] = '\0';
12365 }
12366
12367 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12368 pm_token_buffer_t token_buffer = { 0 };
12369 bool was_line_continuation = false;
12370
12371 while (breakpoint != NULL) {
12372 switch (*breakpoint) {
12373 case '\0':
12374 // Skip directly past the null character.
12375 parser->current.end = breakpoint + 1;
12376 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12377 break;
12378 case '\r':
12379 parser->current.end = breakpoint + 1;
12380
12381 if (peek_at(parser, breakpoint + 1) != '\n') {
12382 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12383 break;
12384 }
12385
12386 // If we hit a \r\n sequence, then we want to replace it
12387 // with a single \n character in the final string.
12388 breakpoint++;
12389 pm_token_buffer_escape(parser, &token_buffer);
12390 token_buffer.cursor = breakpoint;
12391
12393 case '\n': {
12394 if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) {
12395 parser_flush_heredoc_end(parser);
12396 parser->current.end = breakpoint + 1;
12397 pm_token_buffer_flush(parser, &token_buffer);
12398 LEX(PM_TOKEN_STRING_CONTENT);
12399 }
12400
12401 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12402
12403 // If we have a - or ~ heredoc, then we can match after
12404 // some leading whitespace.
12405 const uint8_t *start = breakpoint + 1;
12406
12407 if (!was_line_continuation && (start + ident_length <= parser->end)) {
12408 // We want to match the terminator starting from the end of the line in case
12409 // there is whitespace in the ident such as <<-' DOC' or <<~' DOC'.
12410 const uint8_t *newline = next_newline(start, parser->end - start);
12411
12412 if (newline == NULL) {
12413 newline = parser->end;
12414 } else if (newline[-1] == '\r') {
12415 newline--; // Remove \r
12416 }
12417
12418 /*
12419 * The line on which we are looking for the
12420 * terminator runs from `start` to `newline`. If
12421 * that line is shorter than the identifier the
12422 * terminator cannot fit and we skip the match.
12423 * This also keeps `terminator_start` from pointing
12424 * before `start`.
12425 */
12426 if ((size_t) (newline - start) >= ident_length) {
12427 // Start of a possible terminator.
12428 const uint8_t *terminator_start = newline - ident_length;
12429
12430 // Cursor to check for the leading whitespace. We skip the
12431 // leading whitespace if we have a - or ~ heredoc.
12432 const uint8_t *cursor = start;
12433
12434 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12435 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12436 cursor++;
12437 }
12438 }
12439
12440 if (
12441 cursor == terminator_start &&
12442 (memcmp(terminator_start, ident_start, ident_length) == 0)
12443 ) {
12444 parser->current.end = breakpoint + 1;
12445 pm_token_buffer_flush(parser, &token_buffer);
12446 LEX(PM_TOKEN_STRING_CONTENT);
12447 }
12448 }
12449 }
12450
12451 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.base.indent);
12452
12453 // If we have hit a newline that is followed by a valid
12454 // terminator, then we need to return the content of the
12455 // heredoc here as string content. Then, the next time a
12456 // token is lexed, it will match again and return the
12457 // end of the heredoc.
12458 if (lex_mode->as.heredoc.base.indent == PM_HEREDOC_INDENT_TILDE) {
12459 if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
12460 *lex_mode->as.heredoc.common_whitespace = whitespace;
12461 }
12462
12463 parser->current.end = breakpoint + 1;
12464 pm_token_buffer_flush(parser, &token_buffer);
12465 LEX(PM_TOKEN_STRING_CONTENT);
12466 }
12467
12468 // Otherwise we hit a newline and it wasn't followed by
12469 // a terminator, so we can continue parsing.
12470 parser->current.end = breakpoint + 1;
12471 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12472 break;
12473 }
12474 case '\\': {
12475 // If we hit an escape, then we need to skip past
12476 // however many characters the escape takes up. However
12477 // it's important that if \n or \r\n are escaped, we
12478 // stop looping before the newline and not after the
12479 // newline so that we can still potentially find the
12480 // terminator of the heredoc.
12481 parser->current.end = breakpoint + 1;
12482
12483 // If we've hit the end of the file, then break out of
12484 // the loop by setting the breakpoint to NULL.
12485 if (parser->current.end == parser->end) {
12486 breakpoint = NULL;
12487 continue;
12488 }
12489
12490 pm_token_buffer_escape(parser, &token_buffer);
12491 uint8_t peeked = peek(parser);
12492
12493 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12494 switch (peeked) {
12495 case '\r':
12496 parser->current.end++;
12497 if (peek(parser) != '\n') {
12498 pm_token_buffer_push_byte(&token_buffer, '\\');
12499 pm_token_buffer_push_byte(&token_buffer, '\r');
12500 break;
12501 }
12503 case '\n':
12504 pm_token_buffer_push_byte(&token_buffer, '\\');
12505 pm_token_buffer_push_byte(&token_buffer, '\n');
12506 token_buffer.cursor = parser->current.end + 1;
12507 breakpoint = parser->current.end;
12508 continue;
12509 default:
12510 pm_token_buffer_push_byte(&token_buffer, '\\');
12511 pm_token_buffer_push_escaped(&token_buffer, parser);
12512 break;
12513 }
12514 } else {
12515 switch (peeked) {
12516 case '\r':
12517 parser->current.end++;
12518 if (peek(parser) != '\n') {
12519 pm_token_buffer_push_byte(&token_buffer, '\r');
12520 break;
12521 }
12523 case '\n':
12524 // If we are in a tilde here, we should
12525 // break out of the loop and return the
12526 // string content.
12527 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12528 const uint8_t *end = parser->current.end;
12529
12530 if (parser->heredoc_end == NULL) {
12531 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(end - parser->start + 1));
12532 }
12533
12534 // Here we want the buffer to only
12535 // include up to the backslash.
12536 parser->current.end = breakpoint;
12537 pm_token_buffer_flush(parser, &token_buffer);
12538
12539 // Now we can advance the end of the
12540 // token past the newline.
12541 parser->current.end = end + 1;
12542 lex_mode->as.heredoc.line_continuation = true;
12543 LEX(PM_TOKEN_STRING_CONTENT);
12544 }
12545
12546 was_line_continuation = true;
12547 token_buffer.cursor = parser->current.end + 1;
12548 breakpoint = parser->current.end;
12549 continue;
12550 default:
12551 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12552 break;
12553 }
12554 }
12555
12556 token_buffer.cursor = parser->current.end;
12557 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12558 break;
12559 }
12560 case '#': {
12561 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12562
12563 if (!type) {
12564 // If we haven't returned at this point then we had
12565 // something that looked like an interpolated class
12566 // or instance variable like "#@" but wasn't
12567 // actually. In this case we'll just skip to the
12568 // next breakpoint.
12569 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12570 break;
12571 }
12572
12573 if (type == PM_TOKEN_STRING_CONTENT) {
12574 pm_token_buffer_flush(parser, &token_buffer);
12575 }
12576
12577 LEX(type);
12578 }
12579 default:
12580 assert(false && "unreachable");
12581 }
12582
12583 was_line_continuation = false;
12584 }
12585
12586 if (parser->current.end > parser->current.start) {
12587 parser->current.end = parser->end;
12588 pm_token_buffer_flush(parser, &token_buffer);
12589 LEX(PM_TOKEN_STRING_CONTENT);
12590 }
12591
12592 // If we've hit the end of the string, then this is an unterminated
12593 // heredoc. In that case we'll return a string content token.
12594 parser->current.end = parser->end;
12595 pm_token_buffer_flush(parser, &token_buffer);
12596 LEX(PM_TOKEN_STRING_CONTENT);
12597 }
12598 }
12599
12600 assert(false && "unreachable");
12601}
12602
12603#undef LEX
12604
12605/******************************************************************************/
12606/* Parse functions */
12607/******************************************************************************/
12608
12617typedef enum {
12618 PM_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator
12619 PM_BINDING_POWER_STATEMENT = 2,
12620 PM_BINDING_POWER_MODIFIER_RESCUE = 4, // rescue
12621 PM_BINDING_POWER_MODIFIER = 6, // if unless until while
12622 PM_BINDING_POWER_COMPOSITION = 8, // and or
12623 PM_BINDING_POWER_NOT = 10, // not
12624 PM_BINDING_POWER_MATCH = 12, // => in
12625 PM_BINDING_POWER_DEFINED = 14, // defined?
12626 PM_BINDING_POWER_MULTI_ASSIGNMENT = 16, // =
12627 PM_BINDING_POWER_ASSIGNMENT = 18, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **=
12628 PM_BINDING_POWER_TERNARY = 20, // ?:
12629 PM_BINDING_POWER_RANGE = 22, // .. ...
12630 PM_BINDING_POWER_LOGICAL_OR = 24, // ||
12631 PM_BINDING_POWER_LOGICAL_AND = 26, // &&
12632 PM_BINDING_POWER_EQUALITY = 28, // <=> == === != =~ !~
12633 PM_BINDING_POWER_COMPARISON = 30, // > >= < <=
12634 PM_BINDING_POWER_BITWISE_OR = 32, // | ^
12635 PM_BINDING_POWER_BITWISE_AND = 34, // &
12636 PM_BINDING_POWER_SHIFT = 36, // << >>
12637 PM_BINDING_POWER_TERM = 38, // + -
12638 PM_BINDING_POWER_FACTOR = 40, // * / %
12639 PM_BINDING_POWER_UMINUS = 42, // -@
12640 PM_BINDING_POWER_EXPONENT = 44, // **
12641 PM_BINDING_POWER_UNARY = 46, // ! ~ +@
12642 PM_BINDING_POWER_INDEX = 48, // [] []=
12643 PM_BINDING_POWER_CALL = 50, // :: .
12644 PM_BINDING_POWER_MAX = 52
12645} pm_binding_power_t;
12646
12651typedef struct {
12653 pm_binding_power_t left;
12654
12656 pm_binding_power_t right;
12657
12660
12667
12668#define BINDING_POWER_ASSIGNMENT { PM_BINDING_POWER_UNARY, PM_BINDING_POWER_ASSIGNMENT, true, false }
12669#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, false }
12670#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true, false }
12671#define NON_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, true }
12672#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false }
12673
12674pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
12675 // rescue
12676 [PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = { PM_BINDING_POWER_MODIFIER_RESCUE, PM_BINDING_POWER_COMPOSITION, true, false },
12677
12678 // if unless until while
12679 [PM_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12680 [PM_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12681 [PM_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12682 [PM_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12683
12684 // and or
12685 [PM_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12686 [PM_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12687
12688 // => in
12689 [PM_TOKEN_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12690 [PM_TOKEN_KEYWORD_IN] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12691
12692 // &&= &= ^= = >>= <<= -= %= |= ||= += /= *= **=
12693 [PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12694 [PM_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12695 [PM_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT,
12696 [PM_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT,
12697 [PM_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT,
12698 [PM_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12699 [PM_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12700 [PM_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT,
12701 [PM_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12702 [PM_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12703 [PM_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12704 [PM_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT,
12705 [PM_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12706 [PM_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12707
12708 // ?:
12709 [PM_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_TERNARY),
12710
12711 // .. ...
12712 [PM_TOKEN_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12713 [PM_TOKEN_DOT_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12714 [PM_TOKEN_UDOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12715 [PM_TOKEN_UDOT_DOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12716
12717 // ||
12718 [PM_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_OR),
12719
12720 // &&
12721 [PM_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_AND),
12722
12723 // != !~ == === =~ <=>
12724 [PM_TOKEN_BANG_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12725 [PM_TOKEN_BANG_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12726 [PM_TOKEN_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12727 [PM_TOKEN_EQUAL_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12728 [PM_TOKEN_EQUAL_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12729 [PM_TOKEN_LESS_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12730
12731 // > >= < <=
12732 [PM_TOKEN_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12733 [PM_TOKEN_GREATER_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12734 [PM_TOKEN_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12735 [PM_TOKEN_LESS_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12736
12737 // ^ |
12738 [PM_TOKEN_CARET] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12739 [PM_TOKEN_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12740
12741 // &
12742 [PM_TOKEN_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_AND),
12743
12744 // >> <<
12745 [PM_TOKEN_GREATER_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12746 [PM_TOKEN_LESS_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12747
12748 // - +
12749 [PM_TOKEN_MINUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12750 [PM_TOKEN_PLUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12751
12752 // % / *
12753 [PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12754 [PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12755 [PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12756 [PM_TOKEN_USTAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_FACTOR),
12757
12758 // -@
12759 [PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS),
12760 [PM_TOKEN_UMINUS_NUM] = { PM_BINDING_POWER_UMINUS, PM_BINDING_POWER_MAX, false, false },
12761
12762 // **
12763 [PM_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_EXPONENT),
12764 [PM_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12765
12766 // ! ~ +@
12767 [PM_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12768 [PM_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12769 [PM_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12770
12771 // [
12772 [PM_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_INDEX),
12773
12774 // :: . &.
12775 [PM_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12776 [PM_TOKEN_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12777 [PM_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL)
12778};
12779
12780#undef BINDING_POWER_ASSIGNMENT
12781#undef LEFT_ASSOCIATIVE
12782#undef RIGHT_ASSOCIATIVE
12783#undef RIGHT_ASSOCIATIVE_UNARY
12784
12788static PRISM_INLINE bool
12789match1(const pm_parser_t *parser, pm_token_type_t type) {
12790 return parser->current.type == type;
12791}
12792
12796static PRISM_INLINE bool
12797match2(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12798 return match1(parser, type1) || match1(parser, type2);
12799}
12800
12804static PRISM_INLINE bool
12805match3(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) {
12806 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3);
12807}
12808
12812static PRISM_INLINE bool
12813match4(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) {
12814 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4);
12815}
12816
12820static PRISM_INLINE bool
12821match5(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) {
12822 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5);
12823}
12824
12828static PRISM_INLINE bool
12829match6(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) {
12830 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6);
12831}
12832
12836static PRISM_INLINE bool
12837match8(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) {
12838 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);
12839}
12840
12847static bool
12848accept1(pm_parser_t *parser, pm_token_type_t type) {
12849 if (match1(parser, type)) {
12850 parser_lex(parser);
12851 return true;
12852 }
12853 return false;
12854}
12855
12860static PRISM_INLINE bool
12861accept2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12862 if (match2(parser, type1, type2)) {
12863 parser_lex(parser);
12864 return true;
12865 }
12866 return false;
12867}
12868
12880static void
12881expect1(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id) {
12882 if (accept1(parser, type)) return;
12883
12884 const uint8_t *location = parser->previous.end;
12885 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12886
12887 parser->previous.start = location;
12888 parser->previous.type = 0;
12889}
12890
12895static void
12896expect2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_diagnostic_id_t diag_id) {
12897 if (accept2(parser, type1, type2)) return;
12898
12899 const uint8_t *location = parser->previous.end;
12900 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12901
12902 parser->previous.start = location;
12903 parser->previous.type = 0;
12904}
12905
12910static void
12911expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
12912 if (match1(parser, PM_TOKEN_HEREDOC_END)) {
12913 parser_lex(parser);
12914 } else {
12915 pm_parser_err_heredoc_term(parser, ident_start, ident_length);
12916 parser->previous.start = parser->previous.end;
12917 parser->previous.type = 0;
12918 }
12919}
12920
12927static void
12928expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id, const pm_token_t *opening) {
12929 if (accept1(parser, type)) return;
12930
12931 const uint8_t *start = opening->start;
12932 pm_parser_err(parser, U32(start - parser->start), U32(opening->end - start), diag_id);
12933
12934 parser->previous.start = parser->previous.end;
12935 parser->previous.type = 0;
12936}
12937
12939#define PM_PARSE_ACCEPTS_COMMAND_CALL ((uint8_t) 0x1)
12940#define PM_PARSE_ACCEPTS_LABEL ((uint8_t) 0x2)
12941#define PM_PARSE_ACCEPTS_DO_BLOCK ((uint8_t) 0x4)
12942#define PM_PARSE_IN_ENDLESS_DEF ((uint8_t) 0x8)
12943
12953#define PM_PARSE_ACCEPTS_STATEMENT ((uint8_t) 0x10)
12954
12955static pm_node_t *
12956parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
12957
12962static pm_node_t *
12963parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
12964 pm_node_t *node = parse_expression(parser, binding_power, flags, diag_id, depth);
12965 pm_assert_value_expression(parser, node);
12966 return node;
12967}
12968
12987static PRISM_INLINE bool
12988token_begins_expression_p(pm_token_type_t type) {
12989 switch (type) {
12990 case PM_TOKEN_EQUAL_GREATER:
12991 case PM_TOKEN_KEYWORD_IN:
12992 // We need to special case this because it is a binary operator that
12993 // should not be marked as beginning an expression.
12994 return false;
12995 case PM_TOKEN_BRACE_RIGHT:
12996 case PM_TOKEN_BRACKET_RIGHT:
12997 case PM_TOKEN_COLON:
12998 case PM_TOKEN_COMMA:
12999 case PM_TOKEN_EMBEXPR_END:
13000 case PM_TOKEN_EOF:
13001 case PM_TOKEN_LAMBDA_BEGIN:
13002 case PM_TOKEN_KEYWORD_DO:
13003 case PM_TOKEN_KEYWORD_DO_BLOCK:
13004 case PM_TOKEN_KEYWORD_DO_LAMBDA:
13005 case PM_TOKEN_KEYWORD_DO_LOOP:
13006 case PM_TOKEN_KEYWORD_END:
13007 case PM_TOKEN_KEYWORD_ELSE:
13008 case PM_TOKEN_KEYWORD_ELSIF:
13009 case PM_TOKEN_KEYWORD_ENSURE:
13010 case PM_TOKEN_KEYWORD_THEN:
13011 case PM_TOKEN_KEYWORD_RESCUE:
13012 case PM_TOKEN_KEYWORD_WHEN:
13013 case PM_TOKEN_NEWLINE:
13014 case PM_TOKEN_PARENTHESIS_RIGHT:
13015 case PM_TOKEN_SEMICOLON:
13016 // The reason we need this short-circuit is because we're using the
13017 // binding powers table to tell us if the subsequent token could
13018 // potentially be the start of an expression. If there _is_ a binding
13019 // power for one of these tokens, then we should remove it from this list
13020 // and let it be handled by the default case below.
13021 assert(pm_binding_powers[type].left == PM_BINDING_POWER_UNSET);
13022 return false;
13023 case PM_TOKEN_UAMPERSAND:
13024 // This is a special case because this unary operator cannot appear
13025 // as a general operator, it only appears in certain circumstances.
13026 return false;
13027 case PM_TOKEN_UCOLON_COLON:
13028 case PM_TOKEN_UMINUS:
13029 case PM_TOKEN_UMINUS_NUM:
13030 case PM_TOKEN_UPLUS:
13031 case PM_TOKEN_BANG:
13032 case PM_TOKEN_TILDE:
13033 case PM_TOKEN_UDOT_DOT:
13034 case PM_TOKEN_UDOT_DOT_DOT:
13035 // These unary tokens actually do have binding power associated with them
13036 // so that we can correctly place them into the precedence order. But we
13037 // want them to be marked as beginning an expression, so we need to
13038 // special case them here.
13039 return true;
13040 default:
13041 return pm_binding_powers[type].left == PM_BINDING_POWER_UNSET;
13042 }
13043}
13044
13060static PRISM_INLINE bool
13061token_begins_pattern_p(pm_token_type_t type) {
13062 return (
13063 token_begins_expression_p(type) ||
13064 type == PM_TOKEN_USTAR ||
13065 type == PM_TOKEN_USTAR_STAR ||
13066 type == PM_TOKEN_CARET
13067 );
13068}
13069
13074static pm_node_t *
13075parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
13076 if (accept1(parser, PM_TOKEN_USTAR)) {
13077 pm_token_t operator = parser->previous;
13078 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));
13079 return UP(pm_splat_node_create(parser, &operator, expression));
13080 }
13081
13082 return parse_value_expression(parser, binding_power, flags, diag_id, depth);
13083}
13084
13085static bool
13086pm_node_unreference_each(const pm_node_t *node, void *data) {
13087 switch (PM_NODE_TYPE(node)) {
13088 /* When we are about to destroy a set of nodes that could potentially
13089 * contain block exits for the current scope, we need to check if they
13090 * are contained in the list of block exits and remove them if they are.
13091 */
13092 case PM_BREAK_NODE:
13093 case PM_NEXT_NODE:
13094 case PM_REDO_NODE: {
13095 pm_parser_t *parser = (pm_parser_t *) data;
13096 size_t index = 0;
13097
13098 while (index < parser->current_block_exits->size) {
13099 pm_node_t *block_exit = parser->current_block_exits->nodes[index];
13100
13101 if (block_exit == node) {
13102 if (index + 1 < parser->current_block_exits->size) {
13103 memmove(
13104 &parser->current_block_exits->nodes[index],
13105 &parser->current_block_exits->nodes[index + 1],
13106 (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *)
13107 );
13108 }
13109 parser->current_block_exits->size--;
13110
13111 /* Note returning true here because these nodes could have
13112 * arguments that are themselves block exits. */
13113 return true;
13114 }
13115
13116 index++;
13117 }
13118
13119 return true;
13120 }
13121 /* When an implicit local variable is written to or targeted, it becomes
13122 * a regular, named local variable. This branch removes it from the list
13123 * of implicit parameters when that happens. */
13124 case PM_LOCAL_VARIABLE_READ_NODE:
13125 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13126 pm_parser_t *parser = (pm_parser_t *) data;
13127 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
13128
13129 for (size_t index = 0; index < implicit_parameters->size; index++) {
13130 if (implicit_parameters->nodes[index] == node) {
13131 /* If the node is not the last one in the list, we need to
13132 * shift the remaining nodes down to fill the gap. This is
13133 * extremely unlikely to happen. */
13134 if (index != implicit_parameters->size - 1) {
13135 memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *));
13136 }
13137
13138 implicit_parameters->size--;
13139 break;
13140 }
13141 }
13142
13143 return false;
13144 }
13145 default:
13146 return true;
13147 }
13148}
13149
13155static void
13156pm_node_unreference(pm_parser_t *parser, const pm_node_t *node) {
13157 pm_visit_node(node, pm_node_unreference_each, parser);
13158}
13159
13164static void
13165parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) {
13166 // The method name needs to change. If we previously had
13167 // foo, we now need foo=. In this case we'll allocate a new
13168 // owned string, copy the previous method name in, and
13169 // append an =.
13170 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *name_field);
13171 size_t length = constant->length;
13172 uint8_t *name = (uint8_t *) pm_arena_alloc(parser->arena, length + 1, 1);
13173
13174 memcpy(name, constant->start, length);
13175 name[length] = '=';
13176
13177 *name_field = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, name, length + 1);
13178}
13179
13186static pm_node_t *
13187parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) {
13188 switch (PM_NODE_TYPE(target)) {
13189 case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13190 case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13191 case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13192 case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13193 case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13194 case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13195 case PM_TRUE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13196 default: break;
13197 }
13198
13199 pm_constant_id_t name = pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target));
13200 pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0);
13201
13202 return UP(result);
13203}
13204
13213static pm_node_t *
13214parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_parent) {
13215 switch (PM_NODE_TYPE(target)) {
13216 case PM_ERROR_RECOVERY_NODE:
13217 return target;
13218 case PM_SOURCE_ENCODING_NODE:
13219 case PM_FALSE_NODE:
13220 case PM_SOURCE_FILE_NODE:
13221 case PM_SOURCE_LINE_NODE:
13222 case PM_NIL_NODE:
13223 case PM_SELF_NODE:
13224 case PM_TRUE_NODE: {
13225 // In these special cases, we have specific error messages and we
13226 // will replace them with local variable writes.
13227 return parse_unwriteable_target(parser, target);
13228 }
13229 case PM_CLASS_VARIABLE_READ_NODE:
13231 target->type = PM_CLASS_VARIABLE_TARGET_NODE;
13232 return target;
13233 case PM_CONSTANT_PATH_NODE:
13234 if (context_def_p(parser)) {
13235 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13236 }
13237
13239 target->type = PM_CONSTANT_PATH_TARGET_NODE;
13240
13241 return target;
13242 case PM_CONSTANT_READ_NODE:
13243 if (context_def_p(parser)) {
13244 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13245 }
13246
13247 assert(sizeof(pm_constant_target_node_t) == sizeof(pm_constant_read_node_t));
13248 target->type = PM_CONSTANT_TARGET_NODE;
13249
13250 return target;
13251 case PM_BACK_REFERENCE_READ_NODE:
13252 case PM_NUMBERED_REFERENCE_READ_NODE:
13253 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13254 return UP(pm_error_recovery_node_create_unexpected(parser, target));
13255 case PM_GLOBAL_VARIABLE_READ_NODE:
13257 target->type = PM_GLOBAL_VARIABLE_TARGET_NODE;
13258 return target;
13259 case PM_LOCAL_VARIABLE_READ_NODE: {
13260 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13261 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(target));
13262 pm_node_unreference(parser, target);
13263 }
13264
13265 const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target;
13266 uint32_t name = cast->name;
13267 uint32_t depth = cast->depth;
13268 pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name);
13269
13271 target->type = PM_LOCAL_VARIABLE_TARGET_NODE;
13272
13273 return target;
13274 }
13275 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13276 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13277 pm_node_t *node = UP(pm_local_variable_target_node_create(parser, &target->location, name, 0));
13278
13279 pm_node_unreference(parser, target);
13280
13281 return node;
13282 }
13283 case PM_INSTANCE_VARIABLE_READ_NODE:
13285 target->type = PM_INSTANCE_VARIABLE_TARGET_NODE;
13286 return target;
13287 case PM_MULTI_TARGET_NODE:
13288 if (splat_parent) {
13289 // Multi target is not accepted in all positions. If this is one
13290 // of them, then we need to add an error.
13291 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13292 }
13293
13294 return target;
13295 case PM_SPLAT_NODE: {
13296 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13297
13298 if (splat->expression != NULL) {
13299 splat->expression = parse_target(parser, splat->expression, multiple, true);
13300 }
13301
13302 return UP(splat);
13303 }
13304 case PM_CALL_NODE: {
13305 pm_call_node_t *call = (pm_call_node_t *) target;
13306
13307 // If we have no arguments to the call node and we need this to be a
13308 // target then this is either a method call or a local variable
13309 // write.
13310 if (
13311 (call->message_loc.length > 0) &&
13312 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13313 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13314 (call->opening_loc.length == 0) &&
13315 (call->arguments == NULL) &&
13316 (call->block == NULL)
13317 ) {
13318 if (call->receiver == NULL) {
13319 // When we get here, we have a local variable write, because it
13320 // was previously marked as a method call but now we have an =.
13321 // This looks like:
13322 //
13323 // foo = 1
13324 //
13325 // When it was parsed in the prefix position, foo was seen as a
13326 // method call with no receiver and no arguments. Now we have an
13327 // =, so we know it's a local variable write.
13328 pm_location_t message_loc = call->message_loc;
13329 pm_constant_id_t name = pm_parser_local_add_location(parser, &message_loc, 0);
13330
13331 return UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0));
13332 }
13333
13334 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)) {
13335 if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
13336 pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION);
13337 }
13338
13339 parse_write_name(parser, &call->name);
13340 return UP(pm_call_target_node_create(parser, call));
13341 }
13342 }
13343
13344 // If there is no call operator and the message is "[]" then this is
13345 // an aref expression, and we can transform it into an aset
13346 // expression.
13347 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13348 return UP(pm_index_target_node_create(parser, call));
13349 }
13350 }
13352 default:
13353 // In this case we have a node that we don't know how to convert
13354 // into a target. We need to treat it as an error. For now, we'll
13355 // mark it as an error and just skip right past it.
13356 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13357 return target;
13358 }
13359}
13360
13365static pm_node_t *
13366parse_target_validate(pm_parser_t *parser, pm_node_t *target, bool multiple) {
13367 pm_node_t *result = parse_target(parser, target, multiple, false);
13368
13369 // Ensure that we have one of an =, an 'in' in for indexes, and a ')' in
13370 // parens after the targets.
13371 if (
13372 !match1(parser, PM_TOKEN_EQUAL) &&
13373 !(context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) &&
13374 !(context_p(parser, PM_CONTEXT_PARENS) && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT))
13375 ) {
13376 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13377 }
13378
13379 return result;
13380}
13381
13386static pm_node_t *
13387parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) {
13388 pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser);
13389
13390 if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) {
13391 return UP(pm_shareable_constant_node_create(parser, write, shareable_constant));
13392 }
13393
13394 return write;
13395}
13396
13400static pm_node_t *
13401parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_node_t *value) {
13402 switch (PM_NODE_TYPE(target)) {
13403 case PM_ERROR_RECOVERY_NODE:
13404 return target;
13405 case PM_CLASS_VARIABLE_READ_NODE: {
13406 pm_class_variable_write_node_t *node = pm_class_variable_write_node_create(parser, (pm_class_variable_read_node_t *) target, operator, value);
13407 return UP(node);
13408 }
13409 case PM_CONSTANT_PATH_NODE: {
13410 pm_node_t *node = UP(pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value));
13411
13412 if (context_def_p(parser)) {
13413 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13414 }
13415
13416 return parse_shareable_constant_write(parser, node);
13417 }
13418 case PM_CONSTANT_READ_NODE: {
13419 pm_node_t *node = UP(pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value));
13420
13421 if (context_def_p(parser)) {
13422 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13423 }
13424
13425 return parse_shareable_constant_write(parser, node);
13426 }
13427 case PM_BACK_REFERENCE_READ_NODE:
13428 case PM_NUMBERED_REFERENCE_READ_NODE:
13429 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13431 case PM_GLOBAL_VARIABLE_READ_NODE: {
13432 pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value);
13433 return UP(node);
13434 }
13435 case PM_LOCAL_VARIABLE_READ_NODE: {
13437
13438 pm_location_t location = target->location;
13439 pm_constant_id_t name = local_read->name;
13440 uint32_t depth = local_read->depth;
13441 pm_scope_t *scope = pm_parser_scope_find(parser, depth);
13442
13443 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13444 pm_diagnostic_id_t diag_id = (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED;
13445 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), diag_id, parser->start + PM_NODE_START(target));
13446 pm_node_unreference(parser, target);
13447 }
13448
13449 pm_locals_unread(&scope->locals, name);
13450
13451 return UP(pm_local_variable_write_node_create(parser, name, depth, value, &location, operator));
13452 }
13453 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13454 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13455 pm_node_t *node = UP(pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator));
13456
13457 pm_node_unreference(parser, target);
13458
13459 return node;
13460 }
13461 case PM_INSTANCE_VARIABLE_READ_NODE: {
13462 pm_node_t *write_node = UP(pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value));
13463 return write_node;
13464 }
13465 case PM_MULTI_TARGET_NODE:
13466 return UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value));
13467 case PM_SPLAT_NODE: {
13468 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13469
13470 if (splat->expression != NULL) {
13471 splat->expression = parse_write(parser, splat->expression, operator, value);
13472 }
13473
13474 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
13475 pm_multi_target_node_targets_append(parser, multi_target, UP(splat));
13476
13477 return UP(pm_multi_write_node_create(parser, multi_target, operator, value));
13478 }
13479 case PM_CALL_NODE: {
13480 pm_call_node_t *call = (pm_call_node_t *) target;
13481
13482 // If we have no arguments to the call node and we need this to be a
13483 // target then this is either a method call or a local variable
13484 // write.
13485 if (
13486 (call->message_loc.length > 0) &&
13487 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13488 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13489 (call->opening_loc.length == 0) &&
13490 (call->arguments == NULL) &&
13491 (call->block == NULL)
13492 ) {
13493 if (call->receiver == NULL) {
13494 // When we get here, we have a local variable write, because it
13495 // was previously marked as a method call but now we have an =.
13496 // This looks like:
13497 //
13498 // foo = 1
13499 //
13500 // When it was parsed in the prefix position, foo was seen as a
13501 // method call with no receiver and no arguments. Now we have an
13502 // =, so we know it's a local variable write.
13503 pm_location_t message_loc = call->message_loc;
13504
13505 pm_refute_numbered_parameter(parser, message_loc.start, message_loc.length);
13506 pm_parser_local_add_location(parser, &message_loc, 0);
13507
13508 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));
13509 target = UP(pm_local_variable_write_node_create(parser, constant_id, 0, value, &message_loc, operator));
13510
13511 return target;
13512 }
13513
13514 if (char_is_identifier_start(parser, parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)) {
13515 // When we get here, we have a method call, because it was
13516 // previously marked as a method call but now we have an =. This
13517 // looks like:
13518 //
13519 // foo.bar = 1
13520 //
13521 // When it was parsed in the prefix position, foo.bar was seen as a
13522 // method call with no arguments. Now we have an =, so we know it's
13523 // a method call with an argument. In this case we will create the
13524 // arguments node, parse the argument, and add it to the list.
13525 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
13526 call->arguments = arguments;
13527
13528 pm_arguments_node_arguments_append(parser->arena, arguments, value);
13529 PM_NODE_LENGTH_SET_NODE(call, arguments);
13530 call->equal_loc = TOK2LOC(parser, operator);
13531
13532 parse_write_name(parser, &call->name);
13533 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13534
13535 return UP(call);
13536 }
13537 }
13538
13539 // If there is no call operator and the message is "[]" then this is
13540 // an aref expression, and we can transform it into an aset
13541 // expression.
13542 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13543 if (call->arguments == NULL) {
13544 call->arguments = pm_arguments_node_create(parser);
13545 }
13546
13547 pm_arguments_node_arguments_append(parser->arena, call->arguments, value);
13548 PM_NODE_LENGTH_SET_NODE(target, value);
13549
13550 // Replace the name with "[]=".
13551 call->name = pm_parser_constant_id_constant(parser, "[]=", 3);
13552 call->equal_loc = TOK2LOC(parser, operator);
13553
13554 // Ensure that the arguments for []= don't contain keywords
13555 pm_index_arguments_check(parser, call->arguments, call->block);
13556 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13557
13558 return target;
13559 }
13560
13561 // If there are arguments on the call node, then it can't be a
13562 // method call ending with = or a local variable write, so it must
13563 // be a syntax error. In this case we'll fall through to our default
13564 // handling. We need to free the value that we parsed because there
13565 // is no way for us to attach it to the tree at this point.
13566 //
13567 // Since it is possible for the value to contain an implicit
13568 // parameter somewhere in its subtree, we need to walk it and remove
13569 // any implicit parameters from the list of implicit parameters for
13570 // the current scope.
13571 pm_node_unreference(parser, value);
13572 }
13574 default:
13575 // In this case we have a node that we don't know how to convert into a
13576 // target. We need to treat it as an error. For now, we'll mark it as an
13577 // error and just skip right past it.
13578 pm_parser_err_token(parser, operator, PM_ERR_WRITE_TARGET_UNEXPECTED);
13579 return target;
13580 }
13581}
13582
13589static pm_node_t *
13590parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) {
13591 switch (PM_NODE_TYPE(target)) {
13592 case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13593 case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13594 case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13595 case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13596 case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13597 case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13598 case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13599 default: break;
13600 }
13601
13602 pm_constant_id_t name = pm_parser_local_add_location(parser, &target->location, 1);
13603 pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals);
13604
13605 return UP(result);
13606}
13607
13618static pm_node_t *
13619parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13620 bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE);
13621
13622 pm_multi_target_node_t *result = pm_multi_target_node_create(parser);
13623 pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target, true, false));
13624
13625 while (accept1(parser, PM_TOKEN_COMMA)) {
13626 if (accept1(parser, PM_TOKEN_USTAR)) {
13627 // Here we have a splat operator. It can have a name or be
13628 // anonymous. It can be the final target or be in the middle if
13629 // there haven't been any others yet.
13630 if (has_rest) {
13631 pm_parser_err_previous(parser, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
13632 }
13633
13634 pm_token_t star_operator = parser->previous;
13635 pm_node_t *name = NULL;
13636
13637 if (token_begins_expression_p(parser->current.type)) {
13638 name = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
13639 name = parse_target(parser, name, true, true);
13640 }
13641
13642 pm_node_t *splat = UP(pm_splat_node_create(parser, &star_operator, name));
13643 pm_multi_target_node_targets_append(parser, result, splat);
13644 has_rest = true;
13645 } else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
13646 context_push(parser, PM_CONTEXT_MULTI_TARGET);
13647 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13648 target = parse_target(parser, target, true, false);
13649
13650 pm_multi_target_node_targets_append(parser, result, target);
13651 context_pop(parser);
13652 } else if (token_begins_expression_p(parser->current.type)) {
13653 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13654 target = parse_target(parser, target, true, false);
13655
13656 pm_multi_target_node_targets_append(parser, result, target);
13657 } else if (!match1(parser, PM_TOKEN_EOF)) {
13658 // If we get here, then we have a trailing , in a multi target node.
13659 // We'll add an implicit rest node to represent this.
13660 pm_node_t *rest = UP(pm_implicit_rest_node_create(parser, &parser->previous));
13661 pm_multi_target_node_targets_append(parser, result, rest);
13662 break;
13663 }
13664 }
13665
13666 return UP(result);
13667}
13668
13673static pm_node_t *
13674parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13675 pm_node_t *result = parse_targets(parser, first_target, binding_power, depth);
13676
13677 // If we're inside parentheses, then we allow a newline before the
13678 // closing parenthesis or equals sign. Outside of parentheses, a newline
13679 // is not allowed (e.g., `a, b\n= 1, 2` is not valid).
13680 if (context_p(parser, PM_CONTEXT_PARENS) || context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
13681 accept1(parser, PM_TOKEN_NEWLINE);
13682 }
13683
13684 // Ensure that we have either an = or a ) after the targets.
13685 if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
13686 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13687 }
13688
13689 return result;
13690}
13691
13695static pm_statements_node_t *
13696parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
13697 // First, skip past any optional terminators that might be at the beginning
13698 // of the statements.
13699 while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE));
13700
13701 // If we have a terminator, then we can just return NULL.
13702 if (context_terminator(context, &parser->current)) return NULL;
13703
13704 pm_statements_node_t *statements = pm_statements_node_create(parser);
13705
13706 // At this point we know we have at least one statement, and that it
13707 // immediately follows the current token.
13708 context_push(parser, context);
13709
13710 while (true) {
13711 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
13712 pm_statements_node_body_append(parser, statements, node, true);
13713
13714 // If we're recovering from a syntax error, then we need to stop parsing
13715 // the statements now.
13716 if (parser->recovering) {
13717 // If this is the level of context where the recovery has happened,
13718 // then we can mark the parser as done recovering.
13719 if (context_terminator(context, &parser->current)) parser->recovering = false;
13720 break;
13721 }
13722
13723 // If we have a terminator, then we will parse all consecutive
13724 // terminators and then continue parsing the statements list.
13725 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
13726 // If we have a terminator, then we will continue parsing the
13727 // statements list.
13728 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13729 if (context_terminator(context, &parser->current)) break;
13730
13731 // Now we can continue parsing the list of statements.
13732 continue;
13733 }
13734
13735 // At this point we have a list of statements that are not terminated by
13736 // a newline or semicolon. At this point we need to check if we're at
13737 // the end of the statements list. If we are, then we should break out
13738 // of the loop.
13739 if (context_terminator(context, &parser->current)) break;
13740
13741 // At this point, we have a syntax error, because the statement was not
13742 // terminated by a newline or semicolon, and we're not at the end of the
13743 // statements list. Ideally we should scan forward to determine if we
13744 // should insert a missing terminator or break out of parsing the
13745 // statements list at this point.
13746 //
13747 // We don't have that yet, so instead we'll do a more naive approach. If
13748 // we were unable to parse an expression, then we will skip past this
13749 // token and continue parsing the statements list. Otherwise we'll add
13750 // an error and continue parsing the statements list.
13751 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) {
13752 parser_lex(parser);
13753
13754 // If we are at the end of the file, then we need to stop parsing
13755 // the statements entirely at this point. Mark the parser as
13756 // recovering, as we know that EOF closes the top-level context, and
13757 // then break out of the loop.
13758 if (match1(parser, PM_TOKEN_EOF)) {
13759 parser->recovering = true;
13760 break;
13761 }
13762
13763 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13764 if (context_terminator(context, &parser->current)) break;
13765 } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) {
13766 // This is an inlined version of accept1 because the error that we
13767 // want to add has varargs. If this happens again, we should
13768 // probably extract a helper function.
13769 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
13770 parser->previous.start = parser->previous.end;
13771 parser->previous.type = 0;
13772 }
13773 }
13774
13775 context_pop(parser);
13776
13777 bool last_value = true;
13778 switch (context) {
13779 case PM_CONTEXT_BEGIN_ENSURE:
13780 case PM_CONTEXT_DEF_ENSURE:
13781 last_value = false;
13782 break;
13783 default:
13784 break;
13785 }
13786 pm_void_statements_check(parser, statements, last_value);
13787
13788 return statements;
13789}
13790
13794static void
13795pm_hash_key_duplicated_warn(pm_parser_t *parser, const pm_node_t *duplicated, const pm_node_t *node) {
13796 pm_buffer_t buffer = { 0 };
13797 pm_static_literal_inspect(&buffer, &parser->line_offsets, parser->start, parser->start_line, parser->encoding, duplicated);
13798
13799 pm_diagnostic_list_append_format(
13800 &parser->metadata_arena,
13801 &parser->warning_list,
13802 duplicated->location.start,
13803 duplicated->location.length,
13804 PM_WARN_DUPLICATED_HASH_KEY,
13805 (int) pm_buffer_length(&buffer),
13806 pm_buffer_value(&buffer),
13807 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line
13808 );
13809
13810 pm_buffer_cleanup(&buffer);
13811}
13812
13817static void
13818pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13819 const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, true);
13820
13821 if (duplicated != NULL) {
13822 pm_hash_key_duplicated_warn(parser, duplicated, node);
13823 }
13824}
13825
13833static void
13834pm_hash_key_static_literals_merge(pm_parser_t *parser, pm_static_literals_t *literals, const pm_hash_node_t *hash, uint32_t boundary) {
13835 const pm_node_list_t *elements = &hash->elements;
13836
13837 for (size_t index = 0; index < elements->size; index++) {
13838 pm_node_t *element = elements->nodes[index];
13839
13840 switch (PM_NODE_TYPE(element)) {
13841 case PM_ASSOC_NODE: {
13842 pm_node_t *key = ((pm_assoc_node_t *) element)->key;
13843 const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, key, true);
13844
13845 if (duplicated != NULL && PM_NODE_START(duplicated) < boundary) {
13846 pm_hash_key_duplicated_warn(parser, duplicated, key);
13847 }
13848
13849 break;
13850 }
13851 case PM_ASSOC_SPLAT_NODE: {
13852 const pm_node_t *value = ((pm_assoc_splat_node_t *) element)->value;
13853
13854 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
13855 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, boundary);
13856 }
13857
13858 break;
13859 }
13860 default:
13861 break;
13862 }
13863 }
13864}
13865
13870static void
13871pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13872 pm_node_t *previous;
13873
13874 if ((previous = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, false)) != NULL) {
13875 pm_diagnostic_list_append_format(
13876 &parser->metadata_arena,
13877 &parser->warning_list,
13878 PM_NODE_START(node),
13879 PM_NODE_LENGTH(node),
13880 PM_WARN_DUPLICATED_WHEN_CLAUSE,
13881 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line,
13882 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(previous), parser->start_line).line
13883 );
13884 }
13885}
13886
13890static bool
13891parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node, uint16_t depth) {
13892 assert(PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE));
13893 bool contains_keyword_splat = false;
13894
13895 while (true) {
13896 pm_node_t *element;
13897
13898 switch (parser->current.type) {
13899 case PM_TOKEN_USTAR_STAR: {
13900 parser_lex(parser);
13901 pm_token_t operator = parser->previous;
13902 pm_node_t *value = NULL;
13903
13904 if (token_begins_expression_p(parser->current.type)) {
13905 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));
13906
13907 /* If the splatted value is itself a hash literal, its keys
13908 * become part of this hash for the duplicate key warning. */
13909 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
13910 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, PM_NODE_START(value));
13911 }
13912 } else {
13913 pm_parser_scope_forwarding_keywords_check(parser, &operator);
13914 }
13915
13916 element = UP(pm_assoc_splat_node_create(parser, value, &operator));
13917 contains_keyword_splat = true;
13918 break;
13919 }
13920 case PM_TOKEN_LABEL: {
13921 pm_token_t label = parser->current;
13922 parser_lex(parser);
13923
13924 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &label));
13925 pm_hash_key_static_literals_add(parser, literals, key);
13926
13927 pm_node_t *value = NULL;
13928
13929 if (token_begins_expression_p(parser->current.type)) {
13930 value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_EXPRESSION_AFTER_LABEL, (uint16_t) (depth + 1));
13931 } else {
13932 if (parser->encoding->isupper_char(label.start, (label.end - 1) - label.start)) {
13933 pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 };
13934 value = UP(pm_constant_read_node_create(parser, &constant));
13935 } else {
13936 int depth = -1;
13937 pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 };
13938
13939 if (identifier.end[-1] == '!' || identifier.end[-1] == '?') {
13940 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ);
13941 } else {
13942 depth = pm_parser_local_depth(parser, &identifier);
13943 }
13944
13945 if (depth == -1) {
13946 value = UP(pm_call_node_variable_call_create(parser, &identifier));
13947 } else {
13948 value = UP(pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth));
13949 }
13950 }
13951
13952 value->location.length++;
13953 value = UP(pm_implicit_node_create(parser, value));
13954 }
13955
13956 element = UP(pm_assoc_node_create(parser, key, NULL, value));
13957 break;
13958 }
13959 default: {
13960 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));
13961
13962 // Hash keys that are strings are automatically frozen. We will
13963 // mark that here.
13964 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
13965 pm_node_flag_set(key, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
13966 }
13967
13968 pm_hash_key_static_literals_add(parser, literals, key);
13969
13970 pm_token_t operator = { 0 };
13971 if (!pm_symbol_node_label_p(parser, key)) {
13972 expect1(parser, PM_TOKEN_EQUAL_GREATER, PM_ERR_HASH_ROCKET);
13973 operator = parser->previous;
13974 }
13975
13976 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));
13977 element = UP(pm_assoc_node_create(parser, key, NTOK2PTR(operator), value));
13978 break;
13979 }
13980 }
13981
13982 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
13983 pm_hash_node_elements_append(parser->arena, (pm_hash_node_t *) node, element);
13984 } else {
13985 pm_keyword_hash_node_elements_append(parser->arena, (pm_keyword_hash_node_t *) node, element);
13986 }
13987
13988 // If there's no comma after the element, then we're done.
13989 if (!accept1(parser, PM_TOKEN_COMMA)) break;
13990
13991 // If the next element starts with a label or a **, then we know we have
13992 // another element in the hash, so we'll continue parsing.
13993 if (match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)) continue;
13994
13995 // Otherwise we need to check if the subsequent token begins an expression.
13996 // If it does, then we'll continue parsing.
13997 if (token_begins_expression_p(parser->current.type)) continue;
13998
13999 // Otherwise by default we will exit out of this loop.
14000 break;
14001 }
14002
14003 return contains_keyword_splat;
14004}
14005
14006static PRISM_INLINE bool
14007argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) {
14008 if (pm_symbol_node_label_p(parser, argument)) {
14009 return true;
14010 }
14011
14012 switch (PM_NODE_TYPE(argument)) {
14013 case PM_CALL_NODE: {
14014 pm_call_node_t *cast = (pm_call_node_t *) argument;
14015 if (cast->opening_loc.length == 0 && cast->arguments != NULL) {
14016 if (PM_NODE_FLAG_P(cast->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS | PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
14017 return false;
14018 }
14019 if (cast->block != NULL) {
14020 return false;
14021 }
14022 }
14023 break;
14024 }
14025 default: break;
14026 }
14027 return accept1(parser, PM_TOKEN_EQUAL_GREATER);
14028}
14029
14033static PRISM_INLINE void
14034parse_arguments_append(pm_parser_t *parser, pm_arguments_t *arguments, pm_node_t *argument) {
14035 if (arguments->arguments == NULL) {
14036 arguments->arguments = pm_arguments_node_create(parser);
14037 }
14038
14039 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, argument);
14040}
14041
14046static PRISM_INLINE bool
14047pm_call_node_command_p(const pm_call_node_t *node) {
14048 return (
14049 (node->opening_loc.length == 0) &&
14050 (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) &&
14051 (node->arguments != NULL || node->block != NULL)
14052 );
14053}
14054
14064static bool
14065pm_constant_path_command_call_p(const pm_parser_t *parser, const pm_call_node_t *call) {
14066 return (
14067 call->receiver != NULL &&
14068 call->opening_loc.length == 0 &&
14069 call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE) &&
14070 call->call_operator_loc.length > 0 &&
14071 parser->start[call->call_operator_loc.start] == ':' &&
14072 call->message_loc.length > 0 &&
14073 parser->encoding->isupper_char(parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)
14074 );
14075}
14076
14082static bool
14083pm_command_call_value_p(const pm_parser_t *parser, const pm_node_t *node) {
14084 switch (PM_NODE_TYPE(node)) {
14085 case PM_CALL_NODE: {
14086 const pm_call_node_t *call = (const pm_call_node_t *) node;
14087
14088 /* Command-style calls (e.g., foo bar, obj.foo bar). Attribute
14089 * writes (e.g., a.b = 1) are not commands. */
14090 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)) {
14091 return true;
14092 }
14093
14094 /* A constant-path command with a brace block, e.g. `Foo::Bar { }`. */
14095 if (pm_constant_path_command_call_p(parser, call)) {
14096 return true;
14097 }
14098
14099 /* A `!` or `not` prefix wrapping a command call (e.g., `!foo bar`,
14100 * `not foo bar`) is also a command-call value. */
14101 if (call->receiver != NULL && call->arguments == NULL && call->opening_loc.length == 0 && call->call_operator_loc.length == 0) {
14102 return pm_command_call_value_p(parser, call->receiver);
14103 }
14104
14105 return false;
14106 }
14107 case PM_SUPER_NODE: {
14108 /* A command-style super (no parens). A super carrying a do-block is
14109 * a block call (it permits chaining), so it is excluded here and
14110 * handled by pm_block_call_p instead. */
14111 const pm_super_node_t *cast = (const pm_super_node_t *) node;
14112 return cast->lparen_loc.length == 0 &&
14113 (cast->arguments != NULL || cast->block != NULL) &&
14114 !(cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE));
14115 }
14116 case PM_YIELD_NODE: {
14117 const pm_yield_node_t *cast = (const pm_yield_node_t *) node;
14118 return cast->lparen_loc.length == 0 && cast->arguments != NULL;
14119 }
14120 case PM_RESCUE_MODIFIER_NODE:
14121 return pm_command_call_value_p(parser, ((const pm_rescue_modifier_node_t *) node)->expression);
14122 case PM_DEF_NODE: {
14123 const pm_def_node_t *cast = (const pm_def_node_t *) node;
14124 if (cast->equal_loc.length > 0 && cast->body != NULL) {
14125 const pm_node_t *body = cast->body;
14126 if (PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE)) {
14127 body = ((const pm_statements_node_t *) body)->body.nodes[((const pm_statements_node_t *) body)->body.size - 1];
14128 }
14129 return pm_command_call_value_p(parser, body);
14130 }
14131 return false;
14132 }
14133 default:
14134 return false;
14135 }
14136}
14137
14144static bool
14145pm_block_call_p(const pm_node_t *node) {
14146 while (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
14147 const pm_call_node_t *call = (const pm_call_node_t *) node;
14148
14149 /* Root: a command (no parentheses) carrying command arguments and a
14150 * block (brace or do), e.g. `foo bar do end`, `foo bar { }`. The
14151 * no-parentheses requirement is what distinguishes a command root from
14152 * a method call root like `foo.bar(1) { }`, which is a primary value
14153 * and may be used as an argument.
14154 */
14155 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE)) {
14156 return true;
14157 }
14158
14159 /* Walk up the receiver chain of a `.`/`::`/`&.` call (e.g.,
14160 * `foo bar do end.baz(1)`). Parentheses on the chained call are allowed
14161 * here -- in parse.y a `block_call` can be extended by
14162 * `call_op2 operation2 opt_paren_args` and remains a block call.
14163 */
14164 if (call->call_operator_loc.length > 0 && call->receiver != NULL) {
14165 node = call->receiver;
14166 continue;
14167 }
14168
14169 return false;
14170 }
14171
14172 /* A `super` with command arguments and a do-block is also a block-call root
14173 * (parse.y: `command do_block`, where the command is `keyword_super
14174 * command_args`). `super do end` with no arguments is a forwarding super
14175 * (a primary value) and is handled elsewhere.
14176 */
14177 if (PM_NODE_TYPE_P(node, PM_SUPER_NODE)) {
14178 const pm_super_node_t *super = (const pm_super_node_t *) node;
14179 return super->lparen_loc.length == 0 && super->block != NULL && PM_NODE_TYPE_P(super->block, PM_BLOCK_NODE);
14180 }
14181
14182 return false;
14183}
14184
14188static void
14189parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator, uint8_t flags, uint16_t depth) {
14190 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
14191
14192 // First we need to check if the next token is one that could be the start
14193 // of an argument. If it's not, then we can just return.
14194 if (
14195 match2(parser, terminator, PM_TOKEN_EOF) ||
14196 (binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) ||
14197 context_terminator(parser->current_context->context, &parser->current)
14198 ) {
14199 return;
14200 }
14201
14202 bool parsed_first_argument = false;
14203 bool parsed_bare_hash = false;
14204 bool parsed_block_argument = false;
14205 bool parsed_forwarding_arguments = false;
14206
14207 while (!match1(parser, PM_TOKEN_EOF)) {
14208 if (parsed_forwarding_arguments) {
14209 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES);
14210 }
14211
14212 pm_node_t *argument = NULL;
14213
14214 switch (parser->current.type) {
14215 case PM_TOKEN_USTAR_STAR:
14216 case PM_TOKEN_LABEL: {
14217 if (parsed_bare_hash) {
14218 pm_parser_err_current(parser, PM_ERR_ARGUMENT_BARE_HASH);
14219 }
14220
14221 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
14222 argument = UP(hash);
14223
14224 pm_static_literals_t hash_keys = { 0 };
14225 bool contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1));
14226
14227 parse_arguments_append(parser, arguments, argument);
14228
14229 pm_node_flags_t node_flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14230 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14231 pm_node_flag_set(UP(arguments->arguments), node_flags);
14232
14233 pm_static_literals_free(&hash_keys);
14234 parsed_bare_hash = true;
14235
14236 break;
14237 }
14238 case PM_TOKEN_UAMPERSAND: {
14239 parser_lex(parser);
14240 pm_token_t operator = parser->previous;
14241 pm_node_t *expression = NULL;
14242
14243 if (token_begins_expression_p(parser->current.type)) {
14244 expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14245 } else {
14246 pm_parser_scope_forwarding_block_check(parser, &operator);
14247 }
14248
14249 argument = UP(pm_block_argument_node_create(parser, &operator, expression));
14250 if (parsed_block_argument) {
14251 parse_arguments_append(parser, arguments, argument);
14252 } else {
14253 arguments->block = argument;
14254 }
14255
14256 if (match1(parser, PM_TOKEN_COMMA)) {
14257 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK);
14258 }
14259
14260 parsed_block_argument = true;
14261 break;
14262 }
14263 case PM_TOKEN_USTAR: {
14264 parser_lex(parser);
14265 pm_token_t operator = parser->previous;
14266
14267 if (match4(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_SEMICOLON, PM_TOKEN_BRACKET_RIGHT)) {
14268 pm_parser_scope_forwarding_positionals_check(parser, &operator);
14269 argument = UP(pm_splat_node_create(parser, &operator, NULL));
14270 if (parsed_bare_hash) {
14271 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT);
14272 }
14273 } else {
14274 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));
14275
14276 if (parsed_bare_hash) {
14277 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);
14278 }
14279
14280 argument = UP(pm_splat_node_create(parser, &operator, expression));
14281 }
14282
14283 parse_arguments_append(parser, arguments, argument);
14284 break;
14285 }
14286 case PM_TOKEN_UDOT_DOT_DOT: {
14287 if (accepts_forwarding) {
14288 parser_lex(parser);
14289
14290 if (token_begins_expression_p(parser->current.type)) {
14291 // If the token begins an expression then this ... was
14292 // not actually argument forwarding but was instead a
14293 // range.
14294 pm_token_t operator = parser->previous;
14295 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));
14296
14297 // If we parse a range, we need to validate that we
14298 // didn't accidentally violate the nonassoc rules of the
14299 // ... operator.
14300 if (PM_NODE_TYPE_P(right, PM_RANGE_NODE)) {
14301 pm_range_node_t *range = (pm_range_node_t *) right;
14302 pm_parser_err(parser, range->operator_loc.start, range->operator_loc.length, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
14303 }
14304
14305 argument = UP(pm_range_node_create(parser, NULL, &operator, right));
14306 } else {
14307 pm_parser_scope_forwarding_all_check(parser, &parser->previous);
14308 if (parsed_first_argument && terminator == PM_TOKEN_EOF) {
14309 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND);
14310 }
14311
14312 argument = UP(pm_forwarding_arguments_node_create(parser, &parser->previous));
14313 parse_arguments_append(parser, arguments, argument);
14314 pm_node_flag_set(UP(arguments->arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
14315 arguments->has_forwarding = true;
14316 parsed_forwarding_arguments = true;
14317 break;
14318 }
14319 }
14320 }
14322 default: {
14323 if (argument == NULL) {
14324 argument = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (!parsed_first_argument ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0u) | PM_PARSE_ACCEPTS_LABEL), PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14325 }
14326
14327 bool contains_keywords = false;
14328 bool contains_keyword_splat = false;
14329
14330 if (argument_allowed_for_bare_hash(parser, argument)) {
14331 if (parsed_bare_hash) {
14332 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH);
14333 }
14334
14335 /* A hash key must be an argument (`arg`). A command call or
14336 * block call (e.g. `Foo::Bar { } => v`, `foo bar do end =>
14337 * v`) is not an argument, so reject it as a key. Plain
14338 * command calls never reach here as a key because they
14339 * absorb the `=>` into their own arguments first.
14340 */
14341 if (pm_command_call_value_p(parser, argument) || pm_block_call_p(argument)) {
14342 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->previous.type));
14343 }
14344
14345 pm_token_t operator = { 0 };
14346 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
14347 operator = parser->previous;
14348 }
14349
14350 pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser);
14351 contains_keywords = true;
14352
14353 // Create the set of static literals for this hash.
14354 pm_static_literals_t hash_keys = { 0 };
14355 pm_hash_key_static_literals_add(parser, &hash_keys, argument);
14356
14357 // Finish parsing the one we are part way through.
14358 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));
14359 argument = UP(pm_assoc_node_create(parser, argument, NTOK2PTR(operator), value));
14360
14361 pm_keyword_hash_node_elements_append(parser->arena, bare_hash, argument);
14362 argument = UP(bare_hash);
14363
14364 // Then parse more if we have a comma
14365 if (accept1(parser, PM_TOKEN_COMMA) && (
14366 token_begins_expression_p(parser->current.type) ||
14367 match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)
14368 )) {
14369 contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(bare_hash), (uint16_t) (depth + 1));
14370 }
14371
14372 pm_static_literals_free(&hash_keys);
14373 parsed_bare_hash = true;
14374 }
14375
14376 parse_arguments_append(parser, arguments, argument);
14377
14378 pm_node_flags_t node_flags = 0;
14379 if (contains_keywords) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14380 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14381 pm_node_flag_set(UP(arguments->arguments), node_flags);
14382
14383 break;
14384 }
14385 }
14386
14387 parsed_first_argument = true;
14388
14389 // If parsing the argument failed, we need to stop parsing arguments.
14390 if (PM_NODE_TYPE_P(argument, PM_ERROR_RECOVERY_NODE) || parser->recovering) break;
14391
14392 // If the terminator of these arguments is not EOF, then we have a
14393 // specific token we're looking for. In that case we can accept a
14394 // newline here because it is not functioning as a statement terminator.
14395 bool accepted_newline = false;
14396 if (terminator != PM_TOKEN_EOF) {
14397 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14398 }
14399
14400 if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) {
14401 // If we previously were on a comma and we just parsed a bare hash,
14402 // then we want to continue parsing arguments. This is because the
14403 // comma was grabbed up by the hash parser.
14404 } else if (accept1(parser, PM_TOKEN_COMMA)) {
14405 // If there was a comma, then we need to check if we also accepted a
14406 // newline. If we did, then this is a syntax error.
14407 if (accepted_newline) {
14408 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14409 }
14410
14411 // If this is a command call and an argument takes a block,
14412 // there can be no further arguments. For example,
14413 // `foo(bar 1 do end, 2)` should be rejected.
14414 if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) {
14415 pm_call_node_t *call = (pm_call_node_t *) argument;
14416 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL) {
14417 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14418 break;
14419 }
14420 }
14421 } else {
14422 // If there is no comma at the end of the argument list then we're
14423 // done parsing arguments and can break out of this loop.
14424 break;
14425 }
14426
14427 // If we hit the terminator, then that means we have a trailing comma so
14428 // we can accept that output as well.
14429 if (match1(parser, terminator)) {
14430 // A forwarding `...` argument must be the last argument and cannot
14431 // be followed by a trailing comma, e.g. `foo(...,)`. A comma
14432 // followed by another argument is already rejected at the top of
14433 // this loop, so the only case left to reject here is the trailing
14434 // one.
14435 if (parsed_forwarding_arguments) {
14436 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14437 }
14438
14439 break;
14440 }
14441 }
14442}
14443
14455parse_required_destructured_parameter(pm_parser_t *parser) {
14456 expect1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER);
14457
14458 pm_multi_target_node_t *node = pm_multi_target_node_create(parser);
14459 pm_multi_target_node_opening_set(parser, node, &parser->previous);
14460
14461 do {
14462 pm_node_t *param;
14463
14464 // If we get here then we have a trailing comma, which isn't allowed in
14465 // the grammar. In other places, multi targets _do_ allow trailing
14466 // commas, so here we'll assume this is a mistake of the user not
14467 // knowing it's not allowed here.
14468 if (node->lefts.size > 0 && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
14469 param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14470 pm_multi_target_node_targets_append(parser, node, param);
14471 pm_parser_err_current(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14472 break;
14473 }
14474
14475 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
14476 param = UP(parse_required_destructured_parameter(parser));
14477 } else if (accept1(parser, PM_TOKEN_USTAR)) {
14478 pm_token_t star = parser->previous;
14479 pm_node_t *value = NULL;
14480
14481 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14482 pm_token_t name = parser->previous;
14483 value = UP(pm_required_parameter_node_create(parser, &name));
14484 if (pm_parser_parameter_name_check(parser, &name)) {
14485 pm_node_flag_set_repeated_parameter(value);
14486 }
14487 pm_parser_local_add_token(parser, &name, 1);
14488 }
14489
14490 param = UP(pm_splat_node_create(parser, &star, value));
14491 } else {
14492 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER);
14493 pm_token_t name = parser->previous;
14494
14495 param = UP(pm_required_parameter_node_create(parser, &name));
14496 if (pm_parser_parameter_name_check(parser, &name)) {
14497 pm_node_flag_set_repeated_parameter(param);
14498 }
14499 pm_parser_local_add_token(parser, &name, 1);
14500 }
14501
14502 pm_multi_target_node_targets_append(parser, node, param);
14503 } while (accept1(parser, PM_TOKEN_COMMA));
14504
14505 accept1(parser, PM_TOKEN_NEWLINE);
14506 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN_REQ_PARAMETER);
14507 pm_multi_target_node_closing_set(parser, node, &parser->previous);
14508
14509 return node;
14510}
14511
14516typedef enum {
14517 PM_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state
14518 PM_PARAMETERS_ORDER_NOTHING_AFTER = 1,
14519 PM_PARAMETERS_ORDER_KEYWORDS_REST,
14520 PM_PARAMETERS_ORDER_KEYWORDS,
14521 PM_PARAMETERS_ORDER_REST,
14522 PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14523 PM_PARAMETERS_ORDER_OPTIONAL,
14524 PM_PARAMETERS_ORDER_NAMED,
14525 PM_PARAMETERS_ORDER_NONE,
14526} pm_parameters_order_t;
14527
14531static pm_parameters_order_t parameters_ordering[PM_TOKEN_MAXIMUM] = {
14532 [0] = PM_PARAMETERS_NO_CHANGE,
14533 [PM_TOKEN_UAMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14534 [PM_TOKEN_AMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14535 [PM_TOKEN_UDOT_DOT_DOT] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14536 [PM_TOKEN_IDENTIFIER] = PM_PARAMETERS_ORDER_NAMED,
14537 [PM_TOKEN_PARENTHESIS_LEFT_GROUPING] = PM_PARAMETERS_ORDER_NAMED,
14538 [PM_TOKEN_EQUAL] = PM_PARAMETERS_ORDER_OPTIONAL,
14539 [PM_TOKEN_LABEL] = PM_PARAMETERS_ORDER_KEYWORDS,
14540 [PM_TOKEN_USTAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14541 [PM_TOKEN_STAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14542 [PM_TOKEN_USTAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST,
14543 [PM_TOKEN_STAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST
14544};
14545
14553static bool
14554update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_order_t *current) {
14555 pm_parameters_order_t state = parameters_ordering[token->type];
14556 if (state == PM_PARAMETERS_NO_CHANGE) return true;
14557
14558 // If we see another ordered argument after a optional argument
14559 // we only continue parsing ordered arguments until we stop seeing ordered arguments.
14560 if (*current == PM_PARAMETERS_ORDER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14561 *current = PM_PARAMETERS_ORDER_AFTER_OPTIONAL;
14562 return true;
14563 } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14564 return true;
14565 }
14566
14567 if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14568 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR);
14569 return false;
14570 } else if (token->type == PM_TOKEN_UDOT_DOT_DOT && (*current >= PM_PARAMETERS_ORDER_KEYWORDS_REST && *current <= PM_PARAMETERS_ORDER_AFTER_OPTIONAL)) {
14571 pm_parser_err_token(parser, token, *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL ? PM_ERR_PARAMETER_FORWARDING_AFTER_REST : PM_ERR_PARAMETER_ORDER);
14572 return false;
14573 } else if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) {
14574 // We know what transition we failed on, so we can provide a better error here.
14575 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_ORDER);
14576 return false;
14577 }
14578
14579 if (state < *current) *current = state;
14580 return true;
14581}
14582
14583static PRISM_INLINE void
14584parse_parameters_handle_trailing_comma(
14585 pm_parser_t *parser,
14586 pm_parameters_node_t *params,
14587 pm_parameters_order_t order,
14588 bool in_block,
14589 bool allows_trailing_comma
14590) {
14591 if (!allows_trailing_comma) {
14592 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14593 return;
14594 }
14595
14596 if (in_block) {
14597 if (order >= PM_PARAMETERS_ORDER_NAMED) {
14598 // foo do |bar,|; end
14599 pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14600
14601 if (params->rest == NULL) {
14602 pm_parameters_node_rest_set(params, param);
14603 } else {
14604 pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI);
14605 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14606 }
14607 } else {
14608 // foo do |*bar,|; end
14609 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14610 }
14611 } else {
14612 // https://bugs.ruby-lang.org/issues/19107
14613 // Allow `def foo(bar,); end`, `def foo(*bar,); end`, etc. but not `def foo(...,); end`
14614 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1 || order == PM_PARAMETERS_ORDER_NOTHING_AFTER) {
14615 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14616 }
14617 }
14618}
14619
14623static pm_parameters_node_t *
14624parse_parameters(
14625 pm_parser_t *parser,
14626 pm_binding_power_t binding_power,
14627 bool uses_parentheses,
14628 bool allows_trailing_comma,
14629 bool allows_forwarding_parameters,
14630 bool accepts_blocks_in_defaults,
14631 bool in_block,
14632 pm_diagnostic_id_t diag_id_forwarding,
14633 uint16_t depth
14634) {
14635 pm_do_loop_stack_push(parser, false);
14636
14637 pm_parameters_node_t *params = pm_parameters_node_create(parser);
14638 pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE;
14639
14640 while (true) {
14641 bool parsing = true;
14642
14643 switch (parser->current.type) {
14644 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
14645 update_parameter_state(parser, &parser->current, &order);
14646 pm_node_t *param = UP(parse_required_destructured_parameter(parser));
14647
14648 if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14649 pm_parameters_node_requireds_append(parser->arena, params, param);
14650 } else {
14651 pm_parameters_node_posts_append(parser->arena, params, param);
14652 }
14653 break;
14654 }
14655 case PM_TOKEN_UAMPERSAND:
14656 case PM_TOKEN_AMPERSAND: {
14657 update_parameter_state(parser, &parser->current, &order);
14658 parser_lex(parser);
14659
14660 pm_token_t operator = parser->previous;
14661 pm_node_t *param;
14662
14663 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1 && accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
14664 param = (pm_node_t *) pm_no_block_parameter_node_create(parser, &operator, &parser->previous);
14665 } else {
14666 pm_token_t name = {0};
14667
14668 bool repeated = false;
14669 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14670 name = parser->previous;
14671 repeated = pm_parser_parameter_name_check(parser, &name);
14672 pm_parser_local_add_token(parser, &name, 1);
14673 } else {
14674 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
14675 }
14676
14677 param = (pm_node_t *) pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator);
14678 if (repeated) {
14679 pm_node_flag_set_repeated_parameter(param);
14680 }
14681 }
14682
14683 if (params->block == NULL) {
14684 pm_parameters_node_block_set(params, param);
14685 } else {
14686 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_BLOCK_MULTI);
14687 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
14688 }
14689
14690 break;
14691 }
14692 case PM_TOKEN_UDOT_DOT_DOT: {
14693 if (!allows_forwarding_parameters) {
14694 pm_parser_err_current(parser, diag_id_forwarding);
14695 }
14696
14697 bool succeeded = update_parameter_state(parser, &parser->current, &order);
14698 parser_lex(parser);
14699
14700 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
14701 pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
14702
14703 if (params->keyword_rest != NULL) {
14704 // If we already have a keyword rest parameter, then we replace it with the
14705 // forwarding parameter and move the keyword rest parameter to the posts list.
14706 pm_node_t *keyword_rest = params->keyword_rest;
14707 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, keyword_rest)));
14708 if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD);
14709 params->keyword_rest = NULL;
14710 }
14711
14712 pm_parameters_node_keyword_rest_set(params, UP(param));
14713 break;
14714 }
14715 case PM_TOKEN_CLASS_VARIABLE:
14716 case PM_TOKEN_IDENTIFIER:
14717 case PM_TOKEN_CONSTANT:
14718 case PM_TOKEN_INSTANCE_VARIABLE:
14719 case PM_TOKEN_GLOBAL_VARIABLE:
14720 case PM_TOKEN_METHOD_NAME: {
14721 parser_lex(parser);
14722 switch (parser->previous.type) {
14723 case PM_TOKEN_CONSTANT:
14724 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14725 break;
14726 case PM_TOKEN_INSTANCE_VARIABLE:
14727 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
14728 break;
14729 case PM_TOKEN_GLOBAL_VARIABLE:
14730 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
14731 break;
14732 case PM_TOKEN_CLASS_VARIABLE:
14733 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
14734 break;
14735 case PM_TOKEN_METHOD_NAME:
14736 pm_parser_err_previous(parser, PM_ERR_PARAMETER_METHOD_NAME);
14737 break;
14738 default: break;
14739 }
14740
14741 if (parser->current.type == PM_TOKEN_EQUAL) {
14742 update_parameter_state(parser, &parser->current, &order);
14743 } else {
14744 update_parameter_state(parser, &parser->previous, &order);
14745 }
14746
14747 pm_token_t name = parser->previous;
14748 bool repeated = pm_parser_parameter_name_check(parser, &name);
14749 pm_parser_local_add_token(parser, &name, 1);
14750
14751 if (match1(parser, PM_TOKEN_EQUAL)) {
14752 pm_token_t operator = parser->current;
14753 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14754 parser_lex(parser);
14755
14756 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
14757 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14758
14759 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14760 pm_node_t *value = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1));
14761 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14762
14763 pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
14764
14765 if (repeated) {
14766 pm_node_flag_set_repeated_parameter(UP(param));
14767 }
14768 pm_parameters_node_optionals_append(parser->arena, params, param);
14769
14770 // If the value of the parameter increased the number of
14771 // reads of that parameter, then we need to warn that we
14772 // have a circular definition.
14773 if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14774 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &name, PM_ERR_PARAMETER_CIRCULAR);
14775 }
14776
14777 context_pop(parser);
14778
14779 // If parsing the value of the parameter resulted in error recovery,
14780 // then we can put a missing node in its place and stop parsing the
14781 // parameters entirely now.
14782 if (parser->recovering) {
14783 parsing = false;
14784 break;
14785 }
14786 } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14787 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14788 if (repeated) {
14789 pm_node_flag_set_repeated_parameter(UP(param));
14790 }
14791 pm_parameters_node_requireds_append(parser->arena, params, UP(param));
14792 } else {
14793 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14794 if (repeated) {
14795 pm_node_flag_set_repeated_parameter(UP(param));
14796 }
14797 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14798 }
14799
14800 break;
14801 }
14802 case PM_TOKEN_LABEL: {
14803 if (!uses_parentheses && !in_block) parser->in_keyword_arg = true;
14804 update_parameter_state(parser, &parser->current, &order);
14805
14806 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14807 parser_lex(parser);
14808
14809 pm_token_t name = parser->previous;
14810 pm_token_t local = name;
14811 local.end -= 1;
14812
14813 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)) {
14814 pm_parser_err(parser, PM_TOKEN_START(parser, &local), PM_TOKEN_LENGTH(&local), PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14815 } else if (local.end[-1] == '!' || local.end[-1] == '?') {
14816 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE);
14817 }
14818
14819 bool repeated = pm_parser_parameter_name_check(parser, &local);
14820 pm_parser_local_add_token(parser, &local, 1);
14821
14822 switch (parser->current.type) {
14823 case PM_TOKEN_COMMA:
14824 case PM_TOKEN_PARENTHESIS_RIGHT:
14825 case PM_TOKEN_PIPE: {
14826 context_pop(parser);
14827
14828 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14829 if (repeated) {
14830 pm_node_flag_set_repeated_parameter(param);
14831 }
14832
14833 pm_parameters_node_keywords_append(parser->arena, params, param);
14834 break;
14835 }
14836 case PM_TOKEN_SEMICOLON:
14837 case PM_TOKEN_NEWLINE: {
14838 context_pop(parser);
14839
14840 if (uses_parentheses) {
14841 parsing = false;
14842 break;
14843 }
14844
14845 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14846 if (repeated) {
14847 pm_node_flag_set_repeated_parameter(param);
14848 }
14849
14850 pm_parameters_node_keywords_append(parser->arena, params, param);
14851 break;
14852 }
14853 default: {
14854 pm_node_t *param;
14855
14856 if (token_begins_expression_p(parser->current.type)) {
14857 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
14858 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14859
14860 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14861 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));
14862 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14863
14864 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14865 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_PARAMETER_CIRCULAR);
14866 }
14867
14868 param = UP(pm_optional_keyword_parameter_node_create(parser, &name, value));
14869 }
14870 else {
14871 param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14872 }
14873
14874 if (repeated) {
14875 pm_node_flag_set_repeated_parameter(param);
14876 }
14877
14878 context_pop(parser);
14879 pm_parameters_node_keywords_append(parser->arena, params, param);
14880
14881 // If parsing the value of the parameter resulted in error recovery,
14882 // then we can put a missing node in its place and stop parsing the
14883 // parameters entirely now.
14884 if (parser->recovering) {
14885 parsing = false;
14886 break;
14887 }
14888 }
14889 }
14890
14891 parser->in_keyword_arg = false;
14892 break;
14893 }
14894 case PM_TOKEN_USTAR:
14895 case PM_TOKEN_STAR: {
14896 update_parameter_state(parser, &parser->current, &order);
14897 parser_lex(parser);
14898
14899 pm_token_t operator = parser->previous;
14900 pm_token_t name = { 0 };
14901 bool repeated = false;
14902
14903 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14904 name = parser->previous;
14905 repeated = pm_parser_parameter_name_check(parser, &name);
14906 pm_parser_local_add_token(parser, &name, 1);
14907 } else {
14908 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
14909 }
14910
14911 pm_node_t *param = UP(pm_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
14912 if (repeated) {
14913 pm_node_flag_set_repeated_parameter(param);
14914 }
14915
14916 if (params->rest == NULL) {
14917 pm_parameters_node_rest_set(params, param);
14918 } else {
14919 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_SPLAT_MULTI);
14920 pm_parameters_node_posts_append(parser->arena, params, param);
14921 }
14922
14923 break;
14924 }
14925 case PM_TOKEN_STAR_STAR:
14926 case PM_TOKEN_USTAR_STAR: {
14927 pm_parameters_order_t previous_order = order;
14928 update_parameter_state(parser, &parser->current, &order);
14929 parser_lex(parser);
14930
14931 pm_token_t operator = parser->previous;
14932 pm_node_t *param;
14933
14934 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
14935 if (previous_order <= PM_PARAMETERS_ORDER_KEYWORDS) {
14936 pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW);
14937 }
14938
14939 param = UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
14940 } else {
14941 pm_token_t name = { 0 };
14942
14943 bool repeated = false;
14944 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14945 name = parser->previous;
14946 repeated = pm_parser_parameter_name_check(parser, &name);
14947 pm_parser_local_add_token(parser, &name, 1);
14948 } else {
14949 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS;
14950 }
14951
14952 param = UP(pm_keyword_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
14953 if (repeated) {
14954 pm_node_flag_set_repeated_parameter(param);
14955 }
14956 }
14957
14958 if (params->keyword_rest == NULL) {
14959 pm_parameters_node_keyword_rest_set(params, param);
14960 } else {
14961 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI);
14962 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
14963 }
14964
14965 break;
14966 }
14967 default:
14968 if (parser->previous.type == PM_TOKEN_COMMA) {
14969 parse_parameters_handle_trailing_comma(parser, params, order, in_block, allows_trailing_comma);
14970 }
14971
14972 parsing = false;
14973 break;
14974 }
14975
14976 // If we hit some kind of issue while parsing the parameter, this would
14977 // have been set to false. In that case, we need to break out of the
14978 // loop.
14979 if (!parsing) break;
14980
14981 bool accepted_newline = false;
14982 if (uses_parentheses) {
14983 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14984 }
14985
14986 if (accept1(parser, PM_TOKEN_COMMA)) {
14987 // If there was a comma, but we also accepted a newline, then this
14988 // is a syntax error.
14989 if (accepted_newline) {
14990 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14991 }
14992 } else {
14993 // If there was no comma, then we're done parsing parameters.
14994 break;
14995 }
14996 }
14997
14998 pm_do_loop_stack_pop(parser);
14999
15000 // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`.
15001 if (PM_NODE_START(params) == PM_NODE_END(params)) {
15002 return NULL;
15003 }
15004
15005 return params;
15006}
15007
15012static size_t
15013token_newline_index(const pm_parser_t *parser) {
15014 if (parser->heredoc_end == NULL) {
15015 // This is the common case. In this case we can look at the previously
15016 // recorded newline in the newline list and subtract from the current
15017 // offset.
15018 return parser->line_offsets.size - 1;
15019 } else {
15020 // This is unlikely. This is the case that we have already parsed the
15021 // start of a heredoc, so we cannot rely on looking at the previous
15022 // offset of the newline list, and instead must go through the whole
15023 // process of a binary search for the line number.
15024 return (size_t) pm_line_offset_list_line(&parser->line_offsets, PM_TOKEN_START(parser, &parser->current), 0);
15025 }
15026}
15027
15032static int64_t
15033token_column(const pm_parser_t *parser, size_t newline_index, const pm_token_t *token, bool break_on_non_space) {
15034 const uint8_t *cursor = parser->start + parser->line_offsets.offsets[newline_index];
15035 const uint8_t *end = token->start;
15036
15037 // Skip over the BOM if it is present.
15038 if (
15039 newline_index == 0 &&
15040 parser->start[0] == 0xef &&
15041 parser->start[1] == 0xbb &&
15042 parser->start[2] == 0xbf
15043 ) cursor += 3;
15044
15045 int64_t column = 0;
15046 for (; cursor < end; cursor++) {
15047 switch (*cursor) {
15048 case '\t':
15049 column = ((column / PM_TAB_WHITESPACE_SIZE) + 1) * PM_TAB_WHITESPACE_SIZE;
15050 break;
15051 case ' ':
15052 column++;
15053 break;
15054 default:
15055 column++;
15056 if (break_on_non_space) return -1;
15057 break;
15058 }
15059 }
15060
15061 return column;
15062}
15063
15068static void
15069parser_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) {
15070 // If these warnings are disabled (unlikely), then we can just return.
15071 if (!parser->warn_mismatched_indentation) return;
15072
15073 // If the tokens are on the same line, we do not warn.
15074 size_t closing_newline_index = token_newline_index(parser);
15075 if (opening_newline_index == closing_newline_index) return;
15076
15077 // If the opening token has anything other than spaces or tabs before it,
15078 // then we do not warn. This is unless we are matching up an `if`/`end` pair
15079 // and the `if` immediately follows an `else` keyword.
15080 int64_t opening_column = token_column(parser, opening_newline_index, opening_token, !if_after_else);
15081 if (!if_after_else && (opening_column == -1)) return;
15082
15083 // Get a reference to the closing token off the current parser. This assumes
15084 // that the caller has placed this in the correct position.
15085 pm_token_t *closing_token = &parser->current;
15086
15087 // If the tokens are at the same indentation, we do not warn.
15088 int64_t closing_column = token_column(parser, closing_newline_index, closing_token, true);
15089 if ((closing_column == -1) || (opening_column == closing_column)) return;
15090
15091 // If the closing column is greater than the opening column and we are
15092 // allowing indentation, then we do not warn.
15093 if (allow_indent && (closing_column > opening_column)) return;
15094
15095 // Otherwise, add a warning.
15096 PM_PARSER_WARN_FORMAT(
15097 parser,
15098 PM_TOKEN_START(parser, closing_token),
15099 PM_TOKEN_LENGTH(closing_token),
15100 PM_WARN_INDENTATION_MISMATCH,
15101 (int) (closing_token->end - closing_token->start),
15102 (const char *) closing_token->start,
15103 (int) (opening_token->end - opening_token->start),
15104 (const char *) opening_token->start,
15105 ((int32_t) opening_newline_index) + parser->start_line
15106 );
15107}
15108
15109typedef enum {
15110 PM_RESCUES_BEGIN = 1,
15111 PM_RESCUES_BLOCK,
15112 PM_RESCUES_CLASS,
15113 PM_RESCUES_DEF,
15114 PM_RESCUES_LAMBDA,
15115 PM_RESCUES_MODULE,
15116 PM_RESCUES_SCLASS
15117} pm_rescues_type_t;
15118
15123static PRISM_INLINE void
15124parse_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) {
15125 pm_rescue_node_t *current = NULL;
15126
15127 while (match1(parser, PM_TOKEN_KEYWORD_RESCUE)) {
15128 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15129 parser_lex(parser);
15130
15131 pm_rescue_node_t *rescue = pm_rescue_node_create(parser, &parser->previous);
15132
15133 switch (parser->current.type) {
15134 case PM_TOKEN_EQUAL_GREATER: {
15135 // Here we have an immediate => after the rescue keyword, in which case
15136 // we're going to have an empty list of exceptions to rescue (which
15137 // implies StandardError).
15138 parser_lex(parser);
15139 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15140
15141 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15142 reference = parse_target(parser, reference, false, false);
15143
15144 pm_rescue_node_reference_set(rescue, reference);
15145 break;
15146 }
15147 case PM_TOKEN_NEWLINE:
15148 case PM_TOKEN_SEMICOLON:
15149 case PM_TOKEN_KEYWORD_THEN:
15150 // Here we have a terminator for the rescue keyword, in which
15151 // case we're going to just continue on.
15152 break;
15153 default: {
15154 if (token_begins_expression_p(parser->current.type) || match1(parser, PM_TOKEN_USTAR)) {
15155 // Here we have something that could be an exception expression, so
15156 // we'll attempt to parse it here and any others delimited by commas.
15157
15158 do {
15159 pm_node_t *expression = parse_starred_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_RESCUE_EXPRESSION, (uint16_t) (depth + 1));
15160 pm_rescue_node_exceptions_append(parser->arena, rescue, expression);
15161
15162 // If we hit a newline, then this is the end of the rescue expression. We
15163 // can continue on to parse the statements.
15164 if (match3(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_THEN)) break;
15165
15166 // If we hit a `=>` then we're going to parse the exception variable. Once
15167 // we've done that, we'll break out of the loop and parse the statements.
15168 if (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
15169 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15170
15171 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15172 reference = parse_target(parser, reference, false, false);
15173
15174 pm_rescue_node_reference_set(rescue, reference);
15175 break;
15176 }
15177 } while (accept1(parser, PM_TOKEN_COMMA));
15178 }
15179 }
15180 }
15181
15182 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
15183 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
15184 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15185 }
15186 } else {
15187 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM);
15188 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15189 }
15190
15191 if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
15192 pm_accepts_block_stack_push(parser, true);
15193 pm_context_t context;
15194
15195 switch (type) {
15196 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break;
15197 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break;
15198 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break;
15199 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break;
15200 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break;
15201 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break;
15202 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break;
15203 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15204 }
15205
15206 pm_statements_node_t *statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15207 if (statements != NULL) pm_rescue_node_statements_set(rescue, statements);
15208
15209 pm_accepts_block_stack_pop(parser);
15210 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15211 }
15212
15213 if (current == NULL) {
15214 pm_begin_node_rescue_clause_set(parent_node, rescue);
15215 } else {
15216 pm_rescue_node_subsequent_set(current, rescue);
15217 }
15218
15219 current = rescue;
15220 }
15221
15222 // The end node locations on rescue nodes will not be set correctly
15223 // since we won't know the end until we've found all subsequent
15224 // clauses. This sets the end location on all rescues once we know it.
15225 if (current != NULL) {
15226 pm_rescue_node_t *clause = parent_node->rescue_clause;
15227
15228 while (clause != NULL) {
15229 PM_NODE_LENGTH_SET_NODE(clause, current);
15230 clause = clause->subsequent;
15231 }
15232 }
15233
15234 pm_token_t else_keyword;
15235 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
15236 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15237 opening_newline_index = token_newline_index(parser);
15238
15239 else_keyword = parser->current;
15240 opening = &else_keyword;
15241
15242 parser_lex(parser);
15243 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15244
15245 pm_statements_node_t *else_statements = NULL;
15246 if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) {
15247 pm_accepts_block_stack_push(parser, true);
15248 pm_context_t context;
15249
15250 switch (type) {
15251 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break;
15252 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break;
15253 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break;
15254 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break;
15255 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break;
15256 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break;
15257 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break;
15258 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_ELSE; break;
15259 }
15260
15261 else_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15262 pm_accepts_block_stack_pop(parser);
15263
15264 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15265 }
15266
15267 pm_else_node_t *else_clause = pm_else_node_create(parser, &else_keyword, else_statements, &parser->current);
15268 pm_begin_node_else_clause_set(parent_node, else_clause);
15269
15270 // If we don't have a `current` rescue node, then this is a dangling
15271 // else, and it's an error.
15272 if (current == NULL) pm_parser_err_node(parser, UP(else_clause), PM_ERR_BEGIN_LONELY_ELSE);
15273 }
15274
15275 if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) {
15276 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15277 pm_token_t ensure_keyword = parser->current;
15278
15279 parser_lex(parser);
15280 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15281
15282 pm_statements_node_t *ensure_statements = NULL;
15283 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15284 pm_accepts_block_stack_push(parser, true);
15285 pm_context_t context;
15286
15287 switch (type) {
15288 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break;
15289 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break;
15290 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break;
15291 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break;
15292 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break;
15293 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break;
15294 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break;
15295 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15296 }
15297
15298 ensure_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15299 pm_accepts_block_stack_pop(parser);
15300
15301 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15302 }
15303
15304 pm_ensure_node_t *ensure_clause = pm_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current);
15305 pm_begin_node_ensure_clause_set(parent_node, ensure_clause);
15306 }
15307
15308 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
15309 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15310 pm_begin_node_end_keyword_set(parser, parent_node, &parser->current);
15311 } else {
15312 pm_token_t end_keyword = (pm_token_t) { .type = PM_TOKEN_KEYWORD_END, .start = parser->previous.end, .end = parser->previous.end };
15313 pm_begin_node_end_keyword_set(parser, parent_node, &end_keyword);
15314 }
15315}
15316
15321static pm_begin_node_t *
15322parse_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) {
15323 pm_begin_node_t *node = pm_begin_node_create(parser, NULL, statements);
15324 parse_rescues(parser, opening_newline_index, opening, node, type, (uint16_t) (depth + 1));
15325
15326 node->base.location.start = U32(start - parser->start);
15327 PM_NODE_LENGTH_SET_TOKEN(parser, node, &parser->current);
15328
15329 return node;
15330}
15331
15336parse_block_parameters(
15337 pm_parser_t *parser,
15338 bool allows_trailing_comma,
15339 const pm_token_t *opening,
15340 bool is_lambda_literal,
15341 bool accepts_blocks_in_defaults,
15342 uint16_t depth
15343) {
15344 pm_parameters_node_t *parameters = NULL;
15345 if (!match1(parser, PM_TOKEN_SEMICOLON)) {
15346 if (!is_lambda_literal) {
15347 context_push(parser, PM_CONTEXT_BLOCK_PARAMETERS);
15348 }
15349 parameters = parse_parameters(
15350 parser,
15351 is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX,
15352 false,
15353 allows_trailing_comma,
15354 false,
15355 accepts_blocks_in_defaults,
15356 true,
15357 is_lambda_literal ? PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA : PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK,
15358 (uint16_t) (depth + 1)
15359 );
15360 if (!is_lambda_literal) {
15361 context_pop(parser);
15362 }
15363 }
15364
15365 pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening);
15366 if (opening != NULL) {
15367 accept1(parser, PM_TOKEN_NEWLINE);
15368
15369 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
15370 do {
15371 switch (parser->current.type) {
15372 case PM_TOKEN_CONSTANT:
15373 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
15374 parser_lex(parser);
15375 break;
15376 case PM_TOKEN_INSTANCE_VARIABLE:
15377 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
15378 parser_lex(parser);
15379 break;
15380 case PM_TOKEN_GLOBAL_VARIABLE:
15381 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
15382 parser_lex(parser);
15383 break;
15384 case PM_TOKEN_CLASS_VARIABLE:
15385 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
15386 parser_lex(parser);
15387 break;
15388 default:
15389 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
15390 break;
15391 }
15392
15393 bool repeated = pm_parser_parameter_name_check(parser, &parser->previous);
15394 pm_parser_local_add_token(parser, &parser->previous, 1);
15395
15396 pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous);
15397 if (repeated) pm_node_flag_set_repeated_parameter(UP(local));
15398
15399 pm_block_parameters_node_append_local(parser->arena, block_parameters, local);
15400 } while (accept1(parser, PM_TOKEN_COMMA));
15401 }
15402 }
15403
15404 return block_parameters;
15405}
15406
15411static bool
15412outer_scope_using_numbered_parameters_p(pm_parser_t *parser) {
15413 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15414 if (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) return true;
15415 }
15416
15417 return false;
15418}
15419
15425static const char * const pm_numbered_parameter_names[] = {
15426 "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9"
15427};
15428
15434static pm_node_t *
15435parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_token_t *opening, const pm_token_t *closing) {
15436 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
15437
15438 // If we have ordinary parameters, then we will return them as the set of
15439 // parameters.
15440 if (parameters != NULL) {
15441 // If we also have implicit parameters, then this is an error.
15442 if (implicit_parameters->size > 0) {
15443 pm_node_t *node = implicit_parameters->nodes[0];
15444
15445 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15446 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_ORDINARY);
15447 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15448 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_ORDINARY);
15449 } else {
15450 assert(false && "unreachable");
15451 }
15452 }
15453
15454 return parameters;
15455 }
15456
15457 // If we don't have any implicit parameters, then the set of parameters is
15458 // NULL.
15459 if (implicit_parameters->size == 0) {
15460 return NULL;
15461 }
15462
15463 // If we don't have ordinary parameters, then we now must validate our set
15464 // of implicit parameters. We can only have numbered parameters or it, but
15465 // they cannot be mixed.
15466 uint8_t numbered_parameter = 0;
15467 bool it_parameter = false;
15468
15469 for (size_t index = 0; index < implicit_parameters->size; index++) {
15470 pm_node_t *node = implicit_parameters->nodes[index];
15471
15472 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15473 if (it_parameter) {
15474 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_IT);
15475 } else if (outer_scope_using_numbered_parameters_p(parser)) {
15476 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK);
15477 } else if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_INNER) {
15478 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK);
15479 } else if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
15480 numbered_parameter = MAX(numbered_parameter, (uint8_t) (parser->start[node->location.start + 1] - '0'));
15481 } else {
15482 assert(false && "unreachable");
15483 }
15484 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15485 if (numbered_parameter > 0) {
15486 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_NUMBERED);
15487 } else {
15488 it_parameter = true;
15489 }
15490 }
15491 }
15492
15493 if (numbered_parameter > 0) {
15494 // Go through the parent scopes and mark them as being disallowed from
15495 // using numbered parameters because this inner scope is using them.
15496 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15497 scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_INNER;
15498 }
15499 return UP(pm_numbered_parameters_node_create(parser, opening, closing, numbered_parameter));
15500 }
15501
15502 if (it_parameter) {
15503 return UP(pm_it_parameters_node_create(parser, opening, closing));
15504 }
15505
15506 return NULL;
15507}
15508
15512static pm_block_node_t *
15513parse_block(pm_parser_t *parser, uint16_t depth) {
15514 pm_token_t opening = parser->previous;
15515 accept1(parser, PM_TOKEN_NEWLINE);
15516
15517 /* A brace block is delimited by `{`/`}`, whose block-accepting frame is
15518 * managed by the lexer. A `do`/`end` block is delimited by keywords, so we
15519 * push the frame here (covering the block parameters and body) and pop it
15520 * before consuming `end`, mirroring parse.y's `do_body` rule. */
15521 bool do_block = opening.type != PM_TOKEN_BRACE_LEFT && opening.type != PM_TOKEN_BRACE_LEFT_ARGUMENT;
15522 if (do_block) pm_accepts_block_stack_push(parser, true);
15523 pm_parser_scope_push(parser, false);
15524
15525 pm_block_parameters_node_t *block_parameters = NULL;
15526
15527 if (accept1(parser, PM_TOKEN_PIPE)) {
15528 pm_token_t block_parameters_opening = parser->previous;
15529 if (match1(parser, PM_TOKEN_PIPE)) {
15530 block_parameters = pm_block_parameters_node_create(parser, NULL, &block_parameters_opening);
15531 parser->command_start = true;
15532 parser_lex(parser);
15533 } else {
15534 block_parameters = parse_block_parameters(parser, true, &block_parameters_opening, false, true, (uint16_t) (depth + 1));
15535 accept1(parser, PM_TOKEN_NEWLINE);
15536 parser->command_start = true;
15537 expect1(parser, PM_TOKEN_PIPE, PM_ERR_BLOCK_PARAM_PIPE_TERM);
15538 }
15539
15540 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
15541 }
15542
15543 accept1(parser, PM_TOKEN_NEWLINE);
15544 pm_node_t *statements = NULL;
15545
15546 if (!do_block) {
15547 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
15548 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1)));
15549 }
15550
15551 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening);
15552 } else {
15553 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15554 if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) {
15555 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS, (uint16_t) (depth + 1)));
15556 }
15557
15558 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
15559 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
15560 statements = UP(parse_rescues_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1)));
15561 }
15562 }
15563
15564 /* Pop the `do`/`end` frame before consuming `end` so the token
15565 * following the block is lexed in the enclosing context. */
15566 pm_accepts_block_stack_pop(parser);
15567 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening);
15568 }
15569
15570 pm_constant_id_list_t locals;
15571 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
15572 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &opening, &parser->previous);
15573
15574 pm_parser_scope_pop(parser);
15575 return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous);
15576}
15577
15589static bool
15590parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_arguments, uint8_t flags, uint16_t depth) {
15591 /* Fast path: if the current token can't begin an expression and isn't
15592 * a parenthesis, block opener, or splat/block-pass operator, there are
15593 * no arguments to parse. */
15594 if (
15595 !token_begins_expression_p(parser->current.type) &&
15596 !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)
15597 ) {
15598 return false;
15599 }
15600
15601 bool found = false;
15602 bool parsed_command_args = false;
15603
15604 if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
15605 found |= true;
15606 arguments->opening_loc = TOK2LOC(parser, &parser->previous);
15607
15608 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15609 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15610 } else {
15611 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_PARENTHESIS_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
15612
15613 // `yield` parses its arguments through the restricted `call_args`
15614 // grammar, which (unlike the `opt_call_args` that method calls and
15615 // `super` use) permits neither a block argument nor a trailing
15616 // comma. `full_arguments` is false only for `yield`, so we use it
15617 // to reject the trailing comma in `yield(a,)` that the arguments
15618 // parser otherwise accepts before the closing parenthesis.
15619 if (!full_arguments && parser->previous.type == PM_TOKEN_COMMA) {
15620 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15621 }
15622
15623 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15624 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_str(parser->current.type));
15625 parser->previous.start = parser->previous.end;
15626 parser->previous.type = 0;
15627 }
15628
15629 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15630 }
15631 } else if ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR, PM_TOKEN_UAMPERSAND)) && !match1(parser, PM_TOKEN_BRACE_LEFT)) {
15632 found |= true;
15633 parsed_command_args = true;
15634
15635 /* The command-args frame does not accept blocks, so that a trailing
15636 * `do` binds to this command rather than to an argument. Mirroring
15637 * parse.y's `command_args` rule: when the first argument begins with an
15638 * opening delimiter, the lexer has already pushed that delimiter's
15639 * (block-accepting) frame. We must push the command-args frame beneath
15640 * it, so pop the delimiter frame, push the command-args frame, and then
15641 * restore the delimiter frame on top (the delimiter's closing token
15642 * will pop it back off during argument parsing). */
15643 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);
15644 if (lookahead_delimiter) pm_accepts_block_stack_pop(parser);
15645 pm_accepts_block_stack_push(parser, false);
15646 if (lookahead_delimiter) pm_accepts_block_stack_push(parser, true);
15647
15648 // If we get here, then the subsequent token cannot be used as an infix
15649 // operator. In this case we assume the subsequent token is part of an
15650 // argument to this method call.
15651 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
15652
15653 // If we have done with the arguments and still not consumed the comma,
15654 // then we have a trailing comma where we need to check whether it is
15655 // allowed or not.
15656 if (parser->previous.type == PM_TOKEN_COMMA && !match1(parser, PM_TOKEN_SEMICOLON)) {
15657 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15658 }
15659
15660 /* Symmetrically, if the command arguments are followed by a brace block
15661 * (`m args { }`), the lexer has already pushed that block's frame. Pop
15662 * it, pop the command-args frame beneath it, and restore the block
15663 * frame so the block's `}` still pops it. This mirrors the `tLBRACE_ARG`
15664 * lookahead handling in parse.y's `command_args` rule. */
15665 bool lookahead_brace = match2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT);
15666 if (lookahead_brace) pm_accepts_block_stack_pop(parser);
15667 pm_accepts_block_stack_pop(parser);
15668 if (lookahead_brace) pm_accepts_block_stack_push(parser, true);
15669 }
15670
15671 // If we're at the end of the arguments, we can now check if there is a block
15672 // node that starts with a {. If there is, then we can parse it and add it to
15673 // the arguments.
15674 if (full_arguments) {
15675 pm_block_node_t *block = NULL;
15676
15677 if (accept2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT)) {
15678 found |= true;
15679 block = parse_block(parser, (uint16_t) (depth + 1));
15680 pm_arguments_validate_block(parser, arguments, block);
15681 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
15682 found |= true;
15683 block = parse_block(parser, (uint16_t) (depth + 1));
15684 } else if (parsed_command_args && pm_accepts_block_stack_p(parser) && (flags & PM_PARSE_ACCEPTS_DO_BLOCK) && accept1(parser, PM_TOKEN_KEYWORD_DO_BLOCK)) {
15685 found |= true;
15686 block = parse_block(parser, (uint16_t) (depth + 1));
15687 }
15688
15689 if (block != NULL) {
15690 if (arguments->block == NULL && !arguments->has_forwarding) {
15691 arguments->block = UP(block);
15692 } else {
15693 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_BLOCK_MULTI);
15694
15695 if (arguments->block != NULL) {
15696 if (arguments->arguments == NULL) {
15697 arguments->arguments = pm_arguments_node_create(parser);
15698 }
15699 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, arguments->block);
15700 }
15701 arguments->block = UP(block);
15702 }
15703 }
15704 }
15705
15706 return found;
15707}
15708
15713static void
15714ensure_valid_return(pm_parser_t *parser, pm_node_t *node) {
15715 bool in_sclass = false;
15716 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15717 switch (context_node->context) {
15718 case PM_CONTEXT_BEGIN_ELSE:
15719 case PM_CONTEXT_BEGIN_ENSURE:
15720 case PM_CONTEXT_BEGIN_RESCUE:
15721 case PM_CONTEXT_BEGIN:
15722 case PM_CONTEXT_CASE_IN:
15723 case PM_CONTEXT_CASE_WHEN:
15724 case PM_CONTEXT_DEFAULT_PARAMS:
15725 case PM_CONTEXT_DEFINED:
15726 case PM_CONTEXT_ELSE:
15727 case PM_CONTEXT_ELSIF:
15728 case PM_CONTEXT_EMBEXPR:
15729 case PM_CONTEXT_FOR_INDEX:
15730 case PM_CONTEXT_FOR:
15731 case PM_CONTEXT_IF:
15732 case PM_CONTEXT_LOOP_PREDICATE:
15733 case PM_CONTEXT_MAIN:
15734 case PM_CONTEXT_MULTI_TARGET:
15735 case PM_CONTEXT_PARENS:
15736 case PM_CONTEXT_POSTEXE:
15737 case PM_CONTEXT_PREDICATE:
15738 case PM_CONTEXT_PREEXE:
15739 case PM_CONTEXT_RESCUE_MODIFIER:
15740 case PM_CONTEXT_TERNARY:
15741 case PM_CONTEXT_UNLESS:
15742 case PM_CONTEXT_UNTIL:
15743 case PM_CONTEXT_WHILE:
15744 // Keep iterating up the lists of contexts, because returns can
15745 // see through these.
15746 continue;
15747 case PM_CONTEXT_SCLASS_ELSE:
15748 case PM_CONTEXT_SCLASS_ENSURE:
15749 case PM_CONTEXT_SCLASS_RESCUE:
15750 case PM_CONTEXT_SCLASS:
15751 in_sclass = true;
15752 continue;
15753 case PM_CONTEXT_CLASS_ELSE:
15754 case PM_CONTEXT_CLASS_ENSURE:
15755 case PM_CONTEXT_CLASS_RESCUE:
15756 case PM_CONTEXT_CLASS:
15757 case PM_CONTEXT_MODULE_ELSE:
15758 case PM_CONTEXT_MODULE_ENSURE:
15759 case PM_CONTEXT_MODULE_RESCUE:
15760 case PM_CONTEXT_MODULE:
15761 // These contexts are invalid for a return.
15762 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15763 return;
15764 case PM_CONTEXT_BLOCK_BRACES:
15765 case PM_CONTEXT_BLOCK_ELSE:
15766 case PM_CONTEXT_BLOCK_ENSURE:
15767 case PM_CONTEXT_BLOCK_KEYWORDS:
15768 case PM_CONTEXT_BLOCK_RESCUE:
15769 case PM_CONTEXT_BLOCK_PARAMETERS:
15770 case PM_CONTEXT_DEF_ELSE:
15771 case PM_CONTEXT_DEF_ENSURE:
15772 case PM_CONTEXT_DEF_PARAMS:
15773 case PM_CONTEXT_DEF_RESCUE:
15774 case PM_CONTEXT_DEF:
15775 case PM_CONTEXT_LAMBDA_BRACES:
15776 case PM_CONTEXT_LAMBDA_DO_END:
15777 case PM_CONTEXT_LAMBDA_ELSE:
15778 case PM_CONTEXT_LAMBDA_ENSURE:
15779 case PM_CONTEXT_LAMBDA_RESCUE:
15780 // These contexts are valid for a return, and we should not
15781 // continue to loop.
15782 return;
15783 case PM_CONTEXT_NONE:
15784 case PM_CONTEXT_MAXIMUM:
15785 // This case should never happen.
15786 assert(false && "unreachable");
15787 break;
15788 }
15789 }
15790 if (in_sclass && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
15791 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15792 }
15793}
15794
15799static void
15800ensure_valid_jump(pm_parser_t *parser, pm_node_t *node) {
15801 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15802 switch (context_node->context) {
15803 case PM_CONTEXT_BLOCK_BRACES:
15804 case PM_CONTEXT_BLOCK_KEYWORDS:
15805 case PM_CONTEXT_BLOCK_ELSE:
15806 case PM_CONTEXT_BLOCK_ENSURE:
15807 case PM_CONTEXT_BLOCK_PARAMETERS:
15808 case PM_CONTEXT_BLOCK_RESCUE:
15809 case PM_CONTEXT_DEFINED:
15810 case PM_CONTEXT_FOR:
15811 case PM_CONTEXT_LAMBDA_BRACES:
15812 case PM_CONTEXT_LAMBDA_DO_END:
15813 case PM_CONTEXT_LAMBDA_ELSE:
15814 case PM_CONTEXT_LAMBDA_ENSURE:
15815 case PM_CONTEXT_LAMBDA_RESCUE:
15816 case PM_CONTEXT_LOOP_PREDICATE:
15817 case PM_CONTEXT_UNTIL:
15818 case PM_CONTEXT_WHILE:
15819 // These are the good cases. We're allowed to have a block exit
15820 // in these contexts.
15821 return;
15822 case PM_CONTEXT_POSTEXE:
15823 // https://bugs.ruby-lang.org/issues/20409
15824 if (context_node->context == PM_CONTEXT_POSTEXE) {
15825 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
15826 return;
15827 }
15828 }
15830 case PM_CONTEXT_DEF:
15831 case PM_CONTEXT_DEF_PARAMS:
15832 case PM_CONTEXT_DEF_ELSE:
15833 case PM_CONTEXT_DEF_ENSURE:
15834 case PM_CONTEXT_DEF_RESCUE:
15835 case PM_CONTEXT_MAIN:
15836 case PM_CONTEXT_PREEXE:
15837 case PM_CONTEXT_SCLASS:
15838 case PM_CONTEXT_SCLASS_ELSE:
15839 case PM_CONTEXT_SCLASS_ENSURE:
15840 case PM_CONTEXT_SCLASS_RESCUE:
15841 // These are the bad cases. We're not allowed to have a block
15842 // exit in these contexts.
15843 //
15844 // If we get here, then we're about to mark this block exit
15845 // as invalid. However, it could later _become_ valid if we
15846 // find a trailing while/until on the expression. In this
15847 // case instead of adding the error here, we'll add the
15848 // block exit to the list of exits for the expression, and
15849 // the node parsing will handle validating it instead.
15850 assert(parser->current_block_exits != NULL);
15851 pm_node_list_append(parser->arena, parser->current_block_exits, node);
15852 return;
15853 case PM_CONTEXT_BEGIN_ELSE:
15854 case PM_CONTEXT_BEGIN_ENSURE:
15855 case PM_CONTEXT_BEGIN_RESCUE:
15856 case PM_CONTEXT_BEGIN:
15857 case PM_CONTEXT_CASE_IN:
15858 case PM_CONTEXT_CASE_WHEN:
15859 case PM_CONTEXT_CLASS_ELSE:
15860 case PM_CONTEXT_CLASS_ENSURE:
15861 case PM_CONTEXT_CLASS_RESCUE:
15862 case PM_CONTEXT_CLASS:
15863 case PM_CONTEXT_DEFAULT_PARAMS:
15864 case PM_CONTEXT_ELSE:
15865 case PM_CONTEXT_ELSIF:
15866 case PM_CONTEXT_EMBEXPR:
15867 case PM_CONTEXT_FOR_INDEX:
15868 case PM_CONTEXT_IF:
15869 case PM_CONTEXT_MODULE_ELSE:
15870 case PM_CONTEXT_MODULE_ENSURE:
15871 case PM_CONTEXT_MODULE_RESCUE:
15872 case PM_CONTEXT_MODULE:
15873 case PM_CONTEXT_MULTI_TARGET:
15874 case PM_CONTEXT_PARENS:
15875 case PM_CONTEXT_PREDICATE:
15876 case PM_CONTEXT_RESCUE_MODIFIER:
15877 case PM_CONTEXT_TERNARY:
15878 case PM_CONTEXT_UNLESS:
15879 // In these contexts we should continue walking up the list of
15880 // contexts.
15881 break;
15882 case PM_CONTEXT_NONE:
15883 case PM_CONTEXT_MAXIMUM:
15884 // This case should never happen.
15885 assert(false && "unreachable");
15886 break;
15887 }
15888 }
15889}
15890
15895static pm_node_list_t *
15896push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
15897 pm_node_list_t *previous_block_exits = parser->current_block_exits;
15898 parser->current_block_exits = current_block_exits;
15899 return previous_block_exits;
15900}
15901
15907static void
15908flush_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
15909 pm_node_t *block_exit;
15910 PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
15911 const char *type;
15912
15913 switch (PM_NODE_TYPE(block_exit)) {
15914 case PM_BREAK_NODE: type = "break"; break;
15915 case PM_NEXT_NODE: type = "next"; break;
15916 case PM_REDO_NODE: type = "redo"; break;
15917 default: assert(false && "unreachable"); type = ""; break;
15918 }
15919
15920 PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
15921 }
15922
15923 parser->current_block_exits = previous_block_exits;
15924}
15925
15930static void
15931pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
15932 if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) {
15933 // If we matched a trailing while/until, then all of the block exits in
15934 // the contained list are valid. In this case we do not need to do
15935 // anything.
15936 parser->current_block_exits = previous_block_exits;
15937 } else if (previous_block_exits != NULL) {
15938 // If we did not matching a trailing while/until, then all of the block
15939 // exits contained in the list are invalid for this specific context.
15940 // However, they could still become valid in a higher level context if
15941 // there is another list above this one. In this case we'll push all of
15942 // the block exits up to the previous list.
15943 pm_node_list_concat(parser->arena, previous_block_exits, parser->current_block_exits);
15944 parser->current_block_exits = previous_block_exits;
15945 } else {
15946 // If we did not match a trailing while/until and this was the last
15947 // chance to do so, then all of the block exits in the list are invalid
15948 // and we need to add an error for each of them.
15949 flush_block_exits(parser, previous_block_exits);
15950 }
15951}
15952
15953static PRISM_INLINE pm_node_t *
15954parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword, uint16_t depth) {
15955 context_push(parser, PM_CONTEXT_PREDICATE);
15956 pm_diagnostic_id_t error_id = context == PM_CONTEXT_IF ? PM_ERR_CONDITIONAL_IF_PREDICATE : PM_ERR_CONDITIONAL_UNLESS_PREDICATE;
15957 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));
15958
15959 // Predicates are closed by a term, a "then", or a term and then a "then".
15960 bool predicate_closed = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15961
15962 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
15963 predicate_closed = true;
15964 *then_keyword = parser->previous;
15965 }
15966
15967 if (!predicate_closed) {
15968 pm_parser_err_current(parser, PM_ERR_CONDITIONAL_PREDICATE_TERM);
15969 }
15970
15971 context_pop(parser);
15972 return predicate;
15973}
15974
15975static PRISM_INLINE pm_node_t *
15976parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newline_index, bool if_after_else, uint16_t depth) {
15977 pm_node_list_t current_block_exits = { 0 };
15978 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
15979
15980 pm_token_t keyword = parser->previous;
15981 pm_token_t then_keyword = { 0 };
15982
15983 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, context, &then_keyword, (uint16_t) (depth + 1));
15984 pm_statements_node_t *statements = NULL;
15985
15986 if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
15987 pm_accepts_block_stack_push(parser, true);
15988 statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15989 pm_accepts_block_stack_pop(parser);
15990 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15991 }
15992
15993 pm_node_t *parent = NULL;
15994
15995 switch (context) {
15996 case PM_CONTEXT_IF:
15997 parent = UP(pm_if_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
15998 break;
15999 case PM_CONTEXT_UNLESS:
16000 parent = UP(pm_unless_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements));
16001 break;
16002 default:
16003 assert(false && "unreachable");
16004 break;
16005 }
16006
16007 pm_node_t *current = parent;
16008
16009 // Parse any number of elsif clauses. This will form a linked list of if
16010 // nodes pointing to each other from the top.
16011 if (context == PM_CONTEXT_IF) {
16012 while (match1(parser, PM_TOKEN_KEYWORD_ELSIF)) {
16013 if (parser_end_of_line_p(parser)) {
16014 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
16015 }
16016
16017 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
16018 pm_token_t elsif_keyword = parser->current;
16019 parser_lex(parser);
16020
16021 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1));
16022 pm_accepts_block_stack_push(parser, true);
16023
16024 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1));
16025 pm_accepts_block_stack_pop(parser);
16026 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16027
16028 pm_node_t *elsif = UP(pm_if_node_create(parser, &elsif_keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
16029 ((pm_if_node_t *) current)->subsequent = elsif;
16030 current = elsif;
16031 }
16032 }
16033
16034 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
16035 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
16036 opening_newline_index = token_newline_index(parser);
16037
16038 parser_lex(parser);
16039 pm_token_t else_keyword = parser->previous;
16040
16041 pm_accepts_block_stack_push(parser, true);
16042 pm_statements_node_t *else_statements = parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1));
16043 pm_accepts_block_stack_pop(parser);
16044
16045 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16046 parser_warn_indentation_mismatch(parser, opening_newline_index, &else_keyword, false, false);
16047 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE, &keyword);
16048
16049 pm_else_node_t *else_node = pm_else_node_create(parser, &else_keyword, else_statements, &parser->previous);
16050
16051 switch (context) {
16052 case PM_CONTEXT_IF:
16053 ((pm_if_node_t *) current)->subsequent = UP(else_node);
16054 break;
16055 case PM_CONTEXT_UNLESS:
16056 ((pm_unless_node_t *) parent)->else_clause = else_node;
16057 break;
16058 default:
16059 assert(false && "unreachable");
16060 break;
16061 }
16062 } else {
16063 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false);
16064 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM, &keyword);
16065 }
16066
16067 // Set the appropriate end location for all of the nodes in the subtree.
16068 switch (context) {
16069 case PM_CONTEXT_IF: {
16070 pm_node_t *current = parent;
16071 bool recursing = true;
16072
16073 while (recursing) {
16074 switch (PM_NODE_TYPE(current)) {
16075 case PM_IF_NODE:
16076 pm_if_node_end_keyword_loc_set(parser, (pm_if_node_t *) current, &parser->previous);
16077 current = ((pm_if_node_t *) current)->subsequent;
16078 recursing = current != NULL;
16079 break;
16080 case PM_ELSE_NODE:
16081 pm_else_node_end_keyword_loc_set(parser, (pm_else_node_t *) current, &parser->previous);
16082 recursing = false;
16083 break;
16084 default: {
16085 recursing = false;
16086 break;
16087 }
16088 }
16089 }
16090 break;
16091 }
16092 case PM_CONTEXT_UNLESS:
16093 pm_unless_node_end_keyword_loc_set(parser, (pm_unless_node_t *) parent, &parser->previous);
16094 break;
16095 default:
16096 assert(false && "unreachable");
16097 break;
16098 }
16099
16100 pop_block_exits(parser, previous_block_exits);
16101 return parent;
16102}
16103
16108#define PM_CASE_KEYWORD PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16109 case PM_TOKEN_KEYWORD_ALIAS: case PM_TOKEN_KEYWORD_AND: case PM_TOKEN_KEYWORD_BEGIN: case PM_TOKEN_KEYWORD_BEGIN_UPCASE: \
16110 case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_CASE: case PM_TOKEN_KEYWORD_CLASS: case PM_TOKEN_KEYWORD_DEF: \
16111 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: \
16112 case PM_TOKEN_KEYWORD_ELSIF: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_END_UPCASE: case PM_TOKEN_KEYWORD_ENSURE: \
16113 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD_FOR: case PM_TOKEN_KEYWORD_IF: case PM_TOKEN_KEYWORD_IN: \
16114 case PM_TOKEN_KEYWORD_MODULE: case PM_TOKEN_KEYWORD_NEXT: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_NOT: \
16115 case PM_TOKEN_KEYWORD_OR: case PM_TOKEN_KEYWORD_REDO: case PM_TOKEN_KEYWORD_RESCUE: case PM_TOKEN_KEYWORD_RETRY: \
16116 case PM_TOKEN_KEYWORD_RETURN: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_SUPER: case PM_TOKEN_KEYWORD_THEN: \
16117 case PM_TOKEN_KEYWORD_TRUE: case PM_TOKEN_KEYWORD_UNDEF: case PM_TOKEN_KEYWORD_UNLESS: case PM_TOKEN_KEYWORD_UNTIL: \
16118 case PM_TOKEN_KEYWORD_WHEN: case PM_TOKEN_KEYWORD_WHILE: case PM_TOKEN_KEYWORD_YIELD
16119
16124#define PM_CASE_OPERATOR PM_TOKEN_AMPERSAND: case PM_TOKEN_BACKTICK: case PM_TOKEN_BANG_EQUAL: \
16125 case PM_TOKEN_BANG_TILDE: case PM_TOKEN_BANG: case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \
16126 case PM_TOKEN_BRACKET_LEFT_RIGHT: case PM_TOKEN_CARET: case PM_TOKEN_EQUAL_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL: \
16127 case PM_TOKEN_EQUAL_TILDE: case PM_TOKEN_GREATER_EQUAL: case PM_TOKEN_GREATER_GREATER: case PM_TOKEN_GREATER: \
16128 case PM_TOKEN_LESS_EQUAL_GREATER: case PM_TOKEN_LESS_EQUAL: case PM_TOKEN_LESS_LESS: case PM_TOKEN_LESS: \
16129 case PM_TOKEN_MINUS: case PM_TOKEN_PERCENT: case PM_TOKEN_PIPE: case PM_TOKEN_PLUS: case PM_TOKEN_SLASH: \
16130 case PM_TOKEN_STAR_STAR: case PM_TOKEN_STAR: case PM_TOKEN_TILDE: case PM_TOKEN_UAMPERSAND: case PM_TOKEN_UMINUS: \
16131 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_UPLUS: case PM_TOKEN_USTAR: case PM_TOKEN_USTAR_STAR
16132
16138#define PM_CASE_PRIMITIVE PM_TOKEN_INTEGER: case PM_TOKEN_INTEGER_IMAGINARY: case PM_TOKEN_INTEGER_RATIONAL: \
16139 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: case PM_TOKEN_FLOAT: case PM_TOKEN_FLOAT_IMAGINARY: \
16140 case PM_TOKEN_FLOAT_RATIONAL: case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: case PM_TOKEN_SYMBOL_BEGIN: \
16141 case PM_TOKEN_REGEXP_BEGIN: case PM_TOKEN_XSTRING_BEGIN: case PM_TOKEN_PERCENT_LOWER_X: case PM_TOKEN_PERCENT_LOWER_I: \
16142 case PM_TOKEN_PERCENT_LOWER_W: case PM_TOKEN_PERCENT_UPPER_I: case PM_TOKEN_PERCENT_UPPER_W: \
16143 case PM_TOKEN_STRING_BEGIN: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_TRUE: \
16144 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16145 case PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_MINUS_GREATER: case PM_TOKEN_HEREDOC_START: \
16146 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_CHARACTER_LITERAL
16147
16152#define PM_CASE_PARAMETER PM_TOKEN_UAMPERSAND: case PM_TOKEN_AMPERSAND: case PM_TOKEN_UDOT_DOT_DOT: \
16153 case PM_TOKEN_IDENTIFIER: case PM_TOKEN_LABEL: case PM_TOKEN_USTAR: case PM_TOKEN_STAR: case PM_TOKEN_STAR_STAR: \
16154 case PM_TOKEN_USTAR_STAR: case PM_TOKEN_CONSTANT: case PM_TOKEN_INSTANCE_VARIABLE: case PM_TOKEN_GLOBAL_VARIABLE: \
16155 case PM_TOKEN_CLASS_VARIABLE
16156
16161#define PM_CASE_WRITABLE PM_CLASS_VARIABLE_READ_NODE: case PM_CONSTANT_PATH_NODE: \
16162 case PM_CONSTANT_READ_NODE: case PM_GLOBAL_VARIABLE_READ_NODE: case PM_LOCAL_VARIABLE_READ_NODE: \
16163 case PM_INSTANCE_VARIABLE_READ_NODE: case PM_MULTI_TARGET_NODE: case PM_BACK_REFERENCE_READ_NODE: \
16164 case PM_NUMBERED_REFERENCE_READ_NODE: case PM_IT_LOCAL_VARIABLE_READ_NODE
16165
16166// Assert here that the flags are the same so that we can safely switch the type
16167// of the node without having to move the flags.
16168PM_STATIC_ASSERT(__LINE__, ((int) PM_STRING_FLAGS_FORCED_UTF8_ENCODING) == ((int) PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING), "Expected the flags to match.");
16169
16174static PRISM_INLINE pm_node_flags_t
16175parse_unescaped_encoding(const pm_parser_t *parser, const pm_encoding_t *explicit_encoding) {
16176 if (explicit_encoding != NULL) {
16177 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
16178 // If the there's an explicit encoding and it's using a UTF-8 escape
16179 // sequence, then mark the string as UTF-8.
16180 return PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
16181 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
16182 // If there's a non-UTF-8 escape sequence being used, then the
16183 // string uses the source encoding, unless the source is marked as
16184 // US-ASCII. In that case the string is forced as ASCII-8BIT in
16185 // order to keep the string valid.
16186 return PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
16187 }
16188 }
16189 return 0;
16190}
16191
16196static pm_node_t *
16197parse_string_part(pm_parser_t *parser, uint16_t depth) {
16198 switch (parser->current.type) {
16199 // Here the lexer has returned to us plain string content. In this case
16200 // we'll create a string node that has no opening or closing and return that
16201 // as the part. These kinds of parts look like:
16202 //
16203 // "aaa #{bbb} #@ccc ddd"
16204 // ^^^^ ^ ^^^^
16205 case PM_TOKEN_STRING_CONTENT: {
16206 pm_node_t *node = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16207 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
16208
16209 parser_lex(parser);
16210 return node;
16211 }
16212 // Here the lexer has returned the beginning of an embedded expression. In
16213 // that case we'll parse the inner statements and return that as the part.
16214 // These kinds of parts look like:
16215 //
16216 // "aaa #{bbb} #@ccc ddd"
16217 // ^^^^^^
16218 case PM_TOKEN_EMBEXPR_BEGIN: {
16219 // Ruby disallows seeing encoding around interpolation in strings,
16220 // even though it is known at parse time.
16221 parser->explicit_encoding = NULL;
16222
16223 pm_lex_state_t state = parser->lex_state;
16224 int brace_nesting = parser->brace_nesting;
16225
16226 parser->brace_nesting = 0;
16227 lex_state_set(parser, PM_LEX_STATE_BEG);
16228 parser_lex(parser);
16229
16230 pm_token_t opening = parser->previous;
16231 pm_statements_node_t *statements = NULL;
16232
16233 if (!match3(parser, PM_TOKEN_EMBEXPR_END, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
16234 statements = parse_statements(parser, PM_CONTEXT_EMBEXPR, (uint16_t) (depth + 1));
16235 }
16236
16237 parser->brace_nesting = brace_nesting;
16238 lex_state_set(parser, state);
16239 expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END);
16240
16241 // If this set of embedded statements only contains a single
16242 // statement, then Ruby does not consider it as a possible statement
16243 // that could emit a line event.
16244 if (statements != NULL && statements->body.size == 1) {
16245 pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE);
16246 }
16247
16248 return UP(pm_embedded_statements_node_create(parser, &opening, statements, &parser->previous));
16249 }
16250
16251 // Here the lexer has returned the beginning of an embedded variable.
16252 // In that case we'll parse the variable and create an appropriate node
16253 // for it and then return that node. These kinds of parts look like:
16254 //
16255 // "aaa #{bbb} #@ccc ddd"
16256 // ^^^^^
16257 case PM_TOKEN_EMBVAR: {
16258 // Ruby disallows seeing encoding around interpolation in strings,
16259 // even though it is known at parse time.
16260 parser->explicit_encoding = NULL;
16261
16262 lex_state_set(parser, PM_LEX_STATE_BEG);
16263 parser_lex(parser);
16264
16265 pm_token_t operator = parser->previous;
16266 pm_node_t *variable;
16267
16268 switch (parser->current.type) {
16269 // In this case a back reference is being interpolated. We'll
16270 // create a global variable read node.
16271 case PM_TOKEN_BACK_REFERENCE:
16272 parser_lex(parser);
16273 variable = UP(pm_back_reference_read_node_create(parser, &parser->previous));
16274 break;
16275 // In this case an nth reference is being interpolated. We'll
16276 // create a global variable read node.
16277 case PM_TOKEN_NUMBERED_REFERENCE:
16278 parser_lex(parser);
16279 variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16280 break;
16281 // In this case a global variable is being interpolated. We'll
16282 // create a global variable read node.
16283 case PM_TOKEN_GLOBAL_VARIABLE:
16284 parser_lex(parser);
16285 variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
16286 break;
16287 // In this case an instance variable is being interpolated.
16288 // We'll create an instance variable read node.
16289 case PM_TOKEN_INSTANCE_VARIABLE:
16290 parser_lex(parser);
16291 variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
16292 break;
16293 // In this case a class variable is being interpolated. We'll
16294 // create a class variable read node.
16295 case PM_TOKEN_CLASS_VARIABLE:
16296 parser_lex(parser);
16297 variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
16298 break;
16299 // We can hit here if we got an invalid token. In that case
16300 // we'll not attempt to lex this token and instead just return a
16301 // missing node.
16302 default:
16303 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EMBVAR_INVALID);
16304 variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16305 break;
16306 }
16307
16308 return UP(pm_embedded_variable_node_create(parser, &operator, variable));
16309 }
16310 default:
16311 parser_lex(parser);
16312 pm_parser_err_previous(parser, PM_ERR_CANNOT_PARSE_STRING_PART);
16313 return NULL;
16314 }
16315}
16316
16322static const uint8_t *
16323parse_operator_symbol_name(const pm_token_t *name) {
16324 switch (name->type) {
16325 case PM_TOKEN_TILDE:
16326 case PM_TOKEN_BANG:
16327 if (name->end[-1] == '@') return name->end - 1;
16329 default:
16330 return name->end;
16331 }
16332}
16333
16334static pm_node_t *
16335parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_state_t next_state) {
16336 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, opening, &parser->current, NULL);
16337 const uint8_t *end = parse_operator_symbol_name(&parser->current);
16338
16339 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16340 parser_lex(parser);
16341
16342 pm_string_shared_init(&symbol->unescaped, parser->previous.start, end);
16343 pm_node_flag_set(UP(symbol), PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING);
16344
16345 return UP(symbol);
16346}
16347
16353static pm_node_t *
16354parse_symbol(pm_parser_t *parser, pm_lex_state_t next_state, uint16_t depth) {
16355 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
16356 parser_lex(parser);
16357
16358 const pm_token_t opening = parser->previous;
16359
16360 if (lex_mode.mode != PM_LEX_STRING) {
16361 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16362
16363 switch (parser->current.type) {
16364 case PM_CASE_OPERATOR:
16365 return parse_operator_symbol(parser, &opening, next_state == PM_LEX_STATE_NONE ? PM_LEX_STATE_ENDFN : next_state);
16366 case PM_TOKEN_IDENTIFIER:
16367 case PM_TOKEN_CONSTANT:
16368 case PM_TOKEN_INSTANCE_VARIABLE:
16369 case PM_TOKEN_METHOD_NAME:
16370 case PM_TOKEN_CLASS_VARIABLE:
16371 case PM_TOKEN_GLOBAL_VARIABLE:
16372 case PM_TOKEN_NUMBERED_REFERENCE:
16373 case PM_TOKEN_BACK_REFERENCE:
16374 case PM_CASE_KEYWORD:
16375 parser_lex(parser);
16376 break;
16377 default:
16378 expect2(parser, PM_TOKEN_IDENTIFIER, PM_TOKEN_METHOD_NAME, PM_ERR_SYMBOL_INVALID);
16379 break;
16380 }
16381
16382 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, NULL);
16383 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16384 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16385
16386 return UP(symbol);
16387 }
16388
16389 if (lex_mode.as.string.interpolation) {
16390 // If we have the end of the symbol, then we can return an empty symbol.
16391 if (match1(parser, PM_TOKEN_STRING_END)) {
16392 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16393 parser_lex(parser);
16394 pm_token_t content = {
16395 .type = PM_TOKEN_STRING_CONTENT,
16396 .start = parser->previous.start,
16397 .end = parser->previous.start
16398 };
16399
16400 return UP(pm_symbol_node_create(parser, &opening, &content, &parser->previous));
16401 }
16402
16403 // Now we can parse the first part of the symbol.
16404 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
16405
16406 // If we got a string part, then it's possible that we could transform
16407 // what looks like an interpolated symbol into a regular symbol.
16408 if (part && PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16409 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16410 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16411
16412 return UP(pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous));
16413 }
16414
16415 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16416 if (part) pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16417
16418 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16419 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16420 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16421 }
16422 }
16423
16424 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16425 if (match1(parser, PM_TOKEN_EOF)) {
16426 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16427 } else {
16428 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16429 }
16430
16431 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16432 return UP(symbol);
16433 }
16434
16435 pm_token_t content;
16436 pm_string_t unescaped;
16437
16438 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16439 content = parser->current;
16440 unescaped = parser->current_string;
16441 parser_lex(parser);
16442
16443 // If we have two string contents in a row, then the content of this
16444 // symbol is split because of heredoc contents. This looks like:
16445 //
16446 // <<A; :'a
16447 // A
16448 // b'
16449 //
16450 // In this case, the best way we have to represent this is as an
16451 // interpolated string node, so that's what we'll do here.
16452 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16453 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16454 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16455 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16456
16457 part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->current, NULL, &parser->current_string));
16458 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16459
16460 if (next_state != PM_LEX_STATE_NONE) {
16461 lex_state_set(parser, next_state);
16462 }
16463
16464 parser_lex(parser);
16465 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16466
16467 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16468 return UP(symbol);
16469 }
16470 } else {
16471 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end };
16472 pm_string_shared_init(&unescaped, content.start, content.end);
16473 }
16474
16475 if (next_state != PM_LEX_STATE_NONE) {
16476 lex_state_set(parser, next_state);
16477 }
16478
16479 if (match1(parser, PM_TOKEN_EOF)) {
16480 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_DYNAMIC);
16481 } else {
16482 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16483 }
16484
16485 return UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, false)));
16486}
16487
16492static PRISM_INLINE pm_node_t *
16493parse_undef_argument(pm_parser_t *parser, uint16_t depth) {
16494 switch (parser->current.type) {
16495 case PM_CASE_OPERATOR:
16496 return parse_operator_symbol(parser, NULL, PM_LEX_STATE_NONE);
16497 case PM_CASE_KEYWORD:
16498 case PM_TOKEN_CONSTANT:
16499 case PM_TOKEN_IDENTIFIER:
16500 case PM_TOKEN_METHOD_NAME: {
16501 parser_lex(parser);
16502
16503 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16504 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16505 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16506
16507 return UP(symbol);
16508 }
16509 case PM_TOKEN_SYMBOL_BEGIN:
16510 return parse_symbol(parser, PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16511 default:
16512 pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT);
16513 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16514 }
16515}
16516
16523static PRISM_INLINE pm_node_t *
16524parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) {
16525 switch (parser->current.type) {
16526 case PM_CASE_OPERATOR:
16527 return parse_operator_symbol(parser, NULL, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE);
16528 case PM_CASE_KEYWORD:
16529 case PM_TOKEN_CONSTANT:
16530 case PM_TOKEN_IDENTIFIER:
16531 case PM_TOKEN_METHOD_NAME: {
16532 if (first) lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
16533 parser_lex(parser);
16534
16535 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16536 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16537 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16538
16539 return UP(symbol);
16540 }
16541 case PM_TOKEN_SYMBOL_BEGIN:
16542 return parse_symbol(parser, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16543 case PM_TOKEN_BACK_REFERENCE:
16544 parser_lex(parser);
16545 return UP(pm_back_reference_read_node_create(parser, &parser->previous));
16546 case PM_TOKEN_NUMBERED_REFERENCE:
16547 parser_lex(parser);
16548 return UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16549 case PM_TOKEN_GLOBAL_VARIABLE:
16550 parser_lex(parser);
16551 return UP(pm_global_variable_read_node_create(parser, &parser->previous));
16552 default:
16553 pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT);
16554 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16555 }
16556}
16557
16562static pm_node_t *
16563parse_variable(pm_parser_t *parser) {
16564 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &parser->previous);
16565 int depth;
16566 bool is_numbered_param = pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
16567
16568 if (!is_numbered_param && ((depth = pm_parser_local_depth_constant_id(parser, name_id)) != -1)) {
16569 return UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false));
16570 }
16571
16572 pm_scope_t *current_scope = parser->current_scope;
16573 if (!current_scope->closed && !(current_scope->parameters & PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED)) {
16574 if (is_numbered_param) {
16575 // When you use a numbered parameter, it implies the existence of
16576 // all of the locals that exist before it. For example, referencing
16577 // _2 means that _1 must exist. Therefore here we loop through all
16578 // of the possibilities and add them into the constant pool.
16579 uint8_t maximum = (uint8_t) (parser->previous.start[1] - '0');
16580 for (uint8_t number = 1; number <= maximum; number++) {
16581 pm_parser_local_add_constant(parser, pm_numbered_parameter_names[number - 1], 2);
16582 }
16583
16584 if (!match1(parser, PM_TOKEN_EQUAL)) {
16585 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_FOUND;
16586 }
16587
16588 pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false));
16589 pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16590
16591 return node;
16592 } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16593 pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous));
16594 pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16595
16596 return node;
16597 }
16598 }
16599
16600 return NULL;
16601}
16602
16606static pm_node_t *
16607parse_variable_call(pm_parser_t *parser) {
16608 pm_node_flags_t flags = 0;
16609
16610 if (!match1(parser, PM_TOKEN_PARENTHESIS_LEFT) && (parser->previous.end[-1] != '!') && (parser->previous.end[-1] != '?')) {
16611 pm_node_t *node = parse_variable(parser);
16612 if (node != NULL) return node;
16613 flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL;
16614 }
16615
16616 pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous);
16617 pm_node_flag_set(UP(node), flags);
16618
16619 return UP(node);
16620}
16621
16628parse_method_definition_name(pm_parser_t *parser) {
16629 switch (parser->current.type) {
16630 case PM_CASE_KEYWORD:
16631 case PM_TOKEN_CONSTANT:
16632 case PM_TOKEN_METHOD_NAME:
16633 parser_lex(parser);
16634 return parser->previous;
16635 case PM_TOKEN_IDENTIFIER:
16636 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current));
16637 parser_lex(parser);
16638 return parser->previous;
16639 case PM_CASE_OPERATOR:
16640 lex_state_set(parser, PM_LEX_STATE_ENDFN);
16641 parser_lex(parser);
16642 return parser->previous;
16643 default:
16644 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_NAME, pm_token_str(parser->current.type));
16645 return (pm_token_t) { .type = 0, .start = parser->current.start, .end = parser->current.end };
16646 }
16647}
16648
16649static void
16650parse_heredoc_dedent_string(pm_arena_t *arena, pm_string_t *string, size_t common_whitespace) {
16651 // Make a writable copy in the arena if the string isn't already writable.
16652 // We keep a mutable pointer to the arena memory so we can memmove into it
16653 // below without casting away const from the string's source field.
16654 uint8_t *writable;
16655
16656 if (string->type != PM_STRING_OWNED) {
16657 size_t length = pm_string_length(string);
16658 writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
16659 pm_string_constant_init(string, (const char *) writable, length);
16660 } else {
16661 writable = (uint8_t *) string->source;
16662 }
16663
16664 // Now get the bounds of the existing string. We'll use this as a
16665 // destination to move bytes into. We'll also use it for bounds checking
16666 // since we don't require that these strings be null terminated.
16667 size_t dest_length = pm_string_length(string);
16668 const uint8_t *source_cursor = writable;
16669 const uint8_t *source_end = source_cursor + dest_length;
16670
16671 // We're going to move bytes backward in the string when we get leading
16672 // whitespace, so we'll maintain a pointer to the current position in the
16673 // string that we're writing to.
16674 size_t trimmed_whitespace = 0;
16675
16676 // While we haven't reached the amount of common whitespace that we need to
16677 // trim and we haven't reached the end of the string, we'll keep trimming
16678 // whitespace. Trimming in this context means skipping over these bytes such
16679 // that they aren't copied into the new string.
16680 while ((source_cursor < source_end) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
16681 if (*source_cursor == '\t') {
16682 trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
16683 if (trimmed_whitespace > common_whitespace) break;
16684 } else {
16685 trimmed_whitespace++;
16686 }
16687
16688 source_cursor++;
16689 dest_length--;
16690 }
16691
16692 memmove(writable, source_cursor, (size_t) (source_end - source_cursor));
16693 string->length = dest_length;
16694}
16695
16700static PRISM_INLINE bool
16701heredoc_dedent_discard_string_node(pm_parser_t *parser, pm_string_node_t *string_node) {
16702 if (string_node->unescaped.length == 0) {
16703 const uint8_t *cursor = parser->start + PM_LOCATION_START(&string_node->content_loc);
16704 return pm_memchr(cursor, '\\', string_node->content_loc.length, parser->encoding_changed, parser->encoding) == NULL;
16705 }
16706 return false;
16707}
16708
16712static void
16713parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_whitespace) {
16714 // The next node should be dedented if it's the first node in the list or if
16715 // it follows a string node.
16716 bool dedent_next = true;
16717
16718 // Iterate over all nodes, and trim whitespace accordingly. We're going to
16719 // keep around two indices: a read and a write.
16720 size_t write_index = 0;
16721
16722 pm_node_t *node;
16723 PM_NODE_LIST_FOREACH(nodes, read_index, node) {
16724 // We're not manipulating child nodes that aren't strings. In this case
16725 // we'll skip past it and indicate that the subsequent node should not
16726 // be dedented.
16727 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE)) {
16728 nodes->nodes[write_index++] = node;
16729 dedent_next = false;
16730 continue;
16731 }
16732
16733 pm_string_node_t *string_node = ((pm_string_node_t *) node);
16734 if (dedent_next) {
16735 parse_heredoc_dedent_string(parser->arena, &string_node->unescaped, common_whitespace);
16736 }
16737
16738 if (heredoc_dedent_discard_string_node(parser, string_node)) {
16739 } else {
16740 nodes->nodes[write_index++] = node;
16741 }
16742
16743 // We always dedent the next node if it follows a string node.
16744 dedent_next = true;
16745 }
16746
16747 nodes->size = write_index;
16748}
16749
16753static pm_token_t
16754parse_strings_empty_content(const uint8_t *location) {
16755 return (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = location, .end = location };
16756}
16757
16761static PRISM_INLINE pm_node_t *
16762parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint16_t depth) {
16763 assert(parser->current.type == PM_TOKEN_STRING_BEGIN);
16764 bool concating = false;
16765
16766 while (match1(parser, PM_TOKEN_STRING_BEGIN)) {
16767 pm_node_t *node = NULL;
16768
16769 // Here we have found a string literal. We'll parse it and add it to
16770 // the list of strings.
16771 const pm_lex_mode_t *lex_mode = parser->lex_modes.current;
16772 assert(lex_mode->mode == PM_LEX_STRING);
16773 bool lex_interpolation = lex_mode->as.string.interpolation;
16774 bool label_allowed = lex_mode->as.string.label_allowed && accepts_label;
16775
16776 pm_token_t opening = parser->current;
16777 parser_lex(parser);
16778
16779 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16780 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16781 // If we get here, then we have an end immediately after a
16782 // start. In that case we'll create an empty content token and
16783 // return an uninterpolated string.
16784 pm_token_t content = parse_strings_empty_content(parser->previous.start);
16785 pm_string_node_t *string = pm_string_node_create(parser, &opening, &content, &parser->previous);
16786
16787 pm_string_shared_init(&string->unescaped, content.start, content.end);
16788 node = UP(string);
16789 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16790 // If we get here, then we have an end of a label immediately
16791 // after a start. In that case we'll create an empty symbol
16792 // node.
16793 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, NULL, &parser->previous);
16794 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.start);
16795 node = UP(symbol);
16796
16797 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16798 } else if (!lex_interpolation) {
16799 // If we don't accept interpolation then we expect the string to
16800 // start with a single string content node.
16801 pm_string_t unescaped;
16802 pm_token_t content;
16803
16804 if (match1(parser, PM_TOKEN_EOF)) {
16805 unescaped = PM_STRING_EMPTY;
16806 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start, .end = parser->start };
16807 } else {
16808 unescaped = parser->current_string;
16809 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_EXPECT_STRING_CONTENT);
16810 content = parser->previous;
16811 }
16812
16813 // It is unfortunately possible to have multiple string content
16814 // nodes in a row in the case that there's heredoc content in
16815 // the middle of the string, like this cursed example:
16816 //
16817 // <<-END+'b
16818 // a
16819 // END
16820 // c'+'d'
16821 //
16822 // In that case we need to switch to an interpolated string to
16823 // be able to contain all of the parts.
16824 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16825 pm_node_list_t parts = { 0 };
16826 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16827 pm_node_list_append(parser->arena, &parts, part);
16828
16829 do {
16830 part = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16831 pm_node_list_append(parser->arena, &parts, part);
16832 parser_lex(parser);
16833 } while (match1(parser, PM_TOKEN_STRING_CONTENT));
16834
16835 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16836 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16837 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16838 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, true)));
16839 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16840 } else if (match1(parser, PM_TOKEN_EOF)) {
16841 pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF);
16842 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16843 } else if (accept1(parser, PM_TOKEN_STRING_END)) {
16844 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16845 } else {
16846 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_str(parser->previous.type));
16847 parser->previous.start = parser->previous.end;
16848 parser->previous.type = 0;
16849 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16850 }
16851 } else if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16852 // In this case we've hit string content so we know the string
16853 // at least has something in it. We'll need to check if the
16854 // following token is the end (in which case we can return a
16855 // plain string) or if it's not then it has interpolation.
16856 pm_token_t content = parser->current;
16857 pm_string_t unescaped = parser->current_string;
16858 const pm_encoding_t *explicit_encoding = parser->explicit_encoding;
16859 parser_lex(parser);
16860
16861 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16862 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16863 pm_node_flag_set(node, parse_unescaped_encoding(parser, explicit_encoding));
16864
16865 // Kind of odd behavior, but basically if we have an
16866 // unterminated string and it ends in a newline, we back up one
16867 // character so that the error message is on the last line of
16868 // content in the string.
16869 if (!accept1(parser, PM_TOKEN_STRING_END)) {
16870 const uint8_t *location = parser->previous.end;
16871 if (location > parser->start && location[-1] == '\n') location--;
16872 pm_parser_err(parser, U32(location - parser->start), 0, PM_ERR_STRING_LITERAL_EOF);
16873
16874 parser->previous.start = parser->previous.end;
16875 parser->previous.type = 0;
16876 }
16877 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16878 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, explicit_encoding, &content, &unescaped, true)));
16879 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16880 } else {
16881 // If we get here, then we have interpolation so we'll need
16882 // to create a string or symbol node with interpolation.
16883 pm_node_list_t parts = { 0 };
16884 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
16885 pm_node_flag_set(part, parse_unescaped_encoding(parser, explicit_encoding));
16886 pm_node_list_append(parser->arena, &parts, part);
16887
16888 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
16889 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16890 pm_node_list_append(parser->arena, &parts, part);
16891 }
16892 }
16893
16894 if (accept1(parser, PM_TOKEN_LABEL_END)) {
16895 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
16896 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16897 } else if (match1(parser, PM_TOKEN_EOF)) {
16898 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
16899 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
16900 } else {
16901 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
16902 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16903 }
16904 }
16905 } else {
16906 // If we get here, then the first part of the string is not plain
16907 // string content, in which case we need to parse the string as an
16908 // interpolated string.
16909 pm_node_list_t parts = { 0 };
16910 pm_node_t *part;
16911
16912 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
16913 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16914 pm_node_list_append(parser->arena, &parts, part);
16915 }
16916 }
16917
16918 if (accept1(parser, PM_TOKEN_LABEL_END)) {
16919 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
16920 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16921 } else if (match1(parser, PM_TOKEN_EOF)) {
16922 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
16923 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
16924 } else {
16925 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
16926 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16927 }
16928 }
16929
16930 if (current == NULL) {
16931 // If the node we just parsed is a symbol node, then we can't
16932 // concatenate it with anything else, so we can now return that
16933 // node.
16934 if (PM_NODE_TYPE_P(node, PM_SYMBOL_NODE) || PM_NODE_TYPE_P(node, PM_INTERPOLATED_SYMBOL_NODE)) {
16935 return node;
16936 }
16937
16938 // If we don't already have a node, then it's fine and we can just
16939 // set the result to be the node we just parsed.
16940 current = node;
16941 } else {
16942 // Otherwise we need to check the type of the node we just parsed.
16943 // If it cannot be concatenated with the previous node, then we'll
16944 // need to add a syntax error.
16945 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE) && !PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
16946 pm_parser_err_node(parser, node, PM_ERR_STRING_CONCATENATION);
16947 }
16948
16949 // If we haven't already created our container for concatenation,
16950 // we'll do that now.
16951 if (!concating) {
16952 if (!PM_NODE_TYPE_P(current, PM_STRING_NODE) && !PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
16953 pm_parser_err_node(parser, current, PM_ERR_STRING_CONCATENATION);
16954 }
16955
16956 concating = true;
16957 pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
16958 pm_interpolated_string_node_append(parser, container, current);
16959 current = UP(container);
16960 }
16961
16962 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, node);
16963 }
16964 }
16965
16966 return current;
16967}
16968
16969#define PM_PARSE_PATTERN_SINGLE 0
16970#define PM_PARSE_PATTERN_TOP 1
16971#define PM_PARSE_PATTERN_MULTI 2
16972
16973static pm_node_t *
16974parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
16975
16981static void
16982parse_pattern_capture(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_constant_id_t capture, const pm_location_t *location) {
16983 // Skip this capture if it starts with an underscore.
16984 if (peek_at(parser, parser->start + location->start) == '_') return;
16985
16986 if (!pm_constant_id_set_insert(parser->arena, captures, capture)) {
16987 pm_parser_err(parser, location->start, location->length, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
16988 }
16989}
16990
16994static pm_node_t *
16995parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_t *node, uint16_t depth) {
16996 // Now, if there are any :: operators that follow, parse them as constant
16997 // path nodes.
16998 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
16999 pm_token_t delimiter = parser->previous;
17000 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
17001 node = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
17002 }
17003
17004 // If there is a [ or ( that follows, then this is part of a larger pattern
17005 // expression. We'll parse the inner pattern here, then modify the returned
17006 // inner pattern with our constant path attached.
17007 if (!match2(parser, PM_TOKEN_BRACKET_LEFT, PM_TOKEN_PARENTHESIS_LEFT)) {
17008 return node;
17009 }
17010
17011 pm_token_t opening;
17012 pm_token_t closing;
17013 pm_node_t *inner = NULL;
17014
17015 if (accept1(parser, PM_TOKEN_BRACKET_LEFT)) {
17016 opening = parser->previous;
17017 accept1(parser, PM_TOKEN_NEWLINE);
17018
17019 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17020 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
17021 accept1(parser, PM_TOKEN_NEWLINE);
17022 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
17023 }
17024
17025 closing = parser->previous;
17026 } else {
17027 parser_lex(parser);
17028 opening = parser->previous;
17029 accept1(parser, PM_TOKEN_NEWLINE);
17030
17031 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
17032 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17033 accept1(parser, PM_TOKEN_NEWLINE);
17034 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17035 }
17036
17037 closing = parser->previous;
17038 }
17039
17040 if (!inner) {
17041 // If there was no inner pattern, then we have something like Foo() or
17042 // Foo[]. In that case we'll create an array pattern with no requireds.
17043 return UP(pm_array_pattern_node_constant_create(parser, node, &opening, &closing));
17044 }
17045
17046 // Now that we have the inner pattern, check to see if it's an array, find,
17047 // or hash pattern. If it is, then we'll attach our constant path to it if
17048 // it doesn't already have a constant. If it's not one of those node types
17049 // or it does have a constant, then we'll create an array pattern.
17050 switch (PM_NODE_TYPE(inner)) {
17051 case PM_ARRAY_PATTERN_NODE: {
17052 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17053
17054 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17055 PM_NODE_START_SET_NODE(pattern_node, node);
17056 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17057
17058 pattern_node->constant = node;
17059 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17060 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17061
17062 return UP(pattern_node);
17063 }
17064
17065 break;
17066 }
17067 case PM_FIND_PATTERN_NODE: {
17068 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17069
17070 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17071 PM_NODE_START_SET_NODE(pattern_node, node);
17072 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17073
17074 pattern_node->constant = node;
17075 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17076 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17077
17078 return UP(pattern_node);
17079 }
17080
17081 break;
17082 }
17083 case PM_HASH_PATTERN_NODE: {
17084 pm_hash_pattern_node_t *pattern_node = (pm_hash_pattern_node_t *) inner;
17085
17086 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17087 PM_NODE_START_SET_NODE(pattern_node, node);
17088 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17089
17090 pattern_node->constant = node;
17091 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17092 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17093
17094 return UP(pattern_node);
17095 }
17096
17097 break;
17098 }
17099 default:
17100 break;
17101 }
17102
17103 // If we got here, then we didn't return one of the inner patterns by
17104 // attaching its constant. In this case we'll create an array pattern and
17105 // attach our constant to it.
17106 pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing);
17107 pm_array_pattern_node_requireds_append(parser->arena, pattern_node, inner);
17108 return UP(pattern_node);
17109}
17110
17114static pm_splat_node_t *
17115parse_pattern_rest(pm_parser_t *parser, pm_constant_id_set_t *captures) {
17116 assert(parser->previous.type == PM_TOKEN_USTAR);
17117 pm_token_t operator = parser->previous;
17118 pm_node_t *name = NULL;
17119
17120 // Rest patterns don't necessarily have a name associated with them. So we
17121 // will check for that here. If they do, then we'll add it to the local
17122 // table since this pattern will cause it to become a local variable.
17123 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17124 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17125
17126 int depth;
17127 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17128 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17129 }
17130
17131 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17132 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17133 name = UP(pm_local_variable_target_node_create(
17134 parser,
17135 &previous_loc,
17136 constant_id,
17137 (uint32_t) (depth == -1 ? 0 : depth)
17138 ));
17139 }
17140
17141 // Finally we can return the created node.
17142 return pm_splat_node_create(parser, &operator, name);
17143}
17144
17148static pm_node_t *
17149parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_set_t *captures) {
17150 assert(parser->current.type == PM_TOKEN_USTAR_STAR);
17151 parser_lex(parser);
17152
17153 pm_token_t operator = parser->previous;
17154 pm_node_t *value = NULL;
17155
17156 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
17157 return UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
17158 }
17159
17160 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17161 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17162
17163 int depth;
17164 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17165 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17166 }
17167
17168 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17169 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17170 value = UP(pm_local_variable_target_node_create(
17171 parser,
17172 &previous_loc,
17173 constant_id,
17174 (uint32_t) (depth == -1 ? 0 : depth)
17175 ));
17176 }
17177
17178 return UP(pm_assoc_splat_node_create(parser, value, &operator));
17179}
17180
17185static bool
17186pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
17187 ptrdiff_t length = end - start;
17188 if (length == 0) return false;
17189
17190 // First ensure that it starts with a valid identifier starting character.
17191 size_t width = char_is_identifier_start(parser, start, end - start);
17192 if (width == 0) return false;
17193
17194 // Next, ensure that it's not an uppercase character.
17195 if (parser->encoding_changed) {
17196 if (parser->encoding->isupper_char(start, length)) return false;
17197 } else {
17198 if (pm_encoding_utf_8_isupper_char(start, length)) return false;
17199 }
17200
17201 // Next, iterate through all of the bytes of the string to ensure that they
17202 // are all valid identifier characters.
17203 const uint8_t *cursor = start + width;
17204 while ((width = char_is_identifier(parser, cursor, end - cursor))) cursor += width;
17205 return cursor == end;
17206}
17207
17212static pm_node_t *
17213parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_symbol_node_t *key) {
17214 const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->content_loc;
17215 const uint8_t *start = parser->start + PM_LOCATION_START(value_loc);
17216 const uint8_t *end = parser->start + PM_LOCATION_END(value_loc);
17217
17218 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
17219 int depth = -1;
17220
17221 if (pm_slice_is_valid_local(parser, start, end)) {
17222 depth = pm_parser_local_depth_constant_id(parser, constant_id);
17223 } else {
17224 pm_parser_err(parser, PM_NODE_START(key), PM_NODE_LENGTH(key), PM_ERR_PATTERN_HASH_KEY_LOCALS);
17225
17226 if ((end > start) && ((end[-1] == '!') || (end[-1] == '?'))) {
17227 PM_PARSER_ERR_FORMAT(parser, value_loc->start, value_loc->length, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (end - start), (const char *) start);
17228 }
17229 }
17230
17231 if (depth == -1) {
17232 pm_parser_local_add(parser, constant_id, start, end, 0);
17233 }
17234
17235 parse_pattern_capture(parser, captures, constant_id, value_loc);
17236 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17237 parser,
17238 value_loc,
17239 constant_id,
17240 (uint32_t) (depth == -1 ? 0 : depth)
17241 );
17242
17243 return UP(pm_implicit_node_create(parser, UP(target)));
17244}
17245
17250static void
17251parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) {
17252 if (pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, keys, node, true) != NULL) {
17253 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE);
17254 }
17255}
17256
17261parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_t *first_node, uint16_t depth) {
17262 pm_node_list_t assocs = { 0 };
17263 pm_static_literals_t keys = { 0 };
17264 pm_node_t *rest = NULL;
17265
17266 switch (PM_NODE_TYPE(first_node)) {
17267 case PM_ASSOC_SPLAT_NODE:
17268 case PM_NO_KEYWORDS_PARAMETER_NODE:
17269 rest = first_node;
17270 break;
17271 case PM_INTERPOLATED_SYMBOL_NODE:
17272 case PM_SYMBOL_NODE: {
17273 if (pm_symbol_node_label_p(parser, first_node)) {
17274 if (PM_NODE_TYPE_P(first_node, PM_INTERPOLATED_SYMBOL_NODE)) {
17275 pm_parser_err_node(parser, first_node, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17276 } else {
17277 parse_pattern_hash_key(parser, &keys, first_node);
17278 }
17279
17280 pm_node_t *value;
17281
17282 /*
17283 * The label has an implicit value when the next token cannot
17284 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17285 * reduction.
17286 */
17287 if (!token_begins_pattern_p(parser->current.type)) {
17288 if (PM_NODE_TYPE_P(first_node, PM_SYMBOL_NODE)) {
17289 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
17290 } else {
17291 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(first_node), 0));
17292 }
17293 } else {
17294 // Here we have a value for the first assoc in the list, so
17295 // we will parse it now.
17296 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17297 }
17298
17299 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17300 pm_node_list_append(parser->arena, &assocs, assoc);
17301 break;
17302 }
17303 }
17305 default: {
17306 // If we get anything else, then this is an error. For this we'll
17307 // create a missing node for the value and create an assoc node for
17308 // the first node in the list.
17309 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;
17310 pm_parser_err_node(parser, first_node, diag_id);
17311
17312 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_START(first_node), PM_NODE_LENGTH(first_node)));
17313 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17314
17315 pm_node_list_append(parser->arena, &assocs, assoc);
17316 break;
17317 }
17318 }
17319
17320 // If there are any other assocs, then we'll parse them now.
17321 while (accept1(parser, PM_TOKEN_COMMA)) {
17322 /*
17323 * A trailing comma ends the pattern when the next token cannot begin
17324 * another element, mirroring the grammar's `p_kwargs: p_kwarg ','`
17325 * reduction.
17326 */
17327 if (!token_begins_pattern_p(parser->current.type)) {
17328 // Trailing commas are not allowed to follow a rest pattern.
17329 if (rest != NULL) {
17330 pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17331 }
17332
17333 break;
17334 }
17335
17336 if (match1(parser, PM_TOKEN_USTAR_STAR)) {
17337 pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures);
17338
17339 if (rest == NULL) {
17340 rest = assoc;
17341 } else {
17342 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17343 pm_node_list_append(parser->arena, &assocs, assoc);
17344 }
17345 } else {
17346 pm_node_t *key;
17347
17348 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
17349 key = parse_strings(parser, NULL, true, (uint16_t) (depth + 1));
17350
17351 if (PM_NODE_TYPE_P(key, PM_INTERPOLATED_SYMBOL_NODE)) {
17352 pm_parser_err_node(parser, key, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17353 } else if (!pm_symbol_node_label_p(parser, key)) {
17354 pm_parser_err_node(parser, key, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17355 }
17356 } else if (accept1(parser, PM_TOKEN_LABEL)) {
17357 key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17358 } else {
17359 expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17360
17361 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = parser->previous.end, .end = parser->previous.end };
17362 key = UP(pm_symbol_node_create(parser, NULL, &label, NULL));
17363 }
17364
17365 parse_pattern_hash_key(parser, &keys, key);
17366 pm_node_t *value = NULL;
17367
17368 /*
17369 * The label has an implicit value when the next token cannot
17370 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17371 * reduction.
17372 */
17373 if (!token_begins_pattern_p(parser->current.type)) {
17374 if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) {
17375 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key);
17376 } else {
17377 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(key), 0));
17378 }
17379 } else {
17380 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17381 }
17382
17383 pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, NULL, value));
17384
17385 if (rest != NULL) {
17386 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17387 }
17388
17389 pm_node_list_append(parser->arena, &assocs, assoc);
17390 }
17391 }
17392
17393 pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest);
17394 // assocs.nodes is arena-allocated; no explicit free needed.
17395
17396 pm_static_literals_free(&keys);
17397 return node;
17398}
17399
17403static pm_node_t *
17404parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_diagnostic_id_t diag_id, uint16_t depth) {
17405 switch (parser->current.type) {
17406 case PM_TOKEN_IDENTIFIER:
17407 case PM_TOKEN_METHOD_NAME: {
17408 parser_lex(parser);
17409 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17410
17411 int depth;
17412 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17413 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17414 }
17415
17416 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17417 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17418 return UP(pm_local_variable_target_node_create(
17419 parser,
17420 &previous_loc,
17421 constant_id,
17422 (uint32_t) (depth == -1 ? 0 : depth)
17423 ));
17424 }
17425 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
17426 pm_token_t opening = parser->current;
17427 parser_lex(parser);
17428
17429 if (accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17430 // If we have an empty array pattern, then we'll just return a new
17431 // array pattern node.
17432 return UP(pm_array_pattern_node_empty_create(parser, &opening, &parser->previous));
17433 }
17434
17435 // Otherwise, we'll parse the inner pattern, then deal with it depending
17436 // on the type it returns.
17437 pm_node_t *inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
17438
17439 accept1(parser, PM_TOKEN_NEWLINE);
17440 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
17441 pm_token_t closing = parser->previous;
17442
17443 switch (PM_NODE_TYPE(inner)) {
17444 case PM_ARRAY_PATTERN_NODE: {
17445 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17446 if (pattern_node->opening_loc.length == 0) {
17447 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17448 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17449
17450 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17451 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17452
17453 return UP(pattern_node);
17454 }
17455
17456 break;
17457 }
17458 case PM_FIND_PATTERN_NODE: {
17459 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17460 if (pattern_node->opening_loc.length == 0) {
17461 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17462 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17463
17464 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17465 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17466
17467 return UP(pattern_node);
17468 }
17469
17470 break;
17471 }
17472 default:
17473 break;
17474 }
17475
17476 pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing);
17477 pm_array_pattern_node_requireds_append(parser->arena, node, inner);
17478 return UP(node);
17479 }
17480 case PM_TOKEN_BRACE_LEFT_HASH: {
17481 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17482 parser->pattern_matching_newlines = false;
17483
17485 pm_token_t opening = parser->current;
17486 parser_lex(parser);
17487
17488 if (accept1(parser, PM_TOKEN_BRACE_RIGHT)) {
17489 // If we have an empty hash pattern, then we'll just return a new hash
17490 // pattern node.
17491 node = pm_hash_pattern_node_empty_create(parser, &opening, &parser->previous);
17492 } else {
17493 pm_node_t *first_node;
17494
17495 switch (parser->current.type) {
17496 case PM_TOKEN_LABEL:
17497 parser_lex(parser);
17498 first_node = UP(pm_symbol_node_label_create(parser, &parser->previous));
17499 break;
17500 case PM_TOKEN_USTAR_STAR:
17501 first_node = parse_pattern_keyword_rest(parser, captures);
17502 break;
17503 case PM_TOKEN_STRING_BEGIN:
17504 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));
17505 break;
17506 default: {
17507 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_str(parser->current.type));
17508 parser_lex(parser);
17509
17510 first_node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
17511 break;
17512 }
17513 }
17514
17515 node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1));
17516
17517 accept1(parser, PM_TOKEN_NEWLINE);
17518 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE, &opening);
17519 pm_token_t closing = parser->previous;
17520
17521 PM_NODE_START_SET_TOKEN(parser, node, &opening);
17522 PM_NODE_LENGTH_SET_TOKEN(parser, node, &closing);
17523
17524 node->opening_loc = TOK2LOC(parser, &opening);
17525 node->closing_loc = TOK2LOC(parser, &closing);
17526 }
17527
17528 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17529 return UP(node);
17530 }
17531 case PM_TOKEN_UDOT_DOT:
17532 case PM_TOKEN_UDOT_DOT_DOT: {
17533 pm_token_t operator = parser->current;
17534 parser_lex(parser);
17535
17536 // Since we have a unary range operator, we need to parse the subsequent
17537 // expression as the right side of the range.
17538 switch (parser->current.type) {
17539 case PM_CASE_PRIMITIVE: {
17540 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));
17541 return UP(pm_range_node_create(parser, NULL, &operator, right));
17542 }
17543 default: {
17544 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE);
17545 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17546 return UP(pm_range_node_create(parser, NULL, &operator, right));
17547 }
17548 }
17549 }
17550 case PM_CASE_PRIMITIVE: {
17551 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));
17552
17553 // If we found a label, we need to immediately return to the caller.
17554 if (pm_symbol_node_label_p(parser, node)) return node;
17555
17556 // Call nodes (arithmetic operations) are not allowed in patterns
17557 if (PM_NODE_TYPE(node) == PM_CALL_NODE) {
17558 pm_parser_err_node(parser, node, diag_id);
17559 return UP(pm_error_recovery_node_create_unexpected(parser, node));
17560 }
17561
17562 // Now that we have a primitive, we need to check if it's part of a range.
17563 if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
17564 pm_token_t operator = parser->previous;
17565
17566 // Now that we have the operator, we need to check if this is followed
17567 // by another expression. If it is, then we will create a full range
17568 // node. Otherwise, we'll create an endless range.
17569 switch (parser->current.type) {
17570 case PM_CASE_PRIMITIVE: {
17571 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));
17572 return UP(pm_range_node_create(parser, node, &operator, right));
17573 }
17574 default:
17575 return UP(pm_range_node_create(parser, node, &operator, NULL));
17576 }
17577 }
17578
17579 return node;
17580 }
17581 case PM_TOKEN_CARET: {
17582 parser_lex(parser);
17583 pm_token_t operator = parser->previous;
17584
17585 // At this point we have a pin operator. We need to check the subsequent
17586 // expression to determine if it's a variable or an expression.
17587 switch (parser->current.type) {
17588 case PM_TOKEN_IDENTIFIER: {
17589 parser_lex(parser);
17590 pm_node_t *variable = UP(parse_variable(parser));
17591
17592 if (variable == NULL) {
17593 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->previous, PM_ERR_NO_LOCAL_VARIABLE);
17594 variable = UP(pm_local_variable_read_node_missing_create(parser, &parser->previous, 0));
17595 }
17596
17597 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17598 }
17599 case PM_TOKEN_INSTANCE_VARIABLE: {
17600 parser_lex(parser);
17601 pm_node_t *variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
17602
17603 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17604 }
17605 case PM_TOKEN_CLASS_VARIABLE: {
17606 parser_lex(parser);
17607 pm_node_t *variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
17608
17609 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17610 }
17611 case PM_TOKEN_GLOBAL_VARIABLE: {
17612 parser_lex(parser);
17613 pm_node_t *variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
17614
17615 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17616 }
17617 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
17618 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17619 parser->pattern_matching_newlines = false;
17620
17621 pm_token_t lparen = parser->current;
17622 parser_lex(parser);
17623
17624 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));
17625 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17626
17627 accept1(parser, PM_TOKEN_NEWLINE);
17628 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &lparen);
17629 return UP(pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous));
17630 }
17631 default: {
17632 // If we get here, then we have a pin operator followed by something
17633 // not understood. We'll create a missing node and return that.
17634 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN);
17635 pm_node_t *variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17636 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17637 }
17638 }
17639 }
17640 case PM_TOKEN_UCOLON_COLON: {
17641 pm_token_t delimiter = parser->current;
17642 parser_lex(parser);
17643
17644 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
17645 pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous);
17646
17647 return parse_pattern_constant_path(parser, captures, UP(node), (uint16_t) (depth + 1));
17648 }
17649 case PM_TOKEN_CONSTANT: {
17650 pm_token_t constant = parser->current;
17651 parser_lex(parser);
17652
17653 pm_node_t *node = UP(pm_constant_read_node_create(parser, &constant));
17654 return parse_pattern_constant_path(parser, captures, node, (uint16_t) (depth + 1));
17655 }
17656 default:
17657 pm_parser_err_current(parser, diag_id);
17658 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17659 }
17660}
17661
17669static bool
17670parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
17671 pm_parser_t *parser = (pm_parser_t *) data;
17672
17673 switch (PM_NODE_TYPE(node)) {
17674 case PM_LOCAL_VARIABLE_TARGET_NODE:
17675 // Underscore-prefixed names are not captures, see
17676 // parse_pattern_capture.
17677 if (peek_at(parser, parser->start + PM_NODE_START(node)) != '_') {
17678 pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
17679 }
17680 return false;
17681 case PM_ARRAY_PATTERN_NODE:
17682 case PM_ASSOC_NODE:
17683 case PM_ASSOC_SPLAT_NODE:
17684 case PM_CAPTURE_PATTERN_NODE:
17685 case PM_FIND_PATTERN_NODE:
17686 case PM_HASH_PATTERN_NODE:
17687 case PM_IMPLICIT_NODE:
17688 case PM_PARENTHESES_NODE:
17689 case PM_SPLAT_NODE:
17690 return true;
17691 default:
17692 return false;
17693 }
17694}
17695
17701static void
17702parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) {
17703 pm_visit_node(node, parse_pattern_alternation_error_each, parser);
17704}
17705
17710static pm_node_t *
17711parse_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) {
17712 pm_node_t *node = first_node;
17713 bool alternation = false;
17714
17715 while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) {
17716 if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) {
17717 parse_pattern_alternation_error(parser, node);
17718 }
17719
17720 switch (parser->current.type) {
17721 case PM_TOKEN_IDENTIFIER:
17722 case PM_TOKEN_BRACKET_LEFT_ARRAY:
17723 case PM_TOKEN_BRACE_LEFT_HASH:
17724 case PM_TOKEN_CARET:
17725 case PM_TOKEN_CONSTANT:
17726 case PM_TOKEN_UCOLON_COLON:
17727 case PM_TOKEN_UDOT_DOT:
17728 case PM_TOKEN_UDOT_DOT_DOT:
17729 case PM_CASE_PRIMITIVE: {
17730 if (!alternation) {
17731 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17732 } else {
17733 pm_token_t operator = parser->previous;
17734 pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1));
17735
17736 if (captures->size) parse_pattern_alternation_error(parser, right);
17737 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17738 }
17739
17740 break;
17741 }
17742 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
17743 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
17744 pm_token_t operator = parser->previous;
17745 pm_token_t opening = parser->current;
17746 parser_lex(parser);
17747
17748 pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17749 accept1(parser, PM_TOKEN_NEWLINE);
17750 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17751 pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0));
17752
17753 if (!alternation) {
17754 node = right;
17755 } else {
17756 if (captures->size) parse_pattern_alternation_error(parser, right);
17757 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17758 }
17759
17760 break;
17761 }
17762 default: {
17763 pm_parser_err_current(parser, diag_id);
17764 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17765
17766 if (!alternation) {
17767 node = right;
17768 } else {
17769 if (captures->size) parse_pattern_alternation_error(parser, right);
17770 node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous));
17771 }
17772
17773 break;
17774 }
17775 }
17776 }
17777
17778 // If we have an =>, then we are assigning this pattern to a variable.
17779 // In this case we should create an assignment node.
17780 while (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
17781 pm_token_t operator = parser->previous;
17782 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET);
17783
17784 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17785 int depth;
17786
17787 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17788 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17789 }
17790
17791 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17792 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17793 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17794 parser,
17795 &previous_loc,
17796 constant_id,
17797 (uint32_t) (depth == -1 ? 0 : depth)
17798 );
17799
17800 node = UP(pm_capture_pattern_node_create(parser, node, target, &operator));
17801 }
17802
17803 return node;
17804}
17805
17809static pm_node_t *
17810parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
17811 /* Nested patterns recurse back into parse_pattern without passing through
17812 * parse_expression, so this cycle needs its own depth check. */
17813 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
17814 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
17815 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17816 }
17817
17818 pm_node_t *node = NULL;
17819
17820 bool leading_rest = false;
17821 bool trailing_rest = false;
17822
17823 switch (parser->current.type) {
17824 case PM_TOKEN_LABEL: {
17825 parser_lex(parser);
17826 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17827 node = UP(parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)));
17828
17829 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17830 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17831 }
17832
17833 return node;
17834 }
17835 case PM_TOKEN_USTAR_STAR: {
17836 node = parse_pattern_keyword_rest(parser, captures);
17837 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17838
17839 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17840 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17841 }
17842
17843 return node;
17844 }
17845 case PM_TOKEN_STRING_BEGIN: {
17846 // We need special handling for string beginnings because they could
17847 // be dynamic symbols leading to hash patterns.
17848 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17849
17850 if (pm_symbol_node_label_p(parser, node)) {
17851 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17852
17853 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17854 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17855 }
17856
17857 return node;
17858 }
17859
17860 node = parse_pattern_primitives(parser, captures, node, diag_id, (uint16_t) (depth + 1));
17861 break;
17862 }
17863 case PM_TOKEN_USTAR: {
17864 if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) {
17865 parser_lex(parser);
17866 node = UP(parse_pattern_rest(parser, captures));
17867 leading_rest = true;
17868 break;
17869 }
17870 }
17872 default:
17873 node = parse_pattern_primitives(parser, captures, NULL, diag_id, (uint16_t) (depth + 1));
17874 break;
17875 }
17876
17877 // If we got a dynamic label symbol, then we need to treat it like the
17878 // beginning of a hash pattern.
17879 if (pm_symbol_node_label_p(parser, node)) {
17880 return UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17881 }
17882
17883 if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) {
17884 // If we have a comma, then we are now parsing either an array pattern
17885 // or a find pattern. We need to parse all of the patterns, put them
17886 // into a big list, and then determine which type of node we have.
17887 pm_node_list_t nodes = { 0 };
17888 pm_node_list_append(parser->arena, &nodes, node);
17889
17890 // Gather up all of the patterns into the list.
17891 while (accept1(parser, PM_TOKEN_COMMA)) {
17892 /*
17893 * A trailing comma ends the pattern when the next token cannot
17894 * begin another pattern element, leaving the token for the
17895 * enclosing context to accept or reject.
17896 */
17897 if (!token_begins_pattern_p(parser->current.type)) {
17898 // A trailing comma forms an implicit rest pattern (`[a,]` is
17899 // `[a, *]`). If a rest pattern has already been parsed, then
17900 // this is a second rest, which is not allowed (e.g. `[a, *b,]`
17901 // or `x => a, *b,`).
17902 if (trailing_rest) {
17903 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
17904 }
17905
17906 node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
17907 pm_node_list_append(parser->arena, &nodes, node);
17908 trailing_rest = true;
17909 break;
17910 }
17911
17912 if (accept1(parser, PM_TOKEN_USTAR)) {
17913 node = UP(parse_pattern_rest(parser, captures));
17914
17915 // If we have already parsed a splat pattern, then this is an
17916 // error. We will continue to parse the rest of the patterns,
17917 // but we will indicate it as an error.
17918 if (trailing_rest) {
17919 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
17920 }
17921
17922 trailing_rest = true;
17923 } else {
17924 node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
17925 }
17926
17927 pm_node_list_append(parser->arena, &nodes, node);
17928 }
17929
17930 // If the first pattern and the last pattern are rest patterns, then we
17931 // will call this a find pattern, regardless of how many rest patterns
17932 // are in between because we know we already added the appropriate
17933 // errors. Otherwise we will create an array pattern.
17934 if (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) {
17935 node = UP(pm_find_pattern_node_create(parser, &nodes));
17936
17937 if (nodes.size == 2) {
17938 pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER);
17939 }
17940 } else {
17941 node = UP(pm_array_pattern_node_node_list_create(parser, &nodes));
17942
17943 if (leading_rest && trailing_rest) {
17944 pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS);
17945 }
17946 }
17947
17948 // nodes.nodes is arena-allocated; no explicit free needed.
17949 } else if (leading_rest) {
17950 // Otherwise, if we parsed a single splat pattern, then we know we have
17951 // an array pattern, so we can go ahead and create that node.
17952 node = UP(pm_array_pattern_node_rest_create(parser, node));
17953 }
17954
17955 return node;
17956}
17957
17963static PRISM_INLINE void
17964parse_negative_numeric(pm_node_t *node) {
17965 switch (PM_NODE_TYPE(node)) {
17966 case PM_INTEGER_NODE: {
17967 pm_integer_node_t *cast = (pm_integer_node_t *) node;
17968 cast->base.location.start--;
17969 cast->base.location.length++;
17970 cast->value.negative = true;
17971 break;
17972 }
17973 case PM_FLOAT_NODE: {
17974 pm_float_node_t *cast = (pm_float_node_t *) node;
17975 cast->base.location.start--;
17976 cast->base.location.length++;
17977 cast->value = -cast->value;
17978 break;
17979 }
17980 case PM_RATIONAL_NODE: {
17981 pm_rational_node_t *cast = (pm_rational_node_t *) node;
17982 cast->base.location.start--;
17983 cast->base.location.length++;
17984 cast->numerator.negative = true;
17985 break;
17986 }
17987 case PM_IMAGINARY_NODE:
17988 node->location.start--;
17989 node->location.length++;
17990 parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric);
17991 break;
17992 default:
17993 assert(false && "unreachable");
17994 break;
17995 }
17996}
17997
18003static void
18004pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
18005 switch (diag_id) {
18006 case PM_ERR_HASH_KEY: {
18007 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, pm_token_str(parser->previous.type));
18008 break;
18009 }
18010 case PM_ERR_HASH_VALUE:
18011 case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: {
18012 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
18013 break;
18014 }
18015 case PM_ERR_UNARY_RECEIVER: {
18016 const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_str(parser->current.type));
18017 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, human, parser->previous.start[0]);
18018 break;
18019 }
18020 case PM_ERR_UNARY_DISALLOWED:
18021 case PM_ERR_EXPECT_ARGUMENT: {
18022 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
18023 break;
18024 }
18025 default:
18026 pm_parser_err_previous(parser, diag_id);
18027 break;
18028 }
18029}
18030
18034static void
18035ensure_valid_retry(pm_parser_t *parser, const pm_node_t *node) {
18036#define CONTEXT_NONE 0
18037#define CONTEXT_THROUGH_ENSURE 1
18038#define CONTEXT_THROUGH_ELSE 2
18039
18040 pm_context_node_t *context_node = parser->current_context;
18041 int context = CONTEXT_NONE;
18042
18043 while (context_node != NULL) {
18044 switch (context_node->context) {
18045 case PM_CONTEXT_BEGIN_RESCUE:
18046 case PM_CONTEXT_BLOCK_RESCUE:
18047 case PM_CONTEXT_CLASS_RESCUE:
18048 case PM_CONTEXT_DEF_RESCUE:
18049 case PM_CONTEXT_LAMBDA_RESCUE:
18050 case PM_CONTEXT_MODULE_RESCUE:
18051 case PM_CONTEXT_SCLASS_RESCUE:
18052 case PM_CONTEXT_DEFINED:
18053 case PM_CONTEXT_RESCUE_MODIFIER:
18054 // These are the good cases. We're allowed to have a retry here.
18055 return;
18056 case PM_CONTEXT_CLASS:
18057 case PM_CONTEXT_DEF:
18058 case PM_CONTEXT_DEF_PARAMS:
18059 case PM_CONTEXT_MAIN:
18060 case PM_CONTEXT_MODULE:
18061 case PM_CONTEXT_PREEXE:
18062 case PM_CONTEXT_SCLASS:
18063 // These are the bad cases. We're not allowed to have a retry in
18064 // these contexts.
18065 if (context == CONTEXT_NONE) {
18066 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE);
18067 } else if (context == CONTEXT_THROUGH_ENSURE) {
18068 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE);
18069 } else if (context == CONTEXT_THROUGH_ELSE) {
18070 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE);
18071 }
18072 return;
18073 case PM_CONTEXT_BEGIN_ELSE:
18074 case PM_CONTEXT_BLOCK_ELSE:
18075 case PM_CONTEXT_CLASS_ELSE:
18076 case PM_CONTEXT_DEF_ELSE:
18077 case PM_CONTEXT_LAMBDA_ELSE:
18078 case PM_CONTEXT_MODULE_ELSE:
18079 case PM_CONTEXT_SCLASS_ELSE:
18080 // These are also bad cases, but with a more specific error
18081 // message indicating the else.
18082 context = CONTEXT_THROUGH_ELSE;
18083 break;
18084 case PM_CONTEXT_BEGIN_ENSURE:
18085 case PM_CONTEXT_BLOCK_ENSURE:
18086 case PM_CONTEXT_CLASS_ENSURE:
18087 case PM_CONTEXT_DEF_ENSURE:
18088 case PM_CONTEXT_LAMBDA_ENSURE:
18089 case PM_CONTEXT_MODULE_ENSURE:
18090 case PM_CONTEXT_SCLASS_ENSURE:
18091 // These are also bad cases, but with a more specific error
18092 // message indicating the ensure.
18093 context = CONTEXT_THROUGH_ENSURE;
18094 break;
18095 case PM_CONTEXT_NONE:
18096 case PM_CONTEXT_MAXIMUM:
18097 // This case should never happen.
18098 assert(false && "unreachable");
18099 break;
18100 case PM_CONTEXT_BEGIN:
18101 case PM_CONTEXT_BLOCK_BRACES:
18102 case PM_CONTEXT_BLOCK_KEYWORDS:
18103 case PM_CONTEXT_BLOCK_PARAMETERS:
18104 case PM_CONTEXT_CASE_IN:
18105 case PM_CONTEXT_CASE_WHEN:
18106 case PM_CONTEXT_DEFAULT_PARAMS:
18107 case PM_CONTEXT_ELSE:
18108 case PM_CONTEXT_ELSIF:
18109 case PM_CONTEXT_EMBEXPR:
18110 case PM_CONTEXT_FOR_INDEX:
18111 case PM_CONTEXT_FOR:
18112 case PM_CONTEXT_IF:
18113 case PM_CONTEXT_LAMBDA_BRACES:
18114 case PM_CONTEXT_LAMBDA_DO_END:
18115 case PM_CONTEXT_LOOP_PREDICATE:
18116 case PM_CONTEXT_MULTI_TARGET:
18117 case PM_CONTEXT_PARENS:
18118 case PM_CONTEXT_POSTEXE:
18119 case PM_CONTEXT_PREDICATE:
18120 case PM_CONTEXT_TERNARY:
18121 case PM_CONTEXT_UNLESS:
18122 case PM_CONTEXT_UNTIL:
18123 case PM_CONTEXT_WHILE:
18124 // In these contexts we should continue walking up the list of
18125 // contexts.
18126 break;
18127 }
18128
18129 context_node = context_node->prev;
18130 }
18131
18132#undef CONTEXT_NONE
18133#undef CONTEXT_ENSURE
18134#undef CONTEXT_ELSE
18135}
18136
18140static void
18141ensure_valid_yield(pm_parser_t *parser, const pm_yield_node_t *node) {
18142 pm_context_node_t *context_node = parser->current_context;
18143
18144 while (context_node != NULL) {
18145 switch (context_node->context) {
18146 case PM_CONTEXT_DEF:
18147 case PM_CONTEXT_DEF_PARAMS:
18148 case PM_CONTEXT_DEFINED:
18149 case PM_CONTEXT_DEF_ENSURE:
18150 case PM_CONTEXT_DEF_RESCUE:
18151 case PM_CONTEXT_DEF_ELSE:
18152 // These are the good cases. We're allowed to have a block exit
18153 // in these contexts.
18154 return;
18155 case PM_CONTEXT_CLASS:
18156 case PM_CONTEXT_CLASS_ENSURE:
18157 case PM_CONTEXT_CLASS_RESCUE:
18158 case PM_CONTEXT_CLASS_ELSE:
18159 case PM_CONTEXT_MAIN:
18160 case PM_CONTEXT_MODULE:
18161 case PM_CONTEXT_MODULE_ENSURE:
18162 case PM_CONTEXT_MODULE_RESCUE:
18163 case PM_CONTEXT_MODULE_ELSE:
18164 case PM_CONTEXT_SCLASS:
18165 case PM_CONTEXT_SCLASS_RESCUE:
18166 case PM_CONTEXT_SCLASS_ENSURE:
18167 case PM_CONTEXT_SCLASS_ELSE:
18168 // These are the bad cases. We're not allowed to have a retry in
18169 // these contexts.
18170 pm_parser_err_node(parser, UP(node), PM_ERR_INVALID_YIELD);
18171 return;
18172 case PM_CONTEXT_NONE:
18173 case PM_CONTEXT_MAXIMUM:
18174 // This case should never happen.
18175 assert(false && "unreachable");
18176 break;
18177 case PM_CONTEXT_BEGIN:
18178 case PM_CONTEXT_BEGIN_ELSE:
18179 case PM_CONTEXT_BEGIN_ENSURE:
18180 case PM_CONTEXT_BEGIN_RESCUE:
18181 case PM_CONTEXT_BLOCK_BRACES:
18182 case PM_CONTEXT_BLOCK_KEYWORDS:
18183 case PM_CONTEXT_BLOCK_ELSE:
18184 case PM_CONTEXT_BLOCK_ENSURE:
18185 case PM_CONTEXT_BLOCK_PARAMETERS:
18186 case PM_CONTEXT_BLOCK_RESCUE:
18187 case PM_CONTEXT_CASE_IN:
18188 case PM_CONTEXT_CASE_WHEN:
18189 case PM_CONTEXT_DEFAULT_PARAMS:
18190 case PM_CONTEXT_ELSE:
18191 case PM_CONTEXT_ELSIF:
18192 case PM_CONTEXT_EMBEXPR:
18193 case PM_CONTEXT_FOR_INDEX:
18194 case PM_CONTEXT_FOR:
18195 case PM_CONTEXT_IF:
18196 case PM_CONTEXT_LAMBDA_BRACES:
18197 case PM_CONTEXT_LAMBDA_DO_END:
18198 case PM_CONTEXT_LAMBDA_ELSE:
18199 case PM_CONTEXT_LAMBDA_ENSURE:
18200 case PM_CONTEXT_LAMBDA_RESCUE:
18201 case PM_CONTEXT_LOOP_PREDICATE:
18202 case PM_CONTEXT_MULTI_TARGET:
18203 case PM_CONTEXT_PARENS:
18204 case PM_CONTEXT_POSTEXE:
18205 case PM_CONTEXT_PREDICATE:
18206 case PM_CONTEXT_PREEXE:
18207 case PM_CONTEXT_RESCUE_MODIFIER:
18208 case PM_CONTEXT_TERNARY:
18209 case PM_CONTEXT_UNLESS:
18210 case PM_CONTEXT_UNTIL:
18211 case PM_CONTEXT_WHILE:
18212 // In these contexts we should continue walking up the list of
18213 // contexts.
18214 break;
18215 }
18216
18217 context_node = context_node->prev;
18218 }
18219}
18220
18225static pm_node_t *
18226parse_case(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18227 size_t opening_newline_index = token_newline_index(parser);
18228 parser_lex(parser);
18229
18230 pm_token_t case_keyword = parser->previous;
18231 pm_node_t *predicate = NULL;
18232
18233 pm_node_list_t current_block_exits = { 0 };
18234 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18235
18236 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18237 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18238 predicate = NULL;
18239 } else if (match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_END)) {
18240 predicate = NULL;
18241 } else if (!token_begins_expression_p(parser->current.type)) {
18242 predicate = NULL;
18243 } else {
18244 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));
18245 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18246 }
18247
18248 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
18249 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18250 parser_lex(parser);
18251 pop_block_exits(parser, previous_block_exits);
18252 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18253 return UP(pm_case_node_create(parser, &case_keyword, predicate, &parser->previous));
18254 }
18255
18256 /* At this point we can create a case node, though we don't yet know if it
18257 * is a case-in or case-when node. */
18258 pm_node_t *node;
18259
18260 if (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18261 pm_case_node_t *case_node = pm_case_node_create(parser, &case_keyword, predicate, NULL);
18262 pm_static_literals_t literals = { 0 };
18263
18264 /* At this point we've seen a when keyword, so we know this is a
18265 * case-when node. We will continue to parse the when nodes until we hit
18266 * the end of the list. */
18267 while (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18268 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18269 parser_lex(parser);
18270
18271 pm_token_t when_keyword = parser->previous;
18272 pm_when_node_t *when_node = pm_when_node_create(parser, &when_keyword);
18273
18274 do {
18275 if (accept1(parser, PM_TOKEN_USTAR)) {
18276 pm_token_t operator = parser->previous;
18277 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));
18278
18279 pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression);
18280 pm_when_node_conditions_append(parser->arena, when_node, UP(splat_node));
18281
18282 if (PM_NODE_TYPE_P(expression, PM_ERROR_RECOVERY_NODE)) break;
18283 } else {
18284 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));
18285 pm_when_node_conditions_append(parser->arena, when_node, condition);
18286
18287 /* If we found a missing node, then this is a syntax error
18288 * and we should stop looping. */
18289 if (PM_NODE_TYPE_P(condition, PM_ERROR_RECOVERY_NODE)) break;
18290
18291 /* If this is a string node, then we need to mark it as
18292 * frozen because when clause strings are frozen. */
18293 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
18294 pm_node_flag_set(condition, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
18295 } else if (PM_NODE_TYPE_P(condition, PM_SOURCE_FILE_NODE) && parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
18296 pm_node_flag_set(condition, PM_NODE_FLAG_STATIC_LITERAL);
18297 }
18298
18299 pm_when_clause_static_literals_add(parser, &literals, condition);
18300 }
18301 } while (accept1(parser, PM_TOKEN_COMMA));
18302
18303 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18304 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18305 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18306 }
18307 } else {
18308 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER);
18309 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18310 }
18311
18312 if (!match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18313 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_CASE_WHEN, (uint16_t) (depth + 1));
18314 if (statements != NULL) {
18315 pm_when_node_statements_set(when_node, statements);
18316 }
18317 }
18318
18319 pm_case_node_condition_append(parser->arena, case_node, UP(when_node));
18320 }
18321
18322 /* If we didn't parse any conditions (in or when) then we need to
18323 * indicate that we have an error. */
18324 if (case_node->conditions.size == 0) {
18325 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18326 }
18327
18328 pm_static_literals_free(&literals);
18329 node = UP(case_node);
18330 } else {
18331 pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate);
18332
18333 /* If this is a case-match node (i.e., it is a pattern matching case
18334 * statement) then we must have a predicate. */
18335 if (predicate == NULL) {
18336 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MATCH_MISSING_PREDICATE);
18337 }
18338
18339 /* At this point we expect that we're parsing a case-in node. We will
18340 * continue to parse the in nodes until we hit the end of the list. */
18341 while (match1(parser, PM_TOKEN_KEYWORD_IN)) {
18342 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18343
18344 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
18345 parser->pattern_matching_newlines = true;
18346
18347 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
18348 parser->command_start = false;
18349 parser_lex(parser);
18350
18351 pm_token_t in_keyword = parser->previous;
18352
18353 pm_constant_id_set_t captures = { 0 };
18354 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));
18355
18356 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
18357
18358 /* Since we're in the top-level of the case-in node we need to
18359 * check for guard clauses in the form of `if` or `unless`
18360 * statements. */
18361 if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) {
18362 pm_token_t keyword = parser->previous;
18363 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));
18364 pattern = UP(pm_if_node_modifier_create(parser, pattern, &keyword, predicate));
18365 } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) {
18366 pm_token_t keyword = parser->previous;
18367 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));
18368 pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate));
18369 }
18370
18371 /* Now we need to check for the terminator of the in node's pattern.
18372 * It can be a newline or semicolon optionally followed by a `then`
18373 * keyword. */
18374 pm_token_t then_keyword = { 0 };
18375 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18376 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18377 then_keyword = parser->previous;
18378 }
18379 } else {
18380 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_IN_DELIMITER);
18381 then_keyword = parser->previous;
18382 }
18383
18384 /* Now we can actually parse the statements associated with the in
18385 * node. */
18386 pm_statements_node_t *statements;
18387 if (match3(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18388 statements = NULL;
18389 } else {
18390 statements = parse_statements(parser, PM_CONTEXT_CASE_IN, (uint16_t) (depth + 1));
18391 }
18392
18393 /* Now that we have the full pattern and statements, we can create
18394 * the node and attach it to the case node. */
18395 pm_node_t *condition = UP(pm_in_node_create(parser, pattern, statements, &in_keyword, NTOK2PTR(then_keyword)));
18396 pm_case_match_node_condition_append(parser->arena, case_node, condition);
18397 }
18398
18399 /* If we didn't parse any conditions (in or when) then we need to
18400 * indicate that we have an error. */
18401 if (case_node->conditions.size == 0) {
18402 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18403 }
18404
18405 node = UP(case_node);
18406 }
18407
18408 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18409 if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) {
18410 pm_token_t else_keyword = parser->previous;
18411 pm_else_node_t *else_node;
18412
18413 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
18414 else_node = pm_else_node_create(parser, &else_keyword, parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1)), &parser->current);
18415 } else {
18416 else_node = pm_else_node_create(parser, &else_keyword, NULL, &parser->current);
18417 }
18418
18419 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18420 pm_case_node_else_clause_set((pm_case_node_t *) node, else_node);
18421 } else {
18422 pm_case_match_node_else_clause_set((pm_case_match_node_t *) node, else_node);
18423 }
18424 }
18425
18426 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18427 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword);
18428
18429 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18430 pm_case_node_end_keyword_loc_set(parser, (pm_case_node_t *) node, &parser->previous);
18431 } else {
18432 pm_case_match_node_end_keyword_loc_set(parser, (pm_case_match_node_t *) node, &parser->previous);
18433 }
18434
18435 pop_block_exits(parser, previous_block_exits);
18436 return node;
18437}
18438
18443static pm_node_t *
18444parse_class(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18445 size_t opening_newline_index = token_newline_index(parser);
18446 parser_lex(parser);
18447
18448 pm_token_t class_keyword = parser->previous;
18449 pm_do_loop_stack_push(parser, false);
18450
18451 pm_node_list_t current_block_exits = { 0 };
18452 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18453
18454 if (accept1(parser, PM_TOKEN_LESS_LESS)) {
18455 pm_token_t operator = parser->previous;
18456 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));
18457
18458 pm_parser_scope_push(parser, true);
18459 if (!match2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18460 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_str(parser->current.type));
18461 }
18462
18463 pm_node_t *statements = NULL;
18464 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18465 pm_accepts_block_stack_push(parser, true);
18466 statements = UP(parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1)));
18467 pm_accepts_block_stack_pop(parser);
18468 }
18469
18470 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18471 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18472 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)));
18473 } else {
18474 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18475 }
18476
18477 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18478
18479 pm_constant_id_list_t locals;
18480 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18481
18482 pm_parser_scope_pop(parser);
18483 pm_do_loop_stack_pop(parser);
18484
18485 flush_block_exits(parser, previous_block_exits);
18486 return UP(pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous));
18487 }
18488
18489 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));
18490 pm_token_t name = parser->previous;
18491 if (name.type != PM_TOKEN_CONSTANT) {
18492 pm_parser_err_token(parser, &name, PM_ERR_CLASS_NAME);
18493 }
18494
18495 pm_token_t inheritance_operator = { 0 };
18496 pm_node_t *superclass;
18497
18498 if (match1(parser, PM_TOKEN_LESS)) {
18499 inheritance_operator = parser->current;
18500 lex_state_set(parser, PM_LEX_STATE_BEG);
18501
18502 parser->command_start = true;
18503 parser_lex(parser);
18504
18505 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));
18506 } else {
18507 superclass = NULL;
18508 }
18509
18510 pm_parser_scope_push(parser, true);
18511
18512 if (inheritance_operator.start != NULL) {
18513 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CLASS_UNEXPECTED_END);
18514 } else {
18515 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18516 }
18517 pm_node_t *statements = NULL;
18518
18519 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18520 pm_accepts_block_stack_push(parser, true);
18521 statements = UP(parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1)));
18522 pm_accepts_block_stack_pop(parser);
18523 }
18524
18525 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18526 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18527 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)));
18528 } else {
18529 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18530 }
18531
18532 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18533
18534 if (context_def_p(parser)) {
18535 pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD);
18536 }
18537
18538 pm_constant_id_list_t locals;
18539 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18540
18541 pm_parser_scope_pop(parser);
18542 pm_do_loop_stack_pop(parser);
18543
18544 if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) {
18545 pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME);
18546 if (!PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18547 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18548 }
18549 }
18550
18551 pop_block_exits(parser, previous_block_exits);
18552 return UP(pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, NTOK2PTR(inheritance_operator), superclass, statements, &parser->previous));
18553}
18554
18558static pm_node_t *
18559parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
18560 pm_node_list_t current_block_exits = { 0 };
18561 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18562
18563 pm_token_t def_keyword = parser->current;
18564 size_t opening_newline_index = token_newline_index(parser);
18565
18566 pm_node_t *receiver = NULL;
18567 pm_token_t operator = { 0 };
18568 pm_token_t name;
18569
18570 /* This context is necessary for lexing `...` in a bare params correctly. It
18571 * must be pushed before lexing the first param, so it is here. */
18572 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18573 parser_lex(parser);
18574
18575 /* This will be false if the method name is not a valid identifier but could
18576 * be followed by an operator. */
18577 bool valid_name = true;
18578
18579 switch (parser->current.type) {
18580 case PM_CASE_OPERATOR:
18581 pm_parser_scope_push(parser, true);
18582 lex_state_set(parser, PM_LEX_STATE_ENDFN);
18583 parser_lex(parser);
18584
18585 name = parser->previous;
18586 break;
18587 case PM_TOKEN_IDENTIFIER: {
18588 parser_lex(parser);
18589
18590 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18591 receiver = parse_variable_call(parser);
18592
18593 pm_parser_scope_push(parser, true);
18594 lex_state_set(parser, PM_LEX_STATE_FNAME);
18595 parser_lex(parser);
18596
18597 operator = parser->previous;
18598 name = parse_method_definition_name(parser);
18599 } else {
18600 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
18601 pm_parser_scope_push(parser, true);
18602
18603 name = parser->previous;
18604 }
18605
18606 break;
18607 }
18608 case PM_TOKEN_INSTANCE_VARIABLE:
18609 case PM_TOKEN_CLASS_VARIABLE:
18610 case PM_TOKEN_GLOBAL_VARIABLE:
18611 valid_name = false;
18613 case PM_TOKEN_CONSTANT:
18614 case PM_TOKEN_KEYWORD_NIL:
18615 case PM_TOKEN_KEYWORD_SELF:
18616 case PM_TOKEN_KEYWORD_TRUE:
18617 case PM_TOKEN_KEYWORD_FALSE:
18618 case PM_TOKEN_KEYWORD___FILE__:
18619 case PM_TOKEN_KEYWORD___LINE__:
18620 case PM_TOKEN_KEYWORD___ENCODING__: {
18621 pm_parser_scope_push(parser, true);
18622 parser_lex(parser);
18623
18624 pm_token_t identifier = parser->previous;
18625
18626 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18627 lex_state_set(parser, PM_LEX_STATE_FNAME);
18628 parser_lex(parser);
18629 operator = parser->previous;
18630
18631 switch (identifier.type) {
18632 case PM_TOKEN_CONSTANT:
18633 receiver = UP(pm_constant_read_node_create(parser, &identifier));
18634 break;
18635 case PM_TOKEN_INSTANCE_VARIABLE:
18636 receiver = UP(pm_instance_variable_read_node_create(parser, &identifier));
18637 break;
18638 case PM_TOKEN_CLASS_VARIABLE:
18639 receiver = UP(pm_class_variable_read_node_create(parser, &identifier));
18640 break;
18641 case PM_TOKEN_GLOBAL_VARIABLE:
18642 receiver = UP(pm_global_variable_read_node_create(parser, &identifier));
18643 break;
18644 case PM_TOKEN_KEYWORD_NIL:
18645 receiver = UP(pm_nil_node_create(parser, &identifier));
18646 break;
18647 case PM_TOKEN_KEYWORD_SELF:
18648 receiver = UP(pm_self_node_create(parser, &identifier));
18649 break;
18650 case PM_TOKEN_KEYWORD_TRUE:
18651 receiver = UP(pm_true_node_create(parser, &identifier));
18652 break;
18653 case PM_TOKEN_KEYWORD_FALSE:
18654 receiver = UP(pm_false_node_create(parser, &identifier));
18655 break;
18656 case PM_TOKEN_KEYWORD___FILE__:
18657 receiver = UP(pm_source_file_node_create(parser, &identifier));
18658 break;
18659 case PM_TOKEN_KEYWORD___LINE__:
18660 receiver = UP(pm_source_line_node_create(parser, &identifier));
18661 break;
18662 case PM_TOKEN_KEYWORD___ENCODING__:
18663 receiver = UP(pm_source_encoding_node_create(parser, &identifier));
18664 break;
18665 default:
18666 break;
18667 }
18668
18669 name = parse_method_definition_name(parser);
18670 } else {
18671 if (!valid_name) {
18672 PM_PARSER_ERR_TOKEN_FORMAT(parser, &identifier, PM_ERR_DEF_NAME, pm_token_str(identifier.type));
18673 }
18674
18675 name = identifier;
18676 }
18677 break;
18678 }
18679 case PM_TOKEN_PARENTHESIS_LEFT: {
18680 /* The current context is `PM_CONTEXT_DEF_PARAMS`, however the inner
18681 * expression of this parenthesis should not be processed under this
18682 * context. Thus, the context is popped here. */
18683 context_pop(parser);
18684 parser_lex(parser);
18685
18686 pm_token_t lparen = parser->previous;
18687 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));
18688
18689 accept1(parser, PM_TOKEN_NEWLINE);
18690 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
18691 pm_token_t rparen = parser->previous;
18692
18693 lex_state_set(parser, PM_LEX_STATE_FNAME);
18694 expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM);
18695
18696 operator = parser->previous;
18697 receiver = UP(pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0));
18698
18699 /* To push `PM_CONTEXT_DEF_PARAMS` again is for the same reason as
18700 * described the above. */
18701 pm_parser_scope_push(parser, true);
18702 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18703 name = parse_method_definition_name(parser);
18704 break;
18705 }
18706 default:
18707 pm_parser_scope_push(parser, true);
18708 name = parse_method_definition_name(parser);
18709 break;
18710 }
18711
18712 pm_token_t lparen = { 0 };
18713 pm_token_t rparen = { 0 };
18714 pm_parameters_node_t *params;
18715
18716 bool accept_endless_def = true;
18717 switch (parser->current.type) {
18718 case PM_TOKEN_PARENTHESIS_LEFT: {
18719 parser_lex(parser);
18720 lparen = parser->previous;
18721
18722 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18723 params = NULL;
18724 } else {
18725 /* https://bugs.ruby-lang.org/issues/19107 */
18726 bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1;
18727 params = parse_parameters(
18728 parser,
18729 PM_BINDING_POWER_DEFINED,
18730 true,
18731 allow_trailing_comma,
18732 true,
18733 true,
18734 false,
18735 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18736 (uint16_t) (depth + 1)
18737 );
18738 }
18739
18740 lex_state_set(parser, PM_LEX_STATE_BEG);
18741 parser->command_start = true;
18742
18743 context_pop(parser);
18744 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18745 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_str(parser->current.type));
18746 parser->previous.start = parser->previous.end;
18747 parser->previous.type = 0;
18748 }
18749
18750 rparen = parser->previous;
18751 break;
18752 }
18753 case PM_CASE_PARAMETER: {
18754 /* If we're about to lex a label, we need to add the label state to
18755 * make sure the next newline is ignored. */
18756 if (parser->current.type == PM_TOKEN_LABEL) {
18757 lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL);
18758 }
18759
18760 params = parse_parameters(
18761 parser,
18762 PM_BINDING_POWER_DEFINED,
18763 false,
18764 false,
18765 true,
18766 true,
18767 false,
18768 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18769 (uint16_t) (depth + 1)
18770 );
18771
18772 /* Reject `def * = 1` and similar. We have to specifically check for
18773 * them because they create ambiguity with optional arguments. */
18774 accept_endless_def = false;
18775
18776 context_pop(parser);
18777 break;
18778 }
18779 default: {
18780 params = NULL;
18781 context_pop(parser);
18782 break;
18783 }
18784 }
18785
18786 pm_node_t *statements = NULL;
18787 pm_token_t equal = { 0 };
18788 pm_token_t end_keyword = { 0 };
18789
18790 if (accept1(parser, PM_TOKEN_EQUAL)) {
18791 if (token_is_setter_name(&name)) {
18792 pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER);
18793 }
18794 if (!accept_endless_def) {
18795 pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
18796 }
18797 if (
18798 parser->current_context->context == PM_CONTEXT_DEFAULT_PARAMS &&
18799 parser->current_context->prev->context == PM_CONTEXT_BLOCK_PARAMETERS
18800 ) {
18801 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");
18802 }
18803 equal = parser->previous;
18804
18805 context_push(parser, PM_CONTEXT_DEF);
18806 pm_do_loop_stack_push(parser, false);
18807 statements = UP(pm_statements_node_create(parser));
18808
18809 uint8_t allow_flags;
18810 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
18811 allow_flags = flags & PM_PARSE_ACCEPTS_COMMAND_CALL;
18812 } else {
18813 /* Allow `def foo = puts "Hello"` but not
18814 * `private def foo = puts "Hello"` */
18815 allow_flags = (binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
18816 }
18817
18818 /* Inside a def body, we push true onto the accepts_block_stack so that
18819 * `do` is lexed as PM_TOKEN_KEYWORD_DO (which can only start a block
18820 * for primary-level constructs, not commands). During command argument
18821 * parsing, the stack is pushed to false, causing `do` to be lexed as
18822 * PM_TOKEN_KEYWORD_DO_BLOCK, which is not consumed inside the endless
18823 * def body and instead left for the outer context. A method definition
18824 * opens a fresh context all the way through its rescue modifier, so
18825 * this frame spans the rescue modifier value as well: the `do` in
18826 * `baz def f = a rescue z do end` lexes as a plain keyword that
18827 * attaches to `z` rather than to `baz`. */
18828 pm_accepts_block_stack_push(parser, true);
18829 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));
18830
18831 /* If an unconsumed PM_TOKEN_KEYWORD_DO follows the body, it is an error
18832 * (e.g., `def f = 1 do end`). PM_TOKEN_KEYWORD_DO_BLOCK is
18833 * intentionally not caught here — it should bubble up to the outer
18834 * context (e.g., `private def f = puts "Hello" do end` where the block
18835 * attaches to `private`). */
18836 if (accept1(parser, PM_TOKEN_KEYWORD_DO)) {
18837 pm_block_node_t *block = parse_block(parser, (uint16_t) (depth + 1));
18838 pm_parser_err_node(parser, UP(block), PM_ERR_DEF_ENDLESS_DO_BLOCK);
18839 }
18840
18841 /* Any number of rescue modifiers chain onto the body within the method
18842 * definition itself, associating to the left: `def f = a rescue b
18843 * rescue c` defines a method whose body is `(a rescue b) rescue c`,
18844 * rather than a rescue modifier guarding the definition. */
18845 while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
18846 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
18847
18848 pm_token_t rescue_keyword = parser->previous;
18849
18850 /* In the Ruby grammar, the rescue value of an endless method
18851 * command excludes and/or and in/=>. */
18852 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));
18853 context_pop(parser);
18854
18855 statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value));
18856 }
18857
18858 pm_accepts_block_stack_pop(parser);
18859
18860 /* A nested endless def whose body is a command call (e.g.,
18861 * `def f = def g = foo bar`) is a command assignment and cannot appear
18862 * as a def body. */
18863 if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(parser, statement)) {
18864 PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
18865 }
18866
18867 pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false);
18868 pm_do_loop_stack_pop(parser);
18869 context_pop(parser);
18870 } else {
18871 if (lparen.start == NULL) {
18872 lex_state_set(parser, PM_LEX_STATE_BEG);
18873 parser->command_start = true;
18874 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_DEF_PARAMS_TERM);
18875 } else {
18876 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18877 }
18878
18879 pm_accepts_block_stack_push(parser, true);
18880 pm_do_loop_stack_push(parser, false);
18881
18882 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18883 pm_accepts_block_stack_push(parser, true);
18884 statements = UP(parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1)));
18885 pm_accepts_block_stack_pop(parser);
18886 }
18887
18888 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18889 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18890 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)));
18891 } else {
18892 parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false);
18893 }
18894
18895 pm_accepts_block_stack_pop(parser);
18896 pm_do_loop_stack_pop(parser);
18897
18898 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword);
18899 end_keyword = parser->previous;
18900 }
18901
18902 pm_constant_id_list_t locals;
18903 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18904 pm_parser_scope_pop(parser);
18905
18906 /* If the final character is `@` as is the case when defining methods to
18907 * override the unary operators, we should ignore the @ in the same way we
18908 * do for symbols. */
18909 pm_constant_id_t name_id = pm_parser_constant_id_raw(parser, name.start, parse_operator_symbol_name(&name));
18910
18911 flush_block_exits(parser, previous_block_exits);
18912
18913 return UP(pm_def_node_create(
18914 parser,
18915 name_id,
18916 &name,
18917 receiver,
18918 params,
18919 statements,
18920 &locals,
18921 &def_keyword,
18922 NTOK2PTR(operator),
18923 NTOK2PTR(lparen),
18924 NTOK2PTR(rparen),
18925 NTOK2PTR(equal),
18926 NTOK2PTR(end_keyword)
18927 ));
18928}
18929
18933static pm_node_t *
18934parse_module(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18935 pm_node_list_t current_block_exits = { 0 };
18936 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18937
18938 size_t opening_newline_index = token_newline_index(parser);
18939 parser_lex(parser);
18940 pm_token_t module_keyword = parser->previous;
18941
18942 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));
18943 pm_token_t name;
18944
18945 /* If we can recover from a syntax error that occurred while parsing the
18946 * name of the module, then we'll handle that here. */
18947 if (PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18948 pop_block_exits(parser, previous_block_exits);
18949
18950 pm_token_t missing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
18951 return UP(pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing));
18952 }
18953
18954 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
18955 pm_token_t double_colon = parser->previous;
18956
18957 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
18958 constant_path = UP(pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous));
18959 }
18960
18961 /* Here we retrieve the name of the module. If it wasn't a constant, then
18962 * it's possible that `module foo` was passed, which is a syntax error. We
18963 * handle that here as well. */
18964 name = parser->previous;
18965 if (name.type != PM_TOKEN_CONSTANT) {
18966 pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME);
18967 }
18968
18969 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)) {
18970 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18971 }
18972
18973 pm_parser_scope_push(parser, true);
18974 accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE);
18975 pm_node_t *statements = NULL;
18976
18977 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18978 pm_accepts_block_stack_push(parser, true);
18979 statements = UP(parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1)));
18980 pm_accepts_block_stack_pop(parser);
18981 }
18982
18983 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18984 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18985 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)));
18986 } else {
18987 parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false);
18988 }
18989
18990 pm_constant_id_list_t locals;
18991 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18992
18993 pm_parser_scope_pop(parser);
18994 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword);
18995
18996 if (context_def_p(parser)) {
18997 pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
18998 }
18999
19000 pop_block_exits(parser, previous_block_exits);
19001
19002 return UP(pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous));
19003}
19004
19009static pm_node_t *
19010parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
19011 pm_token_t opening = parser->current;
19012 pm_node_flags_t paren_flags = 0;
19013
19014 pm_node_list_t current_block_exits = { 0 };
19015 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19016
19017 parser_lex(parser);
19018 while (true) {
19019 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19020 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19021 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19022 break;
19023 }
19024 }
19025
19026 /* If this is the end of the file or we match a right parenthesis, then we
19027 * have an empty parentheses node, and we can immediately return. */
19028 if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
19029 /* A command argument group sets EXPR_ENDARG before its ')' is
19030 * consumed, even when the group is empty, so that a following '{' is
19031 * scanned as a block brace. */
19032 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT) && opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19033 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19034 }
19035
19036 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19037 pop_block_exits(parser, previous_block_exits);
19038 return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, paren_flags));
19039 }
19040
19041 /* Otherwise, we're going to parse the first statement in the list of
19042 * statements within the parentheses. */
19043 context_push(parser, PM_CONTEXT_PARENS);
19044 pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19045 context_pop(parser);
19046
19047 /* Determine if this statement is followed by a terminator. In the case of a
19048 * single statement, this is fine. But in the case of multiple statements
19049 * it's required. */
19050 bool terminator_found = false;
19051
19052 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19053 terminator_found = true;
19054 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19055 } else if (accept1(parser, PM_TOKEN_NEWLINE)) {
19056 terminator_found = true;
19057 }
19058
19059 if (terminator_found) {
19060 while (true) {
19061 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19062 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19063 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19064 break;
19065 }
19066 }
19067 }
19068
19069 /* If we hit a right parenthesis, then we're done parsing the parentheses
19070 * node, and we can check which kind of node we should return. */
19071 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19072 if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19073 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19074 }
19075
19076 parser_lex(parser);
19077 pop_block_exits(parser, previous_block_exits);
19078
19079 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19080 /* If we have a single statement and are ending on a right
19081 * parenthesis, then we need to check if this is possibly a multiple
19082 * target node. */
19083 pm_multi_target_node_t *multi_target;
19084
19085 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) && ((pm_multi_target_node_t *) statement)->lparen_loc.length == 0) {
19086 multi_target = (pm_multi_target_node_t *) statement;
19087 } else {
19088 multi_target = pm_multi_target_node_create(parser);
19089 pm_multi_target_node_targets_append(parser, multi_target, statement);
19090 }
19091
19092 multi_target->lparen_loc = TOK2LOC(parser, &opening);
19093 multi_target->rparen_loc = TOK2LOC(parser, &parser->previous);
19094 PM_NODE_START_SET_TOKEN(parser, multi_target, &opening);
19095 PM_NODE_LENGTH_SET_TOKEN(parser, multi_target, &parser->previous);
19096
19097 pm_node_t *result;
19098 if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) {
19099 result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19100 accept1(parser, PM_TOKEN_NEWLINE);
19101 } else {
19102 result = UP(multi_target);
19103 }
19104
19105 if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
19106 /* All set, this is explicitly allowed by the parent context. */
19107 } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match2(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_COMMA)) {
19108 /* All set, we're inside a for loop and we're parsing multiple
19109 * targets. A comma continues the index target list, as in
19110 * `for (a, b), c in ...`. */
19111 } else if (flags & PM_PARSE_ACCEPTS_STATEMENT) {
19112 /* The rescue-modifier value parser promotes this target on a
19113 * following `=` or comma. Reject any other binary operator that
19114 * would otherwise consume the target list (e.g. `(a, b) + c`). */
19115 if (pm_binding_powers[parser->current.type].binary && !match1(parser, PM_TOKEN_EQUAL)) {
19116 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19117 }
19118 } else if (binding_power != PM_BINDING_POWER_STATEMENT) {
19119 /* Multi targets are not allowed when it's not a statement
19120 * level. */
19121 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19122 } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
19123 /* Multi targets must be followed by an equal sign in order to
19124 * be valid (or a right parenthesis if they are nested). */
19125 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19126 }
19127
19128 return result;
19129 }
19130
19131 /* If we have a single statement and are ending on a right parenthesis
19132 * and we didn't return a multiple assignment node, then we can return a
19133 * regular parentheses node now. */
19134 pm_statements_node_t *statements = pm_statements_node_create(parser);
19135 pm_statements_node_body_append(parser, statements, statement, true);
19136
19137 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19138 }
19139
19140 /* If we have more than one statement in the set of parentheses, then we are
19141 * going to parse all of them as a list of statements. We'll do that here.
19142 */
19143 context_push(parser, PM_CONTEXT_PARENS);
19144 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19145
19146 pm_statements_node_t *statements = pm_statements_node_create(parser);
19147 pm_statements_node_body_append(parser, statements, statement, true);
19148
19149 /* If we didn't find a terminator and we didn't find a right parenthesis,
19150 * then this is a syntax error. */
19151 if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) {
19152 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19153 }
19154
19155 /* Parse each statement within the parentheses. */
19156 while (true) {
19157 pm_node_t *node = parse_expression(parser, PM_BINDING_POWER_STATEMENT, PM_PARSE_ACCEPTS_COMMAND_CALL | PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_CANNOT_PARSE_EXPRESSION, (uint16_t) (depth + 1));
19158 pm_statements_node_body_append(parser, statements, node, true);
19159
19160 /* If we're recovering from a syntax error, then we need to stop parsing
19161 * the statements now. */
19162 if (parser->recovering) {
19163 /* If this is the level of context where the recovery has happened,
19164 * then we can mark the parser as done recovering. */
19165 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false;
19166 break;
19167 }
19168
19169 /* If we couldn't parse an expression at all, then we need to bail out
19170 * of the loop. */
19171 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) break;
19172
19173 /* If we successfully parsed a statement, then we are going to need a
19174 * terminator to delimit them. */
19175 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
19176 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
19177 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break;
19178 } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19179 break;
19180 } else if (!match1(parser, PM_TOKEN_EOF)) {
19181 /* If we're at the end of the file, then we're going to add an error
19182 * after this for the ) anyway. */
19183 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19184 }
19185 }
19186
19187 context_pop(parser);
19188 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19189
19190 /* When we're parsing multi targets, we allow them to be followed by a right
19191 * parenthesis if they are at the statement level. This is only possible if
19192 * they are the final statement in a parentheses. We need to explicitly
19193 * reject that here. */
19194 {
19195 pm_node_t *statement = statements->body.nodes[statements->body.size - 1];
19196
19197 if (PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19198 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
19199 pm_multi_target_node_targets_append(parser, multi_target, statement);
19200
19201 statement = UP(multi_target);
19202 statements->body.nodes[statements->body.size - 1] = statement;
19203 }
19204
19205 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) {
19206 const uint8_t *offset = parser->start + PM_NODE_END(statement);
19207 pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset };
19208 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(statement), 0));
19209
19210 statement = UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value));
19211 statements->body.nodes[statements->body.size - 1] = statement;
19212
19213 pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED);
19214 }
19215 }
19216
19217 pop_block_exits(parser, previous_block_exits);
19218 pm_void_statements_check(parser, statements, true);
19219 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19220}
19221
19227static pm_node_t *
19228parse_splat(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19229 pm_token_t operator = parser->previous;
19230 pm_node_t *name = NULL;
19231
19232 if (token_begins_expression_p(parser->current.type)) {
19233 name = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
19234 }
19235
19236 return UP(pm_splat_node_create(parser, &operator, name));
19237}
19238
19240parse_array_keyword_hash(pm_parser_t *parser, pm_node_t *key, uint8_t flags, uint16_t depth) {
19241 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
19242
19243 pm_static_literals_t hash_keys = { 0 };
19244 pm_hash_key_static_literals_add(parser, &hash_keys, key);
19245
19246 pm_token_t operator = { 0 };
19247 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
19248 operator = parser->previous;
19249 }
19250
19251 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));
19252 pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, NTOK2PTR(operator), value));
19253 pm_keyword_hash_node_elements_append(parser->arena, hash, assoc);
19254
19255 if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19256 parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1));
19257 }
19258
19259 pm_static_literals_free(&hash_keys);
19260 return hash;
19261}
19262
19263static pm_array_node_t *
19264parse_array(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19265 pm_array_node_t *array = pm_array_node_create(parser, &parser->current);
19266 parser_lex(parser);
19267
19268 bool parsed_bare_hash = false;
19269 while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
19270 bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
19271
19272 // Handle the case where we don't have a comma and we have a
19273 // newline followed by a right bracket.
19274 if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19275 break;
19276 }
19277
19278 // Ensure that we have a comma between elements in the array.
19279 if (array->elements.size > 0) {
19280 if (accept1(parser, PM_TOKEN_COMMA)) {
19281 // If there was a comma but we also accepts a newline,
19282 // then this is a syntax error.
19283 if (accepted_newline) {
19284 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
19285 }
19286 } else {
19287 // If there was no comma, then we need to add a syntax
19288 // error.
19289 PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_END(parser, &parser->previous), 0, PM_ERR_ARRAY_SEPARATOR, pm_token_str(parser->current.type));
19290 parser->previous.start = parser->previous.end;
19291 parser->previous.type = 0;
19292 }
19293 }
19294
19295 // If we have a right bracket immediately following a comma,
19296 // this is allowed since it's a trailing comma. In this case we
19297 // can break out of the loop.
19298 if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break;
19299
19300 pm_node_t *element;
19301
19302 if (accept1(parser, PM_TOKEN_USTAR)) {
19303 pm_token_t operator = parser->previous;
19304 pm_node_t *expression = NULL;
19305
19306 if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) {
19307 pm_parser_scope_forwarding_positionals_check(parser, &operator);
19308 } else {
19309 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));
19310 }
19311
19312 element = UP(pm_splat_node_create(parser, &operator, expression));
19313 } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) {
19314 if (parsed_bare_hash) {
19315 pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH);
19316 }
19317
19318 element = UP(pm_keyword_hash_node_create(parser));
19319 pm_static_literals_t hash_keys = { 0 };
19320
19321 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)) {
19322 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
19323 }
19324
19325 pm_static_literals_free(&hash_keys);
19326 parsed_bare_hash = true;
19327 } else {
19328 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));
19329
19330 if (pm_symbol_node_label_p(parser, element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
19331 if (parsed_bare_hash) {
19332 pm_parser_err_previous(parser, PM_ERR_EXPRESSION_BARE_HASH);
19333 }
19334
19335 element = UP(parse_array_keyword_hash(parser, element, flags, depth));
19336 parsed_bare_hash = true;
19337 }
19338 }
19339
19340 pm_array_node_elements_append(parser->arena, array, element);
19341 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
19342 }
19343
19344 accept1(parser, PM_TOKEN_NEWLINE);
19345
19346 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19347 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
19348 parser->previous.start = parser->previous.end;
19349 parser->previous.type = 0;
19350 }
19351
19352 pm_array_node_close_set(parser, array, &parser->previous);
19353 return array;
19354}
19355
19356static pm_hash_node_t *
19357parse_hash(pm_parser_t *parser, uint16_t depth) {
19358 pm_token_t opening = parser->current;
19359 pm_hash_node_t *node = pm_hash_node_create(parser, &opening);
19360 parser_lex(parser);
19361
19362 if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
19363 pm_static_literals_t hash_keys = { 0 };
19364 parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1));
19365 pm_static_literals_free(&hash_keys);
19366
19367 accept1(parser, PM_TOKEN_NEWLINE);
19368 }
19369
19370 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening);
19371 pm_hash_node_closing_loc_set(parser, node, &parser->previous);
19372 return node;
19373}
19374
19375static pm_node_t *
19376parse_heredoc(pm_parser_t *parser, uint16_t depth) {
19377 assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC);
19378 pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base;
19379
19380 size_t common_whitespace = (size_t) -1;
19381 parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace;
19382
19383 parser_lex(parser);
19384 pm_token_t opening = parser->previous;
19385
19386 pm_node_t *node;
19387 pm_node_t *part;
19388
19389 if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19390 // If we get here, then we have an empty heredoc. We'll create
19391 // an empty content token and return an empty string node.
19392 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19393 pm_token_t content = parse_strings_empty_content(parser->previous.start);
19394
19395 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19396 node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19397 } else {
19398 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19399 }
19400
19401 PM_NODE_LENGTH_SET_TOKEN(parser, node, &opening);
19402 } else if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) == NULL) {
19403 // If we get here, then we tried to find something in the
19404 // heredoc but couldn't actually parse anything, so we'll just
19405 // return a missing node.
19406 //
19407 // parse_string_part handles its own errors, so there is no need
19408 // for us to add one here.
19409 node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
19410 } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19411 // If we get here, then the part that we parsed was plain string
19412 // content and we're at the end of the heredoc, so we can return
19413 // just a string node with the heredoc opening and closing as
19414 // its opening and closing.
19415 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
19416 pm_string_node_t *cast = (pm_string_node_t *) part;
19417
19418 cast->opening_loc = TOK2LOC(parser, &opening);
19419 cast->closing_loc = TOK2LOC(parser, &parser->current);
19420 cast->base.location = cast->opening_loc;
19421
19422 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19423 assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
19424 cast->base.type = PM_X_STRING_NODE;
19425 }
19426
19427 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19428 parse_heredoc_dedent_string(parser->arena, &cast->unescaped, common_whitespace);
19429 }
19430
19431 node = UP(cast);
19432 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19433 } else {
19434 // If we get here, then we have multiple parts in the heredoc,
19435 // so we'll need to create an interpolated string node to hold
19436 // them all.
19437 pm_node_list_t parts = { 0 };
19438 pm_node_list_append(parser->arena, &parts, part);
19439
19440 while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19441 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19442 pm_node_list_append(parser->arena, &parts, part);
19443 }
19444 }
19445
19446 // Now that we have all of the parts, create the correct type of
19447 // interpolated node.
19448 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19449 pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19450 cast->parts = parts;
19451
19452 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19453 pm_interpolated_xstring_node_closing_set(parser, cast, &parser->previous);
19454
19455 cast->base.location = cast->opening_loc;
19456 node = UP(cast);
19457 } else {
19458 pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
19459
19460 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19461 pm_interpolated_string_node_closing_set(parser, cast, &parser->previous);
19462
19463 cast->base.location = cast->opening_loc;
19464 node = UP(cast);
19465 }
19466
19467 // If this is a heredoc that is indented with a ~, then we need
19468 // to dedent each line by the common leading whitespace.
19469 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19470 pm_node_list_t *nodes;
19471 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19472 nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
19473 } else {
19474 nodes = &((pm_interpolated_string_node_t *) node)->parts;
19475 }
19476
19477 parse_heredoc_dedent(parser, nodes, common_whitespace);
19478 }
19479 }
19480
19481 /* If a missing terminator left this heredoc's lex mode on the stack, it
19482 * still points at our stack-local common_whitespace. Clear the pointer so
19483 * that subsequent lexing cannot read from this function's dead stack frame.
19484 */
19485 pm_lex_mode_t *whitespace_mode = parser->lex_modes.current;
19486 do {
19487 if (whitespace_mode->mode == PM_LEX_HEREDOC && whitespace_mode->as.heredoc.common_whitespace == &common_whitespace) {
19488 whitespace_mode->as.heredoc.common_whitespace = NULL;
19489 }
19490 whitespace_mode = whitespace_mode->prev;
19491 } while (whitespace_mode != NULL);
19492
19493 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
19494 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
19495 }
19496
19497 return node;
19498}
19499
19500static pm_begin_node_t *
19501parse_begin(pm_parser_t *parser, uint16_t depth) {
19502 pm_token_t begin_keyword = parser->current;
19503 size_t opening_newline_index = token_newline_index(parser);
19504 parser_lex(parser);
19505
19506 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19507
19508 pm_node_list_t current_block_exits = { 0 };
19509 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19510 pm_statements_node_t *begin_statements = NULL;
19511
19512 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
19513 pm_accepts_block_stack_push(parser, true);
19514 begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN, (uint16_t) (depth + 1));
19515 pm_accepts_block_stack_pop(parser);
19516 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19517 }
19518
19519 pm_begin_node_t *node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
19520 parse_rescues(parser, opening_newline_index, &begin_keyword, node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1));
19521 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword);
19522
19523 PM_NODE_LENGTH_SET_TOKEN(parser, node, &parser->previous);
19524 pm_begin_node_end_keyword_set(parser, node, &parser->previous);
19525 pop_block_exits(parser, previous_block_exits);
19526 return node;
19527}
19528
19529static pm_defined_node_t *
19530parse_defined(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19531 pm_token_t keyword = parser->current;
19532 parser_lex(parser);
19533
19534 pm_token_t lparen = { 0 };
19535 pm_token_t rparen = { 0 };
19536 pm_node_t *expression;
19537
19538 context_push(parser, PM_CONTEXT_DEFINED);
19539 bool newline = accept1(parser, PM_TOKEN_NEWLINE);
19540
19541 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
19542 lparen = parser->previous;
19543
19544 if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19545 expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
19546 lparen = (pm_token_t) { 0 };
19547 } else {
19548 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));
19549
19550 if (!parser->recovering) {
19551 accept1(parser, PM_TOKEN_NEWLINE);
19552 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19553 rparen = parser->previous;
19554 }
19555 }
19556 } else {
19557 expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19558 }
19559
19560 context_pop(parser);
19561 return pm_defined_node_create(parser, NTOK2PTR(lparen), expression, NTOK2PTR(rparen), &keyword);
19562}
19563
19564static pm_for_node_t *
19565parse_for(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19566 pm_token_t for_keyword = parser->current;
19567 size_t opening_newline_index = token_newline_index(parser);
19568 parser_lex(parser);
19569
19570 pm_node_t *index;
19571 context_push(parser, PM_CONTEXT_FOR_INDEX);
19572
19573 // First, parse out the first index expression.
19574 if (accept1(parser, PM_TOKEN_USTAR)) {
19575 index = parse_splat(parser, flags, depth);
19576 } else if (token_begins_expression_p(parser->current.type)) {
19577 index = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
19578 } else {
19579 pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX);
19580 index = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &for_keyword), PM_TOKEN_LENGTH(&for_keyword)));
19581 }
19582
19583 // Now, if there are multiple index expressions, parse them out.
19584 if (match1(parser, PM_TOKEN_COMMA)) {
19585 index = parse_targets(parser, index, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19586 } else {
19587 index = parse_target(parser, index, false, false);
19588 }
19589
19590 context_pop(parser);
19591 pm_do_loop_stack_push(parser, true);
19592
19593 expect1(parser, PM_TOKEN_KEYWORD_IN, PM_ERR_FOR_IN);
19594 pm_token_t in_keyword = parser->previous;
19595
19596 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));
19597 pm_do_loop_stack_pop(parser);
19598
19599 pm_token_t do_keyword = { 0 };
19600 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
19601 do_keyword = parser->previous;
19602 } else {
19603 if (!match2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)) {
19604 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_str(parser->current.type));
19605 }
19606 }
19607
19608 pm_statements_node_t *statements = NULL;
19609 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
19610 statements = parse_statements(parser, PM_CONTEXT_FOR, (uint16_t) (depth + 1));
19611 }
19612
19613 parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false);
19614 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword);
19615
19616 return pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, NTOK2PTR(do_keyword), &parser->previous);
19617}
19618
19619static pm_lambda_node_t *
19620parse_lambda(pm_parser_t *parser, uint16_t depth) {
19621 int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting;
19622 parser->lambda_enclosure_nesting = parser->enclosure_nesting;
19623
19624 pm_token_t operator = parser->current;
19625 size_t opening_newline_index = token_newline_index(parser);
19626 parser_lex(parser);
19627
19628 pm_parser_scope_push(parser, false);
19629 pm_block_parameters_node_t *block_parameters;
19630
19631 switch (parser->current.type) {
19632 case PM_TOKEN_PARENTHESIS_LEFT: {
19633 pm_token_t opening = parser->current;
19634 parser_lex(parser);
19635
19636 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19637 block_parameters = pm_block_parameters_node_create(parser, NULL, &opening);
19638 } else {
19639 block_parameters = parse_block_parameters(parser, false, &opening, true, true, (uint16_t) (depth + 1));
19640 }
19641
19642 accept1(parser, PM_TOKEN_NEWLINE);
19643 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19644
19645 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
19646 break;
19647 }
19648 case PM_CASE_PARAMETER: {
19649 block_parameters = parse_block_parameters(parser, false, NULL, true, false, (uint16_t) (depth + 1));
19650 break;
19651 }
19652 default: {
19653 block_parameters = NULL;
19654 break;
19655 }
19656 }
19657
19658 pm_token_t opening;
19659 pm_node_t *body = NULL;
19660
19661 if (accept1(parser, PM_TOKEN_LAMBDA_BEGIN)) {
19662 opening = parser->previous;
19663
19664 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
19665 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1)));
19666 }
19667
19668 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
19669
19670 /* Restore the enclosing lambda's nesting now that the body has been
19671 * parsed, so that the token following the closing `}` is lexed in the
19672 * enclosing context. During the body the nesting held this lambda's own
19673 * level, which every token inside the braces sits above. This mirrors
19674 * parse.y restoring `p->lex.lpar_beg` after `lambda_body`. */
19675 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
19676 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
19677 } else {
19678 /* A `-> { }` body is delimited by `{`/`}`, whose block-accepting frame
19679 * the lexer manages. A `-> do end` body is delimited by keywords, so
19680 * push the frame here and pop it before `end`. The push must precede
19681 * consuming the `do`, which lexes the first token of the body; this
19682 * matches parse.y's CMDARG_PUSH(0) before `lambda_body`. */
19683 pm_accepts_block_stack_push(parser, true);
19684 expect1(parser, PM_TOKEN_KEYWORD_DO_LAMBDA, PM_ERR_LAMBDA_OPEN);
19685 opening = parser->previous;
19686
19687 /* The lexer cleared the nesting when it produced the `do`. If it was
19688 * missing entirely, clear it here so that the body is recovered the
19689 * same way it would have been parsed: no token within it sits at the
19690 * beginning of a lambda. */
19691 parser->lambda_enclosure_nesting = -1;
19692
19693 if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
19694 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1)));
19695 }
19696
19697 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
19698 assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE));
19699 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)));
19700 } else {
19701 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
19702 }
19703
19704 pm_accepts_block_stack_pop(parser);
19705
19706 /* As with the brace branch above, restore the nesting before consuming
19707 * the closing `end`, which lexes the token that follows it. */
19708 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
19709 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator);
19710 }
19711
19712 pm_constant_id_list_t locals;
19713 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
19714 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &operator, &parser->previous);
19715
19716 pm_parser_scope_pop(parser);
19717 return pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body);
19718}
19719
19720static pm_node_t *
19721parse_regexp(pm_parser_t *parser, uint16_t depth) {
19722 pm_token_t opening = parser->current;
19723 parser_lex(parser);
19724
19725 if (match1(parser, PM_TOKEN_REGEXP_END)) {
19726 // If we get here, then we have an end immediately after a start. In
19727 // that case we'll create an empty content token and return an
19728 // uninterpolated regular expression.
19729 pm_token_t content = (pm_token_t) {
19730 .type = PM_TOKEN_STRING_CONTENT,
19731 .start = parser->previous.end,
19732 .end = parser->previous.end
19733 };
19734
19735 parser_lex(parser);
19736
19737 pm_regular_expression_node_t *node = pm_regular_expression_node_create(parser, &opening, &content, &parser->previous);
19738 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
19739 return UP(node);
19740 }
19741
19743
19744 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
19745 // In this case we've hit string content so we know the regular
19746 // expression at least has something in it. We'll need to check if the
19747 // following token is the end (in which case we can return a plain
19748 // regular expression) or if it's not then it has interpolation.
19749 pm_string_t unescaped = parser->current_string;
19750 pm_token_t content = parser->current;
19751 parser_lex(parser);
19752
19753 // If we hit an end, then we can create a regular expression
19754 // node without interpolation, which can be represented more
19755 // succinctly and more easily compiled.
19756 if (accept1(parser, PM_TOKEN_REGEXP_END)) {
19757 pm_regular_expression_node_t *node = (pm_regular_expression_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
19758
19759 // If we're not immediately followed by a =~, then we
19760 // parse and validate now. If it is followed by a =~,
19761 // then it will get parsed in the =~ handler where
19762 // named captures can also be extracted.
19763 if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) {
19764 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
19765 }
19766
19767 return UP(node);
19768 }
19769
19770 // If we get here, then we have interpolation so we'll need to create
19771 // a regular expression node with interpolation.
19772 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
19773
19774 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
19775 if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
19776 // This is extremely strange, but the first string part of a
19777 // regular expression will always be tagged as binary if we
19778 // are in a US-ASCII file, no matter its contents.
19779 pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING);
19780 }
19781
19782 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
19783 } else {
19784 // If the first part of the body of the regular expression is not a
19785 // string content, then we have interpolation and we need to create an
19786 // interpolated regular expression node.
19787 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
19788 }
19789
19790 // Now that we're here and we have interpolation, we'll parse all of the
19791 // parts into the list.
19792 pm_node_t *part;
19793 while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) {
19794 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19795 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
19796 }
19797 }
19798
19799 pm_token_t closing = parser->current;
19800 if (match1(parser, PM_TOKEN_EOF)) {
19801 pm_parser_err_token(parser, &opening, PM_ERR_REGEXP_TERM);
19802 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19803 } else {
19804 expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM);
19805 }
19806
19807 pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing);
19808 return UP(interpolated);
19809}
19810
19811static pm_node_t *
19812parse_alias(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
19813 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
19814 pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS);
19815 }
19816
19817 pm_token_t keyword = parser->current;
19818 parser_lex(parser);
19819
19820 pm_node_t *new_name = parse_alias_argument(parser, true, (uint16_t) (depth + 1));
19821 pm_node_t *old_name = parse_alias_argument(parser, false, (uint16_t) (depth + 1));
19822
19823 switch (PM_NODE_TYPE(new_name)) {
19824 case PM_BACK_REFERENCE_READ_NODE:
19825 case PM_NUMBERED_REFERENCE_READ_NODE:
19826 case PM_GLOBAL_VARIABLE_READ_NODE: {
19827 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)) {
19828 if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) {
19829 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE);
19830 }
19831 } else if (!PM_NODE_TYPE_P(old_name, PM_ERROR_RECOVERY_NODE)) {
19832 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
19833 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
19834 }
19835
19836 return UP(pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name));
19837 }
19838 case PM_SYMBOL_NODE:
19839 case PM_INTERPOLATED_SYMBOL_NODE: {
19840 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)) {
19841 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
19842 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
19843 }
19844 }
19846 default:
19847 return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name));
19848 }
19849}
19850
19851static pm_until_node_t *
19852parse_until(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19853 pm_token_t keyword = parser->current;
19854 size_t opening_newline_index = token_newline_index(parser);
19855
19856 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
19857 pm_do_loop_stack_push(parser, true);
19858
19859 parser_lex(parser);
19860 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));
19861
19862 pm_do_loop_stack_pop(parser);
19863 context_pop(parser);
19864
19865 pm_token_t do_keyword = { 0 };
19866 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
19867 do_keyword = parser->previous;
19868 } else {
19869 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
19870 }
19871
19872 pm_statements_node_t *statements = NULL;
19873 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
19874 pm_accepts_block_stack_push(parser, true);
19875 statements = parse_statements(parser, PM_CONTEXT_UNTIL, (uint16_t) (depth + 1));
19876 pm_accepts_block_stack_pop(parser);
19877 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19878 }
19879
19880 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
19881 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword);
19882
19883 return pm_until_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0);
19884}
19885
19886static pm_while_node_t *
19887parse_while(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19888 pm_token_t keyword = parser->current;
19889 size_t opening_newline_index = token_newline_index(parser);
19890
19891 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
19892 pm_do_loop_stack_push(parser, true);
19893
19894 parser_lex(parser);
19895 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));
19896
19897 pm_do_loop_stack_pop(parser);
19898 context_pop(parser);
19899
19900 pm_token_t do_keyword = { 0 };
19901 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
19902 do_keyword = parser->previous;
19903 } else {
19904 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
19905 }
19906
19907 pm_statements_node_t *statements = NULL;
19908 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
19909 pm_accepts_block_stack_push(parser, true);
19910 statements = parse_statements(parser, PM_CONTEXT_WHILE, (uint16_t) (depth + 1));
19911 pm_accepts_block_stack_pop(parser);
19912 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
19913 }
19914
19915 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
19916 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword);
19917
19918 return pm_while_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0);
19919}
19920
19921static pm_node_t *
19922parse_xstring(pm_parser_t *parser, uint16_t depth) {
19923 pm_token_t opening = parser->current;
19924 parser_lex(parser);
19925
19926 // When we get here, we don't know if this string is going to have
19927 // interpolation or not, even though it is allowed. Still, we want to be
19928 // able to return a string node without interpolation if we can since
19929 // it'll be faster.
19930 if (match1(parser, PM_TOKEN_STRING_END)) {
19931 // If we get here, then we have an end immediately after a start. In
19932 // that case we'll create an empty content token and return an
19933 // uninterpolated string.
19934 pm_token_t content = (pm_token_t) {
19935 .type = PM_TOKEN_STRING_CONTENT,
19936 .start = parser->previous.end,
19937 .end = parser->previous.end
19938 };
19939
19940 parser_lex(parser);
19941 return UP(pm_xstring_node_create(parser, &opening, &content, &parser->previous));
19942 }
19943
19945
19946 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
19947 // In this case we've hit string content so we know the string
19948 // at least has something in it. We'll need to check if the
19949 // following token is the end (in which case we can return a
19950 // plain string) or if it's not then it has interpolation.
19951 pm_string_t unescaped = parser->current_string;
19952 pm_token_t content = parser->current;
19953 parser_lex(parser);
19954
19955 if (match1(parser, PM_TOKEN_STRING_END)) {
19956 pm_node_t *node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
19957 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
19958 parser_lex(parser);
19959 return node;
19960 }
19961
19962 // If we get here, then we have interpolation so we'll need to
19963 // create a string node with interpolation.
19964 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19965
19966 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
19967 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
19968
19969 pm_interpolated_xstring_node_append(parser->arena, node, part);
19970 } else {
19971 // If the first part of the body of the string is not a string
19972 // content, then we have interpolation and we need to create an
19973 // interpolated string node.
19974 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19975 }
19976
19977 pm_node_t *part;
19978 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
19979 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19980 pm_interpolated_xstring_node_append(parser->arena, node, part);
19981 }
19982 }
19983
19984 pm_token_t closing = parser->current;
19985 if (match1(parser, PM_TOKEN_EOF)) {
19986 pm_parser_err_token(parser, &opening, PM_ERR_XSTRING_TERM);
19987 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19988 } else {
19989 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM);
19990 }
19991 pm_interpolated_xstring_node_closing_set(parser, node, &closing);
19992
19993 return UP(node);
19994}
19995
19996static pm_node_t *
19997parse_multi_target(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_power, uint16_t depth) {
19998 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19999 /* If we get here, then we have a comma immediately following something
20000 * that could have been a target in a multiple assignment. */
20001 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20002 }
20003 return node;
20004}
20005
20006static pm_node_t *
20007parse_identifier(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20008 pm_token_t identifier = parser->current;
20009 parser_lex(parser);
20010
20011 pm_node_t *node = parse_variable_call(parser);
20012
20013 if (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
20014 // If parse_variable_call returned with a call node, then we
20015 // know the identifier is not in the local table. In that case
20016 // we need to check if there are arguments following the
20017 // identifier.
20018 pm_call_node_t *call = (pm_call_node_t *) node;
20019 pm_arguments_t arguments = { 0 };
20020
20021 if (parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1))) {
20022 // Since we found arguments, we need to turn off the
20023 // variable call bit in the flags.
20024 pm_node_flag_unset(UP(call), PM_CALL_NODE_FLAGS_VARIABLE_CALL);
20025
20026 call->opening_loc = arguments.opening_loc;
20027 call->arguments = arguments.arguments;
20028 call->closing_loc = arguments.closing_loc;
20029 call->block = arguments.block;
20030
20031 const pm_location_t *end = pm_arguments_end(&arguments);
20032 if (end == NULL) {
20033 PM_NODE_LENGTH_SET_LOCATION(call, &call->message_loc);
20034 } else {
20035 PM_NODE_LENGTH_SET_LOCATION(call, end);
20036 }
20037 }
20038 } else {
20039 // Otherwise, we know the identifier is in the local table. This
20040 // can still be a method call if it is followed by arguments or
20041 // a block, so we need to check for that here.
20042 if (
20043 ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) ||
20044 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
20045 match1(parser, PM_TOKEN_BRACE_LEFT)
20046 ) {
20047 pm_arguments_t arguments = { 0 };
20048 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20049 pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments);
20050
20051 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
20052 // If we're about to convert an 'it' implicit local
20053 // variable read into a method call, we need to remove
20054 // it from the list of implicit local variables.
20055 pm_node_unreference(parser, node);
20056 } else {
20057 // Otherwise, we're about to convert a regular local
20058 // variable read into a method call, in which case we
20059 // need to indicate that this was not a read for the
20060 // purposes of warnings.
20061 assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE));
20062
20063 if (pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &identifier), PM_TOKEN_LENGTH(&identifier))) {
20064 pm_node_unreference(parser, node);
20065 } else {
20067 pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
20068 }
20069 }
20070
20071 return UP(fcall);
20072 }
20073 }
20074
20075 return parse_multi_target(parser, node, binding_power, depth);
20076}
20077
20078static pm_array_node_t *
20079parse_uninterpolated_symbol_array(pm_parser_t *parser) {
20080 pm_token_t opening = parser->current;
20081 parser_lex(parser);
20082
20083 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20084 pm_node_t *current = NULL;
20085
20086 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20087 accept1(parser, PM_TOKEN_WORDS_SEP);
20088 if (match1(parser, PM_TOKEN_STRING_END)) break;
20089
20090 // Interpolation is not possible but nested heredocs can still lead to
20091 // consecutive (disjoint) string tokens when the final newline is escaped.
20092 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20093 // Record the string node, moving to interpolation if needed.
20094 if (current == NULL) {
20095 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
20096 parser_lex(parser);
20097 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20098 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20099 parser_lex(parser);
20100 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
20101 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20102 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
20103 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 };
20104 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
20105 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
20106 parser_lex(parser);
20107
20108 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20109 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
20110 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
20111
20112 // current is arena-allocated so no explicit free is needed.
20113 current = UP(interpolated);
20114 } else {
20115 assert(false && "unreachable");
20116 }
20117 }
20118
20119 if (current) {
20120 pm_array_node_elements_append(parser->arena, array, current);
20121 current = NULL;
20122 } else {
20123 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT);
20124 }
20125 }
20126
20127 pm_token_t closing = parser->current;
20128 if (match1(parser, PM_TOKEN_EOF)) {
20129 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_LOWER_TERM);
20130 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20131 } else {
20132 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM);
20133 }
20134 pm_array_node_close_set(parser, array, &closing);
20135
20136 return array;
20137}
20138
20139static pm_array_node_t *
20140parse_interpolated_symbol_array(pm_parser_t *parser, uint16_t depth) {
20141 parser_lex(parser);
20142 pm_token_t opening = parser->previous;
20143 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20144
20145 /* This is the current node that we are parsing that will be added to the
20146 * list of elements. */
20147 pm_node_t *current = NULL;
20148
20149 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20150 switch (parser->current.type) {
20151 case PM_TOKEN_WORDS_SEP: {
20152 /* Reset the explicit encoding if we hit a separator since each
20153 * element can have its own encoding. */
20154 parser->explicit_encoding = NULL;
20155
20156 if (current == NULL) {
20157 /* If we hit a separator before we have any content, then we
20158 * don't need to do anything. */
20159 } else {
20160 /* If we hit a separator after we've hit content, then we
20161 * need to append that content to the list and reset the
20162 * current node. */
20163 pm_array_node_elements_append(parser->arena, array, current);
20164 current = NULL;
20165 }
20166
20167 parser_lex(parser);
20168 break;
20169 }
20170 case PM_TOKEN_STRING_CONTENT: {
20171 if (current == NULL) {
20172 /* If we hit content and the current node is NULL, then this
20173 * is the first string content we've seen. In that case
20174 * we're going to create a new string node and set that to
20175 * the current. */
20176 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
20177 parser_lex(parser);
20178 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20179 /* If we hit string content and the current node is an
20180 * interpolated string, then we need to append the string
20181 * content to the list of child nodes. */
20182 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20183 parser_lex(parser);
20184
20185 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
20186 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20187 /* If we hit string content and the current node is a symbol
20188 * node, then we need to convert the current node into an
20189 * interpolated string and add the string content to the
20190 * list of child nodes. */
20191 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
20192 pm_token_t content = {
20193 .type = PM_TOKEN_STRING_CONTENT,
20194 .start = parser->start + cast->content_loc.start,
20195 .end = parser->start + cast->content_loc.start + cast->content_loc.length
20196 };
20197
20198 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
20199 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
20200 parser_lex(parser);
20201
20202 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20203 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
20204 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
20205
20206 current = UP(interpolated);
20207 } else {
20208 assert(false && "unreachable");
20209 }
20210
20211 break;
20212 }
20213 case PM_TOKEN_EMBVAR: {
20214 bool start_location_set = false;
20215 if (current == NULL) {
20216 /* If we hit an embedded variable and the current node is
20217 * NULL, then this is the start of a new string. We'll set
20218 * the current node to a new interpolated string. */
20219 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
20220 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20221 /* If we hit an embedded variable and the current node is a
20222 * string node, then we'll convert the current into an
20223 * interpolated string and add the string node to the list
20224 * of parts. */
20225 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20226
20227 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
20228 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
20229 PM_NODE_START_SET_NODE(interpolated, current);
20230 start_location_set = true;
20231 current = UP(interpolated);
20232 } else {
20233 /* If we hit an embedded variable and the current node is an
20234 * interpolated string, then we'll just add the embedded
20235 * variable. */
20236 }
20237
20238 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20239 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
20240 if (!start_location_set) {
20241 PM_NODE_START_SET_NODE(current, part);
20242 }
20243 break;
20244 }
20245 case PM_TOKEN_EMBEXPR_BEGIN: {
20246 bool start_location_set = false;
20247 if (current == NULL) {
20248 /* If we hit an embedded expression and the current node is
20249 * NULL, then this is the start of a new string. We'll set
20250 * the current node to a new interpolated string. */
20251 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
20252 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20253 /* If we hit an embedded expression and the current node is
20254 * a string node, then we'll convert the current into an
20255 * interpolated string and add the string node to the list
20256 * of parts. */
20257 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20258
20259 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
20260 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
20261 PM_NODE_START_SET_NODE(interpolated, current);
20262 start_location_set = true;
20263 current = UP(interpolated);
20264 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20265 /* If we hit an embedded expression and the current node is
20266 * an interpolated string, then we'll just continue on. */
20267 } else {
20268 assert(false && "unreachable");
20269 }
20270
20271 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20272 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
20273 if (!start_location_set) {
20274 PM_NODE_START_SET_NODE(current, part);
20275 }
20276 break;
20277 }
20278 default:
20279 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_UPPER_ELEMENT);
20280 parser_lex(parser);
20281 break;
20282 }
20283 }
20284
20285 /* If we have a current node, then we need to append it to the list. */
20286 if (current) {
20287 pm_array_node_elements_append(parser->arena, array, current);
20288 }
20289
20290 pm_token_t closing = parser->current;
20291 if (match1(parser, PM_TOKEN_EOF)) {
20292 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_UPPER_TERM);
20293 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20294 } else {
20295 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM);
20296 }
20297 pm_array_node_close_set(parser, array, &closing);
20298
20299 return array;
20300}
20301
20302static pm_array_node_t *
20303parse_uninterpolated_string_array(pm_parser_t *parser) {
20304 pm_token_t opening = parser->current;
20305 parser_lex(parser);
20306
20307 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20308 pm_node_t *current = NULL;
20309
20310 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20311 accept1(parser, PM_TOKEN_WORDS_SEP);
20312 if (match1(parser, PM_TOKEN_STRING_END)) break;
20313
20314 // Interpolation is not possible but nested heredocs can still lead to
20315 // consecutive (disjoint) string tokens when the final newline is escaped.
20316 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20317 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20318
20319 // Record the string node, moving to interpolation if needed.
20320 if (current == NULL) {
20321 current = string;
20322 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20323 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
20324 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20325 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20326 pm_interpolated_string_node_append(parser, interpolated, current);
20327 pm_interpolated_string_node_append(parser, interpolated, string);
20328 current = UP(interpolated);
20329 } else {
20330 assert(false && "unreachable");
20331 }
20332 parser_lex(parser);
20333 }
20334
20335 if (current) {
20336 pm_array_node_elements_append(parser->arena, array, current);
20337 current = NULL;
20338 } else {
20339 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT);
20340 }
20341 }
20342
20343 pm_token_t closing = parser->current;
20344 if (match1(parser, PM_TOKEN_EOF)) {
20345 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_LOWER_TERM);
20346 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20347 } else {
20348 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM);
20349 }
20350
20351 pm_array_node_close_set(parser, array, &closing);
20352 return array;
20353}
20354
20355static pm_array_node_t *
20356parse_interpolated_string_array(pm_parser_t *parser, uint16_t depth) {
20357 pm_token_t opening = parser->current;
20358 parser_lex(parser);
20359
20360 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20361 pm_node_t *current = NULL;
20362
20363 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20364 switch (parser->current.type) {
20365 case PM_TOKEN_WORDS_SEP: {
20366 /* Reset the explicit encoding if we hit a separator since each
20367 * element can have its own encoding. */
20368 parser->explicit_encoding = NULL;
20369
20370 if (current == NULL) {
20371 /* If we hit a separator before we have any content, then we
20372 * don't need to do anything. */
20373 } else {
20374 /* If we hit a separator after we've hit content, then we
20375 * need to append that content to the list and reset the
20376 * current node. */
20377 pm_array_node_elements_append(parser->arena, array, current);
20378 current = NULL;
20379 }
20380
20381 parser_lex(parser);
20382 break;
20383 }
20384 case PM_TOKEN_STRING_CONTENT: {
20385 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20386 pm_node_flag_set(string, parse_unescaped_encoding(parser, parser->explicit_encoding));
20387 parser_lex(parser);
20388
20389 if (current == NULL) {
20390 /* If we hit content and the current node is NULL, then this
20391 * is the first string content we've seen. In that case
20392 * we're going to create a new string node and set that to
20393 * the current. */
20394 current = string;
20395 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20396 /* If we hit string content and the current node is an
20397 * interpolated string, then we need to append the string
20398 * content to the list of child nodes. */
20399 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
20400 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20401 /* If we hit string content and the current node is a string
20402 * node, then we need to convert the current node into an
20403 * interpolated string and add the string content to the
20404 * list of child nodes. */
20405 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20406 pm_interpolated_string_node_append(parser, interpolated, current);
20407 pm_interpolated_string_node_append(parser, interpolated, string);
20408 current = UP(interpolated);
20409 } else {
20410 assert(false && "unreachable");
20411 }
20412
20413 break;
20414 }
20415 case PM_TOKEN_EMBVAR: {
20416 if (current == NULL) {
20417 /* If we hit an embedded variable and the current node is
20418 * NULL, then this is the start of a new string. We'll set
20419 * the current node to a new interpolated string. */
20420 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
20421 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20422 /* If we hit an embedded variable and the current node is a
20423 * string node, then we'll convert the current into an
20424 * interpolated string and add the string node to the list
20425 * of parts. */
20426 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20427 pm_interpolated_string_node_append(parser, interpolated, current);
20428 current = UP(interpolated);
20429 } else {
20430 /* If we hit an embedded variable and the current node is an
20431 * interpolated string, then we'll just add the embedded
20432 * variable. */
20433 }
20434
20435 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20436 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
20437 break;
20438 }
20439 case PM_TOKEN_EMBEXPR_BEGIN: {
20440 if (current == NULL) {
20441 /* If we hit an embedded expression and the current node is
20442 * NULL, then this is the start of a new string. We'll set
20443 * the current node to a new interpolated string. */
20444 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
20445 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20446 /* If we hit an embedded expression and the current node is
20447 * a string node, then we'll convert the current into an
20448 * interpolated string and add the string node to the list
20449 * of parts. */
20450 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20451 pm_interpolated_string_node_append(parser, interpolated, current);
20452 current = UP(interpolated);
20453 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20454 /* If we hit an embedded expression and the current node is
20455 * an interpolated string, then we'll just continue on. */
20456 } else {
20457 assert(false && "unreachable");
20458 }
20459
20460 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
20461 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
20462 break;
20463 }
20464 default:
20465 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_UPPER_ELEMENT);
20466 parser_lex(parser);
20467 break;
20468 }
20469 }
20470
20471 /* If we have a current node, then we need to append it to the list. */
20472 if (current) {
20473 pm_array_node_elements_append(parser->arena, array, current);
20474 }
20475
20476 pm_token_t closing = parser->current;
20477 if (match1(parser, PM_TOKEN_EOF)) {
20478 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_UPPER_TERM);
20479 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20480 } else {
20481 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM);
20482 }
20483
20484 pm_array_node_close_set(parser, array, &closing);
20485 return array;
20486}
20487
20488static pm_node_t *
20489parse_jump(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20490 parser_lex(parser);
20491
20492 pm_token_t keyword = parser->previous;
20493 pm_arguments_t arguments = { 0 };
20494
20495 if (token_begins_expression_p(parser->current.type) || match2(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)) {
20496 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
20497
20498 if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
20499 pm_token_t next = parser->current;
20500 parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
20501
20502 // Reject `foo && return bar`.
20503 if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) {
20504 PM_PARSER_ERR_TOKEN_FORMAT(parser, &next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(next.type));
20505 }
20506
20507 // Reject a trailing comma, e.g. `return a,`. The arguments
20508 // parser silently accepts a trailing comma only when it is
20509 // immediately followed by the EOF terminator; in every other
20510 // case (e.g. `return a,;`) it reports the dangling comma
20511 // itself. We reject the accepted case here to stay in line
20512 // with the command call argument parsing above.
20513 if (parser->previous.type == PM_TOKEN_COMMA && match1(parser, PM_TOKEN_EOF)) {
20514 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
20515 }
20516 }
20517
20518 // It's possible that we've parsed a block argument through our
20519 // call to parse_arguments. If we found one, we should mark it
20520 // as invalid and destroy it, as we don't have a place for it.
20521 if (arguments.block != NULL) {
20522 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20523 pm_node_unreference(parser, arguments.block);
20524 arguments.block = NULL;
20525 }
20526 }
20527
20528 switch (keyword.type) {
20529 case PM_TOKEN_KEYWORD_BREAK: {
20530 pm_node_t *node = UP(pm_break_node_create(parser, &keyword, arguments.arguments));
20531 if (!parser->partial_script) ensure_valid_jump(parser, node);
20532 return node;
20533 }
20534 case PM_TOKEN_KEYWORD_NEXT: {
20535 pm_node_t *node = UP(pm_next_node_create(parser, &keyword, arguments.arguments));
20536 if (!parser->partial_script) ensure_valid_jump(parser, node);
20537 return node;
20538 }
20539 case PM_TOKEN_KEYWORD_RETURN: {
20540 pm_node_t *node = UP(pm_return_node_create(parser, &keyword, arguments.arguments));
20541 ensure_valid_return(parser, node);
20542 return node;
20543 }
20544 default:
20545 assert(false && "unreachable");
20546 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20547 }
20548}
20549
20550static pm_node_t *
20551parse_not(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20552 pm_token_t message = parser->current;
20553 parser_lex(parser);
20554
20555 pm_arguments_t arguments = { 0 };
20556 pm_node_t *receiver = NULL;
20557
20558 /* The `not` keyword without parentheses is only valid in contexts where it
20559 * would be parsed as an expression (i.e., at or below the `not` binding
20560 * power level). In other contexts (e.g., method arguments, array elements,
20561 * assignment right-hand sides), parentheses are required: `not(x)`. An
20562 * exception is made for endless def bodies, where `not` is valid as both
20563 * `arg` and `command` (e.g., `def f = not 1`, `def f = not foo bar`). */
20564 if (binding_power > PM_BINDING_POWER_NOT && !(flags & PM_PARSE_IN_ENDLESS_DEF) && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
20565 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) {
20566 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->previous), 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN);
20567 } else {
20568 accept1(parser, PM_TOKEN_NEWLINE);
20569 pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER);
20570 }
20571
20572 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
20573 }
20574
20575 accept1(parser, PM_TOKEN_NEWLINE);
20576
20577 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
20578 pm_token_t lparen = parser->previous;
20579
20580 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20581 receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
20582 } else {
20583 arguments.opening_loc = TOK2LOC(parser, &lparen);
20584 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));
20585
20586 if (!parser->recovering) {
20587 accept1(parser, PM_TOKEN_NEWLINE);
20588 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20589 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
20590 }
20591 }
20592 } else {
20593 receiver = parse_expression(parser, PM_BINDING_POWER_NOT, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | PM_PARSE_ACCEPTS_COMMAND_CALL), PM_ERR_NOT_EXPRESSION, (uint16_t) (depth + 1));
20594 }
20595
20596 return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
20597}
20598
20600parse_preexe(pm_parser_t *parser, pm_binding_power_t binding_power, uint16_t depth) {
20601 pm_node_list_t current_block_exits = { 0 };
20602 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
20603
20604 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20605 pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
20606 }
20607
20608 pm_token_t keyword = parser->current;
20609 parser_lex(parser);
20610
20611 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_BEGIN_UPCASE_BRACE);
20612 pm_token_t opening = parser->previous;
20613 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE, (uint16_t) (depth + 1));
20614
20615 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening);
20616 pm_context_t context = parser->current_context->context;
20617 if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
20618 pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
20619 }
20620
20621 flush_block_exits(parser, previous_block_exits);
20622 return pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
20623}
20624
20626parse_postexe(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20627 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20628 pm_parser_err_current(parser, PM_ERR_STATEMENT_POSTEXE_END);
20629 }
20630
20631 pm_token_t keyword = parser->current;
20632 parser_lex(parser);
20633
20634 if (context_def_p(parser)) {
20635 pm_parser_warn_token(parser, &keyword, PM_WARN_END_IN_METHOD);
20636 }
20637
20638 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_END_UPCASE_BRACE);
20639 pm_token_t opening = parser->previous;
20640 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE, (uint16_t) (depth + 1));
20641
20642 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening);
20643 return pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous);
20644}
20645
20646static pm_undef_node_t *
20647parse_undef(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20648 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20649 pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF);
20650 }
20651
20652 parser_lex(parser);
20653 pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous);
20654 pm_node_t *name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20655
20656 if (!PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20657 pm_undef_node_append(parser->arena, undef, name);
20658
20659 while (match1(parser, PM_TOKEN_COMMA)) {
20660 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
20661 parser_lex(parser);
20662
20663 name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20664 if (PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20665 break;
20666 }
20667
20668 pm_undef_node_append(parser->arena, undef, name);
20669 }
20670 }
20671
20672 return undef;
20673}
20674
20675static pm_yield_node_t *
20676parse_yield(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20677 pm_token_t keyword = parser->current;
20678 parser_lex(parser);
20679
20680 pm_arguments_t arguments = { 0 };
20681 parse_arguments_list(parser, &arguments, false, flags, (uint16_t) (depth + 1));
20682
20683 /* It's possible that we've parsed a block argument through our call to
20684 * parse_arguments_list. If we found one, we should mark it as invalid and
20685 * destroy it, as we don't have a place for it on the yield node. */
20686 if (arguments.block != NULL) {
20687 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20688 pm_node_unreference(parser, arguments.block);
20689 arguments.block = NULL;
20690 }
20691
20692 pm_yield_node_t *node = pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc);
20693 if (!parser->parsing_eval && !parser->partial_script) ensure_valid_yield(parser, node);
20694
20695 return node;
20696}
20697
20698static pm_node_t *
20699parse_char(pm_parser_t *parser, uint16_t depth) {
20700 pm_node_t *node = UP(pm_string_node_create_current_string(
20701 parser,
20702 &(pm_token_t) {
20703 .type = PM_TOKEN_STRING_BEGIN,
20704 .start = parser->current.start,
20705 .end = parser->current.start + 1
20706 },
20707 &(pm_token_t) {
20708 .type = PM_TOKEN_STRING_CONTENT,
20709 .start = parser->current.start + 1,
20710 .end = parser->current.end
20711 },
20712 NULL
20713 ));
20714
20715 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
20716
20717 /* Skip past the character literal here, since now we have handled
20718 * parser->explicit_encoding correctly. */
20719 parser_lex(parser);
20720
20721 /* Characters can be followed by strings in which case they are
20722 * automatically concatenated. */
20723 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
20724 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
20725 }
20726
20727 return node;
20728}
20729
20730static pm_node_t *
20731parse_constant(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20732 pm_token_t constant = parser->current;
20733 parser_lex(parser);
20734
20735 /* If a constant is immediately followed by parentheses, then this is in
20736 * fact a method call, not a constant read. */
20737 if (
20738 match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
20739 ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR))) ||
20740 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
20741 match1(parser, PM_TOKEN_BRACE_LEFT)
20742 ) {
20743 pm_arguments_t arguments = { 0 };
20744 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20745 return UP(pm_call_node_fcall_create(parser, &constant, &arguments));
20746 }
20747
20748 return parse_multi_target(parser, UP(pm_constant_read_node_create(parser, &parser->previous)), binding_power, depth);
20749}
20750
20751static pm_node_t *
20752parse_constant_path_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint16_t depth) {
20753 pm_token_t delimiter = parser->current;
20754 parser_lex(parser);
20755
20756 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
20757 return parse_multi_target(parser, UP(pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous)), binding_power, depth);
20758}
20759
20760static pm_call_node_t *
20761parse_unary_plus(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20762 if (binding_power > PM_BINDING_POWER_UNARY) {
20763 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20764 }
20765 parser_lex(parser);
20766
20767 pm_token_t operator = parser->previous;
20768 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));
20769 return pm_call_node_unary_create(parser, &operator, receiver, "+@");
20770}
20771
20772static pm_call_node_t *
20773parse_unary_minus(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20774 if (binding_power > PM_BINDING_POWER_UNARY) {
20775 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20776 }
20777 parser_lex(parser);
20778
20779 pm_token_t operator = parser->previous;
20780 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));
20781 return pm_call_node_unary_create(parser, &operator, receiver, "-@");
20782}
20783
20784static pm_node_t *
20785parse_unary_minus_number(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20786 pm_token_t operator = parser->current;
20787 parser_lex(parser);
20788
20789 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));
20790
20791 if (accept1(parser, PM_TOKEN_STAR_STAR)) {
20792 pm_token_t exponent_operator = parser->previous;
20793 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));
20794 node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0));
20795 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20796 } else {
20797 switch (PM_NODE_TYPE(node)) {
20798 case PM_INTEGER_NODE:
20799 case PM_FLOAT_NODE:
20800 case PM_RATIONAL_NODE:
20801 case PM_IMAGINARY_NODE:
20802 parse_negative_numeric(node);
20803 break;
20804 default:
20805 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20806 break;
20807 }
20808 }
20809
20810 return node;
20811}
20812
20813static pm_node_t *
20814parse_unary_star(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
20815 parser_lex(parser);
20816
20817 /* * operators at the beginning of expressions are only valid in the context
20818 * of a multiple assignment. We enforce that here. We'll still lex past it
20819 * though and create an error recovery node. */
20820 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20821 pm_parser_err_prefix(parser, diag_id);
20822 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20823 }
20824
20825 pm_node_t *splat = parse_splat(parser, flags, depth);
20826
20827 if (match1(parser, PM_TOKEN_COMMA)) {
20828 return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20829 } else {
20830 return parse_target_validate(parser, splat, true);
20831 }
20832}
20833
20834static pm_node_t *
20835parse_super(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20836 pm_token_t keyword = parser->current;
20837 parser_lex(parser);
20838
20839 pm_arguments_t arguments = { 0 };
20840 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20841
20842 if (
20843 arguments.opening_loc.length == 0 &&
20844 arguments.arguments == NULL &&
20845 ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE))
20846 ) {
20847 return UP(pm_forwarding_super_node_create(parser, &keyword, &arguments));
20848 }
20849
20850 return UP(pm_super_node_create(parser, &keyword, &arguments));
20851}
20852
20853static pm_range_node_t *
20854parse_range(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
20855 pm_token_t operator = parser->current;
20856 parser_lex(parser);
20857
20858 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));
20859
20860 /* Unary .. and ... are special because these are non-associative operators
20861 * that can also be unary operators. In this case we need to explicitly
20862 * reject code that has a .. or ... that follows this expression. */
20863 if (match2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
20864 pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
20865 }
20866
20867 return pm_range_node_create(parser, NULL, &operator, right);
20868}
20869
20870static pm_call_node_t *
20871parse_bang(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20872 if (binding_power > PM_BINDING_POWER_UNARY) {
20873 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20874 }
20875
20876 parser_lex(parser);
20877
20878 pm_token_t operator = parser->previous;
20879 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));
20880 pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
20881 return pm_call_node_unary_create(parser, &operator, receiver, "!");
20882}
20883
20884static pm_call_node_t *
20885parse_tilde(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
20886 if (binding_power > PM_BINDING_POWER_UNARY) {
20887 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20888 }
20889
20890 parser_lex(parser);
20891
20892 pm_token_t operator = parser->previous;
20893 pm_node_t *receiver = parse_expression(parser, pm_binding_powers[parser->previous.type].right, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20894 return pm_call_node_unary_create(parser, &operator, receiver, "~");
20895}
20896
20900static PRISM_INLINE pm_node_t *
20901parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
20902 switch (parser->current.type) {
20903 case PM_TOKEN_BRACKET_LEFT_ARRAY:
20904 return UP(parse_array(parser, flags, depth));
20905 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
20906 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
20907 return parse_parentheses(parser, binding_power, flags, depth);
20908 case PM_TOKEN_BRACE_LEFT_HASH:
20909 return UP(parse_hash(parser, depth));
20910 case PM_TOKEN_CHARACTER_LITERAL:
20911 return parse_char(parser, depth);
20912 case PM_TOKEN_CONSTANT:
20913 return parse_constant(parser, binding_power, flags, depth);
20914 case PM_TOKEN_UCOLON_COLON:
20915 return parse_constant_path_prefix(parser, binding_power, depth);
20916 case PM_TOKEN_UDOT_DOT:
20917 case PM_TOKEN_UDOT_DOT_DOT:
20918 return UP(parse_range(parser, flags, depth));
20919 case PM_TOKEN_FLOAT:
20920 parser_lex(parser);
20921 return UP(pm_float_node_create(parser, &parser->previous));
20922 case PM_TOKEN_FLOAT_IMAGINARY:
20923 parser_lex(parser);
20924 return UP(pm_float_node_imaginary_create(parser, &parser->previous));
20925 case PM_TOKEN_FLOAT_RATIONAL:
20926 parser_lex(parser);
20927 return UP(pm_float_node_rational_create(parser, &parser->previous));
20928 case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY:
20929 parser_lex(parser);
20930 return UP(pm_float_node_rational_imaginary_create(parser, &parser->previous));
20931 case PM_TOKEN_IDENTIFIER:
20932 case PM_TOKEN_METHOD_NAME:
20933 return parse_identifier(parser, binding_power, flags, depth);
20934 case PM_TOKEN_HEREDOC_START:
20935 return parse_heredoc(parser, depth);
20936 case PM_TOKEN_CLASS_VARIABLE:
20937 parser_lex(parser);
20938 return parse_multi_target(parser, UP(pm_class_variable_read_node_create(parser, &parser->previous)), binding_power, depth);
20939 case PM_TOKEN_NUMBERED_REFERENCE:
20940 parser_lex(parser);
20941 return parse_multi_target(parser, UP(pm_numbered_reference_read_node_create(parser, &parser->previous)), binding_power, depth);
20942 case PM_TOKEN_GLOBAL_VARIABLE:
20943 parser_lex(parser);
20944 return parse_multi_target(parser, UP(pm_global_variable_read_node_create(parser, &parser->previous)), binding_power, depth);
20945 case PM_TOKEN_BACK_REFERENCE:
20946 parser_lex(parser);
20947 return parse_multi_target(parser, UP(pm_back_reference_read_node_create(parser, &parser->previous)), binding_power, depth);
20948 case PM_TOKEN_INSTANCE_VARIABLE:
20949 parser_lex(parser);
20950 return parse_multi_target(parser, UP(pm_instance_variable_read_node_create(parser, &parser->previous)), binding_power, depth);
20951 case PM_TOKEN_INTEGER: {
20952 pm_node_flags_t base = parser->integer.base;
20953 parser_lex(parser);
20954 return UP(pm_integer_node_create(parser, base, &parser->previous));
20955 }
20956 case PM_TOKEN_INTEGER_IMAGINARY: {
20957 pm_node_flags_t base = parser->integer.base;
20958 parser_lex(parser);
20959 return UP(pm_integer_node_imaginary_create(parser, base, &parser->previous));
20960 }
20961 case PM_TOKEN_INTEGER_RATIONAL: {
20962 pm_node_flags_t base = parser->integer.base;
20963 parser_lex(parser);
20964 return UP(pm_integer_node_rational_create(parser, base, &parser->previous));
20965 }
20966 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: {
20967 pm_node_flags_t base = parser->integer.base;
20968 parser_lex(parser);
20969 return UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous));
20970 }
20971 case PM_TOKEN_KEYWORD___ENCODING__:
20972 parser_lex(parser);
20973 return UP(pm_source_encoding_node_create(parser, &parser->previous));
20974 case PM_TOKEN_KEYWORD___FILE__:
20975 parser_lex(parser);
20976 return UP(pm_source_file_node_create(parser, &parser->previous));
20977 case PM_TOKEN_KEYWORD___LINE__:
20978 parser_lex(parser);
20979 return UP(pm_source_line_node_create(parser, &parser->previous));
20980 case PM_TOKEN_KEYWORD_ALIAS:
20981 return parse_alias(parser, binding_power, flags, depth);
20982 case PM_TOKEN_KEYWORD_CASE:
20983 return parse_case(parser, flags, depth);
20984 case PM_TOKEN_KEYWORD_BEGIN:
20985 return UP(parse_begin(parser, depth));
20986 case PM_TOKEN_KEYWORD_BEGIN_UPCASE:
20987 return UP(parse_preexe(parser, binding_power, depth));
20988 case PM_TOKEN_KEYWORD_BREAK:
20989 case PM_TOKEN_KEYWORD_NEXT:
20990 case PM_TOKEN_KEYWORD_RETURN:
20991 return parse_jump(parser, flags, depth);
20992 case PM_TOKEN_KEYWORD_SUPER:
20993 return parse_super(parser, flags, depth);
20994 case PM_TOKEN_KEYWORD_YIELD:
20995 return UP(parse_yield(parser, flags, depth));
20996 case PM_TOKEN_KEYWORD_CLASS:
20997 return parse_class(parser, flags, depth);
20998 case PM_TOKEN_KEYWORD_DEF:
20999 return parse_def(parser, binding_power, flags, depth);
21000 case PM_TOKEN_KEYWORD_DEFINED:
21001 return UP(parse_defined(parser, flags, depth));
21002 case PM_TOKEN_KEYWORD_END_UPCASE:
21003 return UP(parse_postexe(parser, binding_power, flags, depth));
21004 case PM_TOKEN_KEYWORD_FALSE:
21005 parser_lex(parser);
21006 return UP(pm_false_node_create(parser, &parser->previous));
21007 case PM_TOKEN_KEYWORD_FOR:
21008 return UP(parse_for(parser, flags, depth));
21009 case PM_TOKEN_KEYWORD_IF:
21010 if (parser_end_of_line_p(parser)) {
21011 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
21012 }
21013
21014 size_t opening_newline_index = token_newline_index(parser);
21015 bool if_after_else = parser->previous.type == PM_TOKEN_KEYWORD_ELSE;
21016 parser_lex(parser);
21017
21018 return parse_conditional(parser, PM_CONTEXT_IF, opening_newline_index, if_after_else, (uint16_t) (depth + 1));
21019 case PM_TOKEN_KEYWORD_UNDEF:
21020 return UP(parse_undef(parser, binding_power, flags, depth));
21021 case PM_TOKEN_KEYWORD_NOT:
21022 return parse_not(parser, binding_power, flags, depth);
21023 case PM_TOKEN_KEYWORD_UNLESS: {
21024 size_t opening_newline_index = token_newline_index(parser);
21025 parser_lex(parser);
21026
21027 return parse_conditional(parser, PM_CONTEXT_UNLESS, opening_newline_index, false, (uint16_t) (depth + 1));
21028 }
21029 case PM_TOKEN_KEYWORD_MODULE:
21030 return parse_module(parser, flags, depth);
21031 case PM_TOKEN_KEYWORD_NIL:
21032 parser_lex(parser);
21033 return UP(pm_nil_node_create(parser, &parser->previous));
21034 case PM_TOKEN_KEYWORD_REDO: {
21035 parser_lex(parser);
21036
21037 pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous));
21038 if (!parser->partial_script) ensure_valid_jump(parser, node);
21039
21040 return node;
21041 }
21042 case PM_TOKEN_KEYWORD_RETRY: {
21043 parser_lex(parser);
21044
21045 pm_node_t *node = UP(pm_retry_node_create(parser, &parser->previous));
21046 ensure_valid_retry(parser, node);
21047
21048 return node;
21049 }
21050 case PM_TOKEN_KEYWORD_SELF:
21051 parser_lex(parser);
21052 return UP(pm_self_node_create(parser, &parser->previous));
21053 case PM_TOKEN_KEYWORD_TRUE:
21054 parser_lex(parser);
21055 return UP(pm_true_node_create(parser, &parser->previous));
21056 case PM_TOKEN_KEYWORD_UNTIL:
21057 return UP(parse_until(parser, flags, depth));
21058 case PM_TOKEN_KEYWORD_WHILE:
21059 return UP(parse_while(parser, flags, depth));
21060 case PM_TOKEN_PERCENT_LOWER_I:
21061 return UP(parse_uninterpolated_symbol_array(parser));
21062 case PM_TOKEN_PERCENT_UPPER_I:
21063 return UP(parse_interpolated_symbol_array(parser, depth));
21064 case PM_TOKEN_PERCENT_LOWER_W:
21065 return UP(parse_uninterpolated_string_array(parser));
21066 case PM_TOKEN_PERCENT_UPPER_W:
21067 return UP(parse_interpolated_string_array(parser, depth));
21068 case PM_TOKEN_REGEXP_BEGIN:
21069 return parse_regexp(parser, depth);
21070 case PM_TOKEN_XSTRING_BEGIN:
21071 case PM_TOKEN_PERCENT_LOWER_X:
21072 return parse_xstring(parser, depth);
21073 case PM_TOKEN_USTAR:
21074 return parse_unary_star(parser, binding_power, flags, diag_id, depth);
21075 case PM_TOKEN_BANG:
21076 return UP(parse_bang(parser, binding_power, flags, depth));
21077 case PM_TOKEN_TILDE:
21078 return UP(parse_tilde(parser, binding_power, flags, depth));
21079 case PM_TOKEN_UMINUS:
21080 return UP(parse_unary_minus(parser, binding_power, flags, depth));
21081 case PM_TOKEN_UMINUS_NUM:
21082 return UP(parse_unary_minus_number(parser, flags, depth));
21083 case PM_TOKEN_MINUS_GREATER:
21084 return UP(parse_lambda(parser, depth));
21085 case PM_TOKEN_UPLUS:
21086 return UP(parse_unary_plus(parser, binding_power, flags, depth));
21087 case PM_TOKEN_STRING_BEGIN:
21088 return parse_strings(parser, NULL, flags & PM_PARSE_ACCEPTS_LABEL, (uint16_t) (depth + 1));
21089 case PM_TOKEN_SYMBOL_BEGIN:
21090 return parse_symbol(parser, PM_LEX_STATE_END, (uint16_t) (depth + 1));
21091 default: {
21092 pm_context_t recoverable = context_recoverable(parser, &parser->current);
21093
21094 if (recoverable != PM_CONTEXT_NONE) {
21095 parser->recovering = true;
21096
21097 // If the given error is not the generic one, then we'll add it
21098 // here because it will provide more context in addition to the
21099 // recoverable error that we will also add.
21100 if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) {
21101 pm_parser_err_prefix(parser, diag_id);
21102 }
21103
21104 // If we get here, then we are assuming this token is closing a
21105 // parent context, so we'll indicate that to the user so that
21106 // they know how we behaved.
21107 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_str(parser->current.type), context_human(recoverable));
21108 } else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) {
21109 // We're going to make a special case here, because "cannot
21110 // parse expression" is pretty generic, and we know here that we
21111 // have an unexpected token.
21112 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_str(parser->current.type));
21113 } else {
21114 pm_parser_err_prefix(parser, diag_id);
21115 }
21116
21117 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
21118 }
21119 }
21120}
21121
21122static pm_node_t *
21123parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth);
21124
21132static void
21133parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
21134 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21135 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21136 parser_lex(parser);
21137 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));
21138 }
21139}
21140
21150static pm_node_t *
21151parse_assignment_value(pm_parser_t *parser, pm_binding_power_t previous_binding_power, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
21152 pm_node_t *value = parse_value_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT ? (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) : (previous_binding_power < PM_BINDING_POWER_MATCH ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0))), diag_id, (uint16_t) (depth + 1));
21153
21154 // Assignments whose value is a command call (e.g., a = b c) can only
21155 // be followed by modifiers (if/unless/while/until/rescue) and not by
21156 // operators with higher binding power. If we find one, emit an error
21157 // and skip the operator and its right-hand side.
21158 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER && (pm_command_call_value_p(parser, value) || pm_block_call_p(value))) {
21159 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21160 parser_lex(parser);
21161 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));
21162 }
21163
21164 // Contradicting binding powers, the right-hand-side value of the assignment
21165 // allows the `rescue` modifier.
21166 if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21167 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21168
21169 pm_token_t rescue = parser->current;
21170 parser_lex(parser);
21171
21172 // As in parse_assignment_values, the resbody is a `stmt` (permitting a
21173 // multiple assignment / command call) when the rescued value is itself a
21174 // command call, and a plain `arg` otherwise.
21175 bool statement_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21176 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21177
21178 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21179 context_pop(parser);
21180
21181 // A pattern-match resbody is a statement, but here the rescue is nested
21182 // in an assignment value where parse_expression_terminator cannot see
21183 // it, so reject a trailing operator above the modifier level directly.
21184 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21185 parse_rescue_modifier_terminator(parser, flags, depth);
21186 }
21187
21188 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21189 }
21190
21191 return value;
21192}
21193
21198static void
21199parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) {
21200 switch (PM_NODE_TYPE(node)) {
21201 case PM_BEGIN_NODE: {
21202 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
21203 if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements);
21204 break;
21205 }
21206 case PM_LOCAL_VARIABLE_WRITE_NODE: {
21208 pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
21209 break;
21210 }
21211 case PM_PARENTHESES_NODE: {
21212 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
21213 if (cast->body != NULL) parse_assignment_value_local(parser, cast->body);
21214 break;
21215 }
21216 case PM_STATEMENTS_NODE: {
21217 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
21218 const pm_node_t *statement;
21219
21220 PM_NODE_LIST_FOREACH(&cast->body, index, statement) {
21221 parse_assignment_value_local(parser, statement);
21222 }
21223 break;
21224 }
21225 default:
21226 break;
21227 }
21228}
21229
21242static pm_node_t *
21243parse_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) {
21244 bool statement_level = (previous_binding_power == PM_BINDING_POWER_STATEMENT) || (flags & PM_PARSE_ACCEPTS_STATEMENT);
21245
21246 bool permitted = true;
21247 if (!statement_level && match1(parser, PM_TOKEN_USTAR)) permitted = false;
21248
21249 // A command call (e.g. `x = y z`) is permitted as the value when assigning
21250 // directly (carrying the caller's flag), or in any statement-level context
21251 // — which includes a rescue modifier value via the flag.
21252 uint8_t command_call_flag = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
21253 ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21254 : ((previous_binding_power < PM_BINDING_POWER_MODIFIER || statement_level) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21255
21256 pm_node_t *value = parse_starred_expression(parser, binding_power, (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | command_call_flag), diag_id, (uint16_t) (depth + 1));
21257 if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
21258
21259 parse_assignment_value_local(parser, value);
21260 bool single_value = true;
21261
21262 // Block calls (command call + do block, e.g., `foo bar do end`) cannot
21263 // be followed by a comma to form a multi-value RHS because each element
21264 // of a multi-value assignment must be an `arg`, not a `block_call`.
21265 if (statement_level && !pm_block_call_p(value) && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) {
21266 single_value = false;
21267
21268 pm_array_node_t *array = pm_array_node_create(parser, NULL);
21269 pm_array_node_elements_append(parser->arena, array, value);
21270 value = UP(array);
21271
21272 while (accept1(parser, PM_TOKEN_COMMA)) {
21273 pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1));
21274
21275 pm_array_node_elements_append(parser->arena, array, element);
21276 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
21277
21278 parse_assignment_value_local(parser, element);
21279 }
21280 }
21281
21282 // Assignments whose value is a command call (e.g., a = b c) can only
21283 // be followed by modifiers (if/unless/while/until/rescue) and not by
21284 // operators with higher binding power. If we find one, emit an error
21285 // and skip the operator and its right-hand side.
21286 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))) {
21287 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21288 parser_lex(parser);
21289 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));
21290 }
21291
21292 // Contradicting binding powers, the right-hand-side value of the assignment
21293 // allows the `rescue` modifier.
21294 bool multiple_assignment = (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1));
21295 if ((single_value || multiple_assignment) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21296 bool command_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21297
21298 // A multiple assignment whose value is a command call (`x, y = foo
21299 // bar`) is a complete statement (parse.y: `mlhs '='
21300 // command_call_value`, which has no rescue), so a trailing `rescue`
21301 // modifies the whole assignment rather than the value. Leave it for the
21302 // statement-level rescue instead of binding it to the value here. For a
21303 // non-command value the rescue does bind to the value (parse.y:
21304 // `mlhs '=' mrhs_arg modifier_rescue stmt`).
21305 if (multiple_assignment && command_value) return value;
21306
21307 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21308
21309 pm_token_t rescue = parser->current;
21310 parser_lex(parser);
21311
21312 // The resbody is a `stmt` (parse.y: `command_rhs`/`mlhs '=' mrhs_arg`),
21313 // which permits a multiple assignment and a command call, when this is a
21314 // multiple assignment or the rescued value is itself a command call.
21315 // Otherwise it is a plain `arg` (parse.y: `arg_rhs`).
21316 bool statement_value = multiple_assignment || command_value;
21317 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21318
21319 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21320 context_pop(parser);
21321
21322 // A pattern-match resbody is a statement, but here the rescue is nested
21323 // in an assignment value where parse_expression_terminator cannot see
21324 // it, so reject a trailing operator above the modifier level directly.
21325 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21326 parse_rescue_modifier_terminator(parser, flags, depth);
21327 }
21328
21329 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21330 }
21331
21332 return value;
21333}
21334
21347static pm_node_t *
21348parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth) {
21349 if (statement) {
21350 pm_node_t *value;
21351 bool multiple;
21352
21353 if (match1(parser, PM_TOKEN_USTAR)) {
21354 // A leading splat can only begin a multiple assignment target list.
21355 parser_lex(parser);
21356 value = parse_splat(parser, flags, depth);
21357 multiple = true;
21358 } else {
21359 // The flag lets a single-target assignment take a multiple-value or
21360 // splat right-hand side (`b = c, d` / `b = *c`); a comma _before_ an
21361 // `=`, or a parenthesized target list (`(b, c), d = 1`), instead
21362 // promotes to a multiple assignment target list below.
21363 value = parse_expression(parser, pm_binding_powers[PM_TOKEN_KEYWORD_RESCUE_MODIFIER].right, flags | PM_PARSE_ACCEPTS_STATEMENT, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21364 multiple = match1(parser, PM_TOKEN_COMMA) || PM_NODE_TYPE_P(value, PM_MULTI_TARGET_NODE);
21365 }
21366
21367 if (multiple) {
21368 pm_node_t *target = parse_targets_validate(parser, value, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21369
21370 // A promoted target list is only a valid rescue value as part of a
21371 // complete `targets = values`. parse_targets_validate already
21372 // reports a missing `=` for every terminator except `)` (which it
21373 // permits for an enclosing mlhs paren that does not apply here), so
21374 // reject that case.
21375 if (!match1(parser, PM_TOKEN_EQUAL)) {
21376 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
21377 return target;
21378 }
21379
21380 pm_token_t operator = parser->current;
21381 parser_lex(parser);
21382
21383 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));
21384 value = parse_write(parser, target, &operator, values);
21385 }
21386
21387 // Reject a trailing operator that cannot follow a statement resbody.
21388 // Pattern-match handlers are statements too, but for the bare statement
21389 // form they are reported by parse_expression_terminator instead (which
21390 // keeps its existing error-recovery), so they are excluded here.
21391 if (!PM_NODE_TYPE_P(value, PM_MATCH_REQUIRED_NODE) && !PM_NODE_TYPE_P(value, PM_MATCH_PREDICATE_NODE)) {
21392 parse_rescue_modifier_terminator(parser, flags, depth);
21393 }
21394
21395 return value;
21396 }
21397
21398 // Otherwise the resbody is a plain `arg` (parse.y: `arg modifier_rescue
21399 // arg`), parsed above the `and`/`or`/`not` level so those stay outside it.
21400 return parse_expression(parser, PM_BINDING_POWER_DEFINED, flags, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21401}
21402
21410static void
21411parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) {
21412 if (call_node->arguments != NULL) {
21413 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS);
21414 pm_node_unreference(parser, UP(call_node->arguments));
21415 call_node->arguments = NULL;
21416 }
21417
21418 if (call_node->block != NULL) {
21419 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK);
21420 pm_node_unreference(parser, UP(call_node->block));
21421 call_node->block = NULL;
21422 }
21423}
21424
21425static PRISM_INLINE const uint8_t *
21426pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21427 cursor++;
21428
21429 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21430 uint8_t value = escape_hexadecimal_digit(*cursor);
21431 cursor++;
21432
21433 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21434 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(*cursor));
21435 cursor++;
21436 }
21437
21438 pm_buffer_append_byte(unescaped, value);
21439 } else {
21440 pm_buffer_append_string(unescaped, "\\x", 2);
21441 }
21442
21443 return cursor;
21444}
21445
21446static PRISM_INLINE const uint8_t *
21447pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21448 uint8_t value = (uint8_t) (*cursor - '0');
21449 cursor++;
21450
21451 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21452 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21453 cursor++;
21454
21455 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21456 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21457 cursor++;
21458 }
21459 }
21460
21461 pm_buffer_append_byte(unescaped, value);
21462 return cursor;
21463}
21464
21465static PRISM_INLINE const uint8_t *
21466pm_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) {
21467 const uint8_t *start = cursor - 1;
21468 cursor++;
21469
21470 if (cursor >= end) {
21471 pm_buffer_append_string(unescaped, "\\u", 2);
21472 return cursor;
21473 }
21474
21475 if (*cursor != '{') {
21476 size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4));
21477 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21478
21479 if (!pm_buffer_append_unicode_codepoint(unescaped, value)) {
21480 pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start));
21481 }
21482
21483 return cursor + length;
21484 }
21485
21486 cursor++;
21487 for (;;) {
21488 while (cursor < end && *cursor == ' ') cursor++;
21489
21490 if (cursor >= end) break;
21491 if (*cursor == '}') {
21492 cursor++;
21493 break;
21494 }
21495
21496 size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor);
21497 if (length == 0) {
21498 break;
21499 }
21500 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21501
21502 (void) pm_buffer_append_unicode_codepoint(unescaped, value);
21503 cursor += length;
21504 }
21505
21506 return cursor;
21507}
21508
21509static void
21510pm_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) {
21511 const uint8_t *end = source + length;
21512 pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source));
21513
21514 for (;;) {
21515 if (++cursor >= end) {
21516 pm_buffer_append_byte(unescaped, '\\');
21517 return;
21518 }
21519
21520 switch (*cursor) {
21521 case 'x':
21522 cursor = pm_named_capture_escape_hex(unescaped, cursor, end);
21523 break;
21524 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
21525 cursor = pm_named_capture_escape_octal(unescaped, cursor, end);
21526 break;
21527 case 'u':
21528 cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location);
21529 break;
21530 default:
21531 pm_buffer_append_byte(unescaped, '\\');
21532 break;
21533 }
21534
21535 const uint8_t *next_cursor = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, parser->encoding);
21536 if (next_cursor == NULL) break;
21537
21538 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (next_cursor - cursor));
21539 cursor = next_cursor;
21540 }
21541
21542 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (end - cursor));
21543}
21544
21563
21568static void
21569parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *capture, void *data) {
21570 pm_regexp_name_data_t *name_data = (pm_regexp_name_data_t *) data;
21571 pm_call_node_t *call = name_data->call;
21572 pm_constant_id_set_t *names = &name_data->names;
21573
21574 const uint8_t *source = pm_string_source(capture);
21575 size_t length = pm_string_length(capture);
21576 pm_buffer_t unescaped = { 0 };
21577
21578 // First, we need to handle escapes within the name of the capture group.
21579 // This is because regular expressions have three different representations
21580 // in prism. The first is the plain source code. The second is the
21581 // representation that will be sent to the regular expression engine, which
21582 // is the value of the "unescaped" field. This is poorly named, because it
21583 // actually still contains escapes, just a subset of them that the regular
21584 // expression engine knows how to handle. The third representation is fully
21585 // unescaped, which is what we need.
21586 const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding);
21587 if (PRISM_UNLIKELY(cursor != NULL)) {
21588 pm_named_capture_escape(parser, &unescaped, source, length, cursor, name_data->shared ? NULL : &call->receiver->location);
21589
21590 /*
21591 * Escapes like \u{} write nothing, so the unescaped name can be empty.
21592 * An empty name is never a valid local, and the buffer is unallocated
21593 * (NULL) in that case, so there is no slice to check.
21594 */
21595 if (pm_buffer_length(&unescaped) == 0) return;
21596
21597 source = (const uint8_t *) pm_buffer_value(&unescaped);
21598 length = pm_buffer_length(&unescaped);
21599 }
21600
21601 const uint8_t *start;
21602 const uint8_t *end;
21603 pm_constant_id_t name;
21604
21605 // If the name of the capture group isn't a valid identifier, we do
21606 // not add it to the local table.
21607 if (!pm_slice_is_valid_local(parser, source, source + length)) {
21608 pm_buffer_cleanup(&unescaped);
21609 return;
21610 }
21611
21612 if (name_data->shared) {
21613 // If the unescaped string is a slice of the source, then we can
21614 // copy the names directly. The pointers will line up.
21615 start = source;
21616 end = source + length;
21617 name = pm_parser_constant_id_raw(parser, start, end);
21618 } else {
21619 // Otherwise, the name is a slice of the malloc-ed owned string,
21620 // in which case we need to copy it out into a new string.
21621 start = parser->start + PM_NODE_START(call->receiver);
21622 end = parser->start + PM_NODE_END(call->receiver);
21623
21624 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
21625 memcpy(memory, source, length);
21626 name = pm_parser_constant_id_owned(parser, memory, length);
21627 }
21628
21629 /*
21630 * Add this name to the set of constants if it is valid, not duplicated,
21631 * and not a keyword.
21632 */
21633 if (name != 0 && pm_constant_id_set_insert(parser->arena, names, name)) {
21634
21635 int depth;
21636 if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
21637 // If the local is not already a local but it is a keyword, then we
21638 // do not want to add a capture for this.
21639 if (pm_local_is_keyword((const char *) source, length)) {
21640 pm_buffer_cleanup(&unescaped);
21641 return;
21642 }
21643
21644 // If the identifier is not already a local, then we will add it to
21645 // the local table.
21646 pm_parser_local_add(parser, name, start, end, 0);
21647 }
21648
21649 // Here we lazily create the MatchWriteNode since we know we're
21650 // about to add a target.
21651 if (name_data->match == NULL) {
21652 name_data->match = pm_match_write_node_create(parser, call);
21653 }
21654
21655 // Next, create the local variable target and add it to the list of
21656 // targets for the match.
21657 pm_token_t token = { .type = 0, .start = start, .end = end };
21658 pm_location_t token_loc = TOK2LOC(parser, &token);
21659 pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &token_loc, name, depth == -1 ? 0 : (uint32_t) depth));
21660 pm_node_list_append(parser->arena, &name_data->match->targets, target);
21661 }
21662
21663 pm_buffer_cleanup(&unescaped);
21664}
21665
21671static pm_node_t *
21672parse_interpolated_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call, bool extended_mode) {
21673 pm_regexp_name_data_t name_data = {
21674 .call = call,
21675 .match = NULL,
21676 .names = { 0 },
21677 .shared = false,
21678 };
21679
21680 pm_regexp_parse_named_captures(parser, pm_string_source(content), pm_string_length(content), extended_mode, parse_regular_expression_named_capture, &name_data);
21681
21682 if (name_data.match != NULL) {
21683 return UP(name_data.match);
21684 } else {
21685 return UP(call);
21686 }
21687}
21688
21689static pm_if_node_t *
21690parse_ternary(pm_parser_t *parser, pm_node_t *node, uint8_t flags, uint16_t depth) {
21691 context_push(parser, PM_CONTEXT_TERNARY);
21692 pm_node_list_t current_block_exits = { 0 };
21693 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
21694
21695 pm_token_t qmark = parser->current;
21696 parser_lex(parser);
21697
21698 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));
21699
21700 if (parser->recovering) {
21701 // If parsing the true expression of this ternary resulted in a syntax
21702 // error that we can recover from, then we're going to put missing nodes
21703 // and tokens into the remaining places. We want to be sure to do this
21704 // before the `expect` function call to make sure it doesn't
21705 // accidentally move past a ':' token that occurs after the syntax
21706 // error.
21707 pm_token_t colon = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
21708 pm_node_t *false_expression = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &colon), PM_TOKEN_LENGTH(&colon)));
21709
21710 context_pop(parser);
21711 pop_block_exits(parser, previous_block_exits);
21712 return pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
21713 }
21714
21715 accept1(parser, PM_TOKEN_NEWLINE);
21716 expect1(parser, PM_TOKEN_COLON, PM_ERR_TERNARY_COLON);
21717
21718 pm_token_t colon = parser->previous;
21719 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));
21720
21721 context_pop(parser);
21722 pop_block_exits(parser, previous_block_exits);
21723 return pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression);
21724}
21725
21726static pm_node_t *
21727parse_constant_path_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, uint8_t flags, uint16_t depth) {
21728 pm_token_t delimiter = parser->current;
21729 parser_lex(parser);
21730
21731 switch (parser->current.type) {
21732 case PM_TOKEN_CONSTANT: {
21733 parser_lex(parser);
21734 pm_node_t *path;
21735
21736 if (
21737 (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
21738 ((flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && (token_begins_expression_p(parser->current.type) || match3(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)))
21739 ) {
21740 /* If we have a constant immediately following a '::' operator,
21741 * then this can either be a constant path or a method call,
21742 * depending on what follows the constant.
21743 *
21744 * If we have parentheses, then this is a method call. That
21745 * would look like Foo::Bar(). */
21746 pm_token_t message = parser->previous;
21747 pm_arguments_t arguments = { 0 };
21748
21749 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
21750 path = UP(pm_call_node_call_create(parser, node, &delimiter, &message, &arguments));
21751 } else {
21752 /* Otherwise, this is a constant path. That would look like
21753 * Foo::Bar. */
21754 path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
21755 }
21756
21757 return parse_multi_target(parser, path, previous_binding_power, depth);
21758 }
21759 case PM_CASE_OPERATOR:
21760 case PM_CASE_KEYWORD:
21761 case PM_TOKEN_IDENTIFIER:
21762 case PM_TOKEN_METHOD_NAME: {
21763 parser_lex(parser);
21764 pm_token_t message = parser->previous;
21765
21766 /* If we have an identifier following a '::' operator, then it is
21767 * for sure a method call. */
21768 pm_arguments_t arguments = { 0 };
21769 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
21770
21771 pm_call_node_t *call = pm_call_node_call_create(parser, node, &delimiter, &message, &arguments);
21772 return parse_multi_target(parser, UP(call), previous_binding_power, depth);
21773 }
21774 case PM_TOKEN_PARENTHESIS_LEFT: {
21775 /* If we have a parenthesis following a '::' operator, then it is the
21776 * method call shorthand. That would look like Foo::(bar). */
21777 pm_arguments_t arguments = { 0 };
21778 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
21779
21780 return UP(pm_call_node_shorthand_create(parser, node, &delimiter, &arguments));
21781 }
21782 default:
21783 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
21784 return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
21785 }
21786}
21787
21788static pm_node_t *
21789parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
21790 /* Note that we _must_ parse the value before adding the local variables in
21791 * order to properly mirror the behavior of Ruby. For example,
21792 *
21793 * /(?<foo>bar)/ =~ foo
21794 *
21795 * In this case, `foo` should be a method call and not a local yet.
21796 */
21797 pm_token_t operator = parser->current;
21798 parser_lex(parser);
21799
21800 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));
21801
21802 // By default, we're going to create a call node and then return it.
21803 pm_call_node_t *call = pm_call_node_binary_create(parser, node, &operator, argument, 0);
21804 pm_node_t *result = UP(call);
21805
21806 /* If the receiver of this =~ is a regular expression node, then we need to
21807 * introduce local variables for it based on its named capture groups. */
21808 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE)) {
21809 /* It's possible to have an interpolated regular expression node that
21810 * only contains strings. This is because it can be split up by a
21811 * heredoc. In this case we need to concat the unescaped strings
21812 * together and then parse them as a regular expression. */
21814
21815 bool interpolated = false;
21816 size_t total_length = 0;
21817
21818 pm_node_t *part;
21819 PM_NODE_LIST_FOREACH(parts, index, part) {
21820 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
21821 total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped);
21822 } else {
21823 interpolated = true;
21824 break;
21825 }
21826 }
21827
21828 if (!interpolated && total_length > 0) {
21829 void *memory = xmalloc(total_length);
21830 if (!memory) abort();
21831
21832 uint8_t *cursor = memory;
21833 PM_NODE_LIST_FOREACH(parts, index, part) {
21834 pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
21835 size_t length = pm_string_length(unescaped);
21836
21837 memcpy(cursor, pm_string_source(unescaped), length);
21838 cursor += length;
21839 }
21840
21841 pm_string_t owned;
21842 pm_string_owned_init(&owned, (uint8_t *) memory, total_length);
21843
21844 result = parse_interpolated_regular_expression_named_captures(parser, &owned, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
21845 pm_string_cleanup(&owned);
21846 }
21847 } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
21848 /* If we have a regular expression node, then we can parse the named
21849 * captures and validate encoding in one pass. */
21851 pm_regexp_name_data_t name_data = {
21852 .call = call,
21853 .match = NULL,
21854 .names = { 0 },
21855 .shared = regexp->unescaped.type == PM_STRING_SHARED,
21856 };
21857
21858 pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data));
21859
21860 if (name_data.match != NULL) {
21861 result = UP(name_data.match);
21862 }
21863 }
21864
21865 return result;
21866}
21867
21868static pm_node_t *
21869parse_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) {
21870 pm_token_t operator = parser->current;
21871
21872 switch (PM_NODE_TYPE(node)) {
21873 case PM_CALL_NODE: {
21874 // If we have no arguments to the call node and we need this
21875 // to be a target then this is either a method call or a
21876 // local variable write. This _must_ happen before the value
21877 // is parsed because it could be referenced in the value.
21878 pm_call_node_t *call_node = (pm_call_node_t *) node;
21879 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21880 pm_parser_local_add_location(parser, &call_node->message_loc, 0);
21881 }
21882 }
21884 case PM_CASE_WRITABLE: {
21885 // When we have `it = value`, we need to add `it` as a local
21886 // variable before parsing the value, in case the value
21887 // references the variable.
21888 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
21889 pm_parser_local_add_location(parser, &node->location, 0);
21890 }
21891
21892 parser_lex(parser);
21893 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));
21894
21895 if (PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) && previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
21896 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_MULTI_WRITE);
21897 }
21898
21899 return parse_write(parser, node, &operator, value);
21900 }
21901 case PM_SPLAT_NODE: {
21902 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
21903 pm_multi_target_node_targets_append(parser, multi_target, node);
21904
21905 parser_lex(parser);
21906 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));
21907 return parse_write(parser, UP(multi_target), &operator, value);
21908 }
21909 case PM_SOURCE_ENCODING_NODE:
21910 case PM_FALSE_NODE:
21911 case PM_SOURCE_FILE_NODE:
21912 case PM_SOURCE_LINE_NODE:
21913 case PM_NIL_NODE:
21914 case PM_SELF_NODE:
21915 case PM_TRUE_NODE: {
21916 // In these special cases, we have specific error messages
21917 // and we will replace them with local variable writes.
21918 parser_lex(parser);
21919 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
21920 return parse_unwriteable_write(parser, node, &operator, value);
21921 }
21922 default:
21923 // In this case we have an = sign, but we don't know what
21924 // it's for. We need to treat it as an error. We'll mark it
21925 // as an error and skip past it.
21926 parser_lex(parser);
21927 pm_parser_err_token(parser, &operator, PM_ERR_EXPRESSION_NOT_WRITABLE);
21928 return node;
21929 }
21930}
21931
21932static pm_node_t *
21933parse_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) {
21934 pm_token_t operator = parser->current;
21935
21936 switch (PM_NODE_TYPE(node)) {
21937 case PM_BACK_REFERENCE_READ_NODE:
21938 case PM_NUMBERED_REFERENCE_READ_NODE:
21939 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21941 case PM_GLOBAL_VARIABLE_READ_NODE: {
21942 parser_lex(parser);
21943
21944 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21945 pm_node_t *result = UP(pm_global_variable_and_write_node_create(parser, node, &operator, value));
21946
21947 return result;
21948 }
21949 case PM_CLASS_VARIABLE_READ_NODE: {
21950 parser_lex(parser);
21951
21952 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21953 pm_node_t *result = UP(pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &operator, value));
21954
21955 return result;
21956 }
21957 case PM_CONSTANT_PATH_NODE: {
21958 parser_lex(parser);
21959
21960 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21961 pm_node_t *write = UP(pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &operator, value));
21962
21963 return parse_shareable_constant_write(parser, write);
21964 }
21965 case PM_CONSTANT_READ_NODE: {
21966 parser_lex(parser);
21967
21968 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21969 pm_node_t *write = UP(pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &operator, value));
21970
21971 if (context_def_p(parser)) {
21972 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21973 }
21974
21975 return parse_shareable_constant_write(parser, write);
21976 }
21977 case PM_INSTANCE_VARIABLE_READ_NODE: {
21978 parser_lex(parser);
21979
21980 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21981 pm_node_t *result = UP(pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &operator, value));
21982
21983 return result;
21984 }
21985 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21986 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21987 parser_lex(parser);
21988
21989 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21990 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &operator, value, name, 0));
21991
21992 pm_node_unreference(parser, node);
21993 return result;
21994 }
21995 case PM_LOCAL_VARIABLE_READ_NODE: {
21996 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21997 PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + node->location.start);
21998 pm_node_unreference(parser, node);
21999 }
22000
22002 parser_lex(parser);
22003
22004 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22005 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &operator, value, cast->name, cast->depth));
22006
22007 return result;
22008 }
22009 case PM_CALL_NODE: {
22010 pm_call_node_t *cast = (pm_call_node_t *) node;
22011
22012 // If we have a vcall (a method with no arguments and no
22013 // receiver that could have been a local variable) then we
22014 // will transform it into a local variable write.
22015 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22016 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
22017 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
22018 parser_lex(parser);
22019
22020 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22021 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &operator, value, constant_id, 0));
22022
22023 return result;
22024 }
22025
22026 // Move past the token here so that we have already added
22027 // the local variable by this point.
22028 parser_lex(parser);
22029
22030 // If there is no call operator and the message is "[]" then
22031 // this is an aref expression, and we can transform it into
22032 // an aset expression.
22033 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
22034 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22035 return UP(pm_index_and_write_node_create(parser, cast, &operator, value));
22036 }
22037
22038 // If this node cannot be writable, then we have an error.
22039 if (pm_call_node_writable_p(parser, cast)) {
22040 parse_write_name(parser, &cast->name);
22041 } else {
22042 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22043 }
22044
22045 parse_call_operator_write(parser, cast, &operator);
22046 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
22047 return UP(pm_call_and_write_node_create(parser, cast, &operator, value));
22048 }
22049 case PM_MULTI_WRITE_NODE: {
22050 parser_lex(parser);
22051
22052 pm_parser_err_token(parser, &operator, PM_ERR_AMPAMPEQ_MULTI_ASSIGN);
22053 return node;
22054 }
22055 default:
22056 parser_lex(parser);
22057
22058 // In this case we have an &&= sign, but we don't know what it's for.
22059 // We need to treat it as an error. For now, we'll mark it as an error
22060 // and just skip right past it.
22061 pm_parser_err_token(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
22062 return node;
22063 }
22064}
22065
22066static pm_node_t *
22067parse_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) {
22068 pm_token_t operator = parser->current;
22069
22070 switch (PM_NODE_TYPE(node)) {
22071 case PM_BACK_REFERENCE_READ_NODE:
22072 case PM_NUMBERED_REFERENCE_READ_NODE:
22073 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
22075 case PM_GLOBAL_VARIABLE_READ_NODE: {
22076 parser_lex(parser);
22077
22078 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22079 pm_node_t *result = UP(pm_global_variable_or_write_node_create(parser, node, &operator, value));
22080
22081 return result;
22082 }
22083 case PM_CLASS_VARIABLE_READ_NODE: {
22084 parser_lex(parser);
22085
22086 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22087 pm_node_t *result = UP(pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &operator, value));
22088
22089 return result;
22090 }
22091 case PM_CONSTANT_PATH_NODE: {
22092 parser_lex(parser);
22093
22094 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22095 pm_node_t *write = UP(pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &operator, value));
22096
22097 return parse_shareable_constant_write(parser, write);
22098 }
22099 case PM_CONSTANT_READ_NODE: {
22100 parser_lex(parser);
22101
22102 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22103 pm_node_t *write = UP(pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &operator, value));
22104
22105 if (context_def_p(parser)) {
22106 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
22107 }
22108
22109 return parse_shareable_constant_write(parser, write);
22110 }
22111 case PM_INSTANCE_VARIABLE_READ_NODE: {
22112 parser_lex(parser);
22113
22114 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22115 pm_node_t *result = UP(pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &operator, value));
22116
22117 return result;
22118 }
22119 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
22120 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
22121 parser_lex(parser);
22122
22123 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22124 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &operator, value, name, 0));
22125
22126 pm_node_unreference(parser, node);
22127 return result;
22128 }
22129 case PM_LOCAL_VARIABLE_READ_NODE: {
22130 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
22131 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
22132 pm_node_unreference(parser, node);
22133 }
22134
22136 parser_lex(parser);
22137
22138 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22139 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &operator, value, cast->name, cast->depth));
22140
22141 return result;
22142 }
22143 case PM_CALL_NODE: {
22144 pm_call_node_t *cast = (pm_call_node_t *) node;
22145
22146 // If we have a vcall (a method with no arguments and no
22147 // receiver that could have been a local variable) then we
22148 // will transform it into a local variable write.
22149 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22150 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
22151 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
22152 parser_lex(parser);
22153
22154 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22155 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &operator, value, constant_id, 0));
22156
22157 return result;
22158 }
22159
22160 // Move past the token here so that we have already added
22161 // the local variable by this point.
22162 parser_lex(parser);
22163
22164 // If there is no call operator and the message is "[]" then
22165 // this is an aref expression, and we can transform it into
22166 // an aset expression.
22167 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
22168 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22169 return UP(pm_index_or_write_node_create(parser, cast, &operator, value));
22170 }
22171
22172 // If this node cannot be writable, then we have an error.
22173 if (pm_call_node_writable_p(parser, cast)) {
22174 parse_write_name(parser, &cast->name);
22175 } else {
22176 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22177 }
22178
22179 parse_call_operator_write(parser, cast, &operator);
22180 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
22181 return UP(pm_call_or_write_node_create(parser, cast, &operator, value));
22182 }
22183 case PM_MULTI_WRITE_NODE: {
22184 parser_lex(parser);
22185 pm_parser_err_token(parser, &operator, PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN);
22186 return node;
22187 }
22188 default:
22189 parser_lex(parser);
22190
22191 // In this case we have an ||= sign, but we don't know what it's for.
22192 // We need to treat it as an error. For now, we'll mark it as an error
22193 // and just skip right past it.
22194 pm_parser_err_token(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
22195 return node;
22196 }
22197}
22198
22199static pm_node_t *
22200parse_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) {
22201 pm_token_t operator = parser->current;
22202
22203 switch (PM_NODE_TYPE(node)) {
22204 case PM_BACK_REFERENCE_READ_NODE:
22205 case PM_NUMBERED_REFERENCE_READ_NODE:
22206 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
22208 case PM_GLOBAL_VARIABLE_READ_NODE: {
22209 parser_lex(parser);
22210
22211 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22212 pm_node_t *result = UP(pm_global_variable_operator_write_node_create(parser, node, &operator, value));
22213
22214 return result;
22215 }
22216 case PM_CLASS_VARIABLE_READ_NODE: {
22217 parser_lex(parser);
22218
22219 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22220 pm_node_t *result = UP(pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &operator, value));
22221
22222 return result;
22223 }
22224 case PM_CONSTANT_PATH_NODE: {
22225 parser_lex(parser);
22226
22227 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22228 pm_node_t *write = UP(pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &operator, value));
22229
22230 return parse_shareable_constant_write(parser, write);
22231 }
22232 case PM_CONSTANT_READ_NODE: {
22233 parser_lex(parser);
22234
22235 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22236 pm_node_t *write = UP(pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &operator, value));
22237
22238 if (context_def_p(parser)) {
22239 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
22240 }
22241
22242 return parse_shareable_constant_write(parser, write);
22243 }
22244 case PM_INSTANCE_VARIABLE_READ_NODE: {
22245 parser_lex(parser);
22246
22247 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22248 pm_node_t *result = UP(pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &operator, value));
22249
22250 return result;
22251 }
22252 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
22253 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
22254 parser_lex(parser);
22255
22256 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22257 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &operator, value, name, 0));
22258
22259 pm_node_unreference(parser, node);
22260 return result;
22261 }
22262 case PM_LOCAL_VARIABLE_READ_NODE: {
22263 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
22264 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
22265 pm_node_unreference(parser, node);
22266 }
22267
22269 parser_lex(parser);
22270
22271 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22272 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &operator, value, cast->name, cast->depth));
22273
22274 return result;
22275 }
22276 case PM_CALL_NODE: {
22277 parser_lex(parser);
22278 pm_call_node_t *cast = (pm_call_node_t *) node;
22279
22280 // If we have a vcall (a method with no arguments and no
22281 // receiver that could have been a local variable) then we
22282 // will transform it into a local variable write.
22283 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
22284 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
22285 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
22286 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22287 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &operator, value, constant_id, 0));
22288
22289 return result;
22290 }
22291
22292 // If there is no call operator and the message is "[]" then
22293 // this is an aref expression, and we can transform it into
22294 // an aset expression.
22295 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
22296 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22297 return UP(pm_index_operator_write_node_create(parser, cast, &operator, value));
22298 }
22299
22300 // If this node cannot be writable, then we have an error.
22301 if (pm_call_node_writable_p(parser, cast)) {
22302 parse_write_name(parser, &cast->name);
22303 } else {
22304 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22305 }
22306
22307 parse_call_operator_write(parser, cast, &operator);
22308 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22309 return UP(pm_call_operator_write_node_create(parser, cast, &operator, value));
22310 }
22311 case PM_MULTI_WRITE_NODE: {
22312 parser_lex(parser);
22313 pm_parser_err_token(parser, &operator, PM_ERR_OPERATOR_MULTI_ASSIGN);
22314 return node;
22315 }
22316 default:
22317 parser_lex(parser);
22318
22319 // In this case we have an operator but we don't know what it's for.
22320 // We need to treat it as an error. For now, we'll mark it as an error
22321 // and just skip right past it.
22322 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_str(parser->current.type));
22323 return node;
22324 }
22325}
22326
22327static pm_node_t *
22328parse_call(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, uint8_t flags, uint16_t depth) {
22329 pm_token_t operator = parser->current;
22330 parser_lex(parser);
22331
22332 pm_arguments_t arguments = { 0 };
22333
22334 // This if statement handles the foo.() syntax.
22335 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
22336 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
22337 return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments));
22338 }
22339
22340 switch (PM_NODE_TYPE(node)) {
22341 case PM_RESCUE_MODIFIER_NODE: {
22343 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22344 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22345 }
22346 break;
22347 }
22348 case PM_AND_NODE: {
22349 pm_and_node_t *cast = (pm_and_node_t *) node;
22350 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22351 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22352 }
22353 break;
22354 }
22355 case PM_OR_NODE: {
22356 pm_or_node_t *cast = (pm_or_node_t *) node;
22357 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22358 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22359 }
22360 break;
22361 }
22362 default:
22363 break;
22364 }
22365
22366 pm_token_t message;
22367
22368 switch (parser->current.type) {
22369 case PM_CASE_OPERATOR:
22370 case PM_CASE_KEYWORD:
22371 case PM_TOKEN_CONSTANT:
22372 case PM_TOKEN_IDENTIFIER:
22373 case PM_TOKEN_METHOD_NAME: {
22374 parser_lex(parser);
22375 message = parser->previous;
22376 break;
22377 }
22378 default: {
22379 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_str(parser->current.type));
22380 message = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
22381 }
22382 }
22383
22384 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22385 pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments);
22386
22387 if (
22388 (previous_binding_power == PM_BINDING_POWER_STATEMENT) &&
22389 arguments.arguments == NULL &&
22390 arguments.opening_loc.length == 0 &&
22391 match1(parser, PM_TOKEN_COMMA)
22392 ) {
22393 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22394 } else {
22395 return UP(call);
22396 }
22397}
22398
22399static pm_node_t *
22400parse_aref(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t previous_binding_power, uint8_t flags, uint16_t depth) {
22401 parser_lex(parser);
22402
22403 pm_arguments_t arguments = { 0 };
22404 arguments.opening_loc = TOK2LOC(parser, &parser->previous);
22405
22406 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
22407 parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
22408 expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET);
22409 }
22410
22411 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
22412
22413 // If we have a comma after the closing bracket then this is a multiple
22414 // assignment and we should parse the targets.
22415 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22416 pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments);
22417 return parse_targets_validate(parser, UP(aref), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22418 }
22419
22420 // If we're at the end of the arguments, we can now check if there is a
22421 // block node that starts with a {. If there is, then we can parse it and
22422 // add it to the arguments.
22423 pm_block_node_t *block = NULL;
22424 if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
22425 block = parse_block(parser, (uint16_t) (depth + 1));
22426 pm_arguments_validate_block(parser, &arguments, block);
22427 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
22428 block = parse_block(parser, (uint16_t) (depth + 1));
22429 }
22430
22431 if (block != NULL) {
22432 if (arguments.block != NULL) {
22433 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_AFTER_BLOCK);
22434 if (arguments.arguments == NULL) {
22435 arguments.arguments = pm_arguments_node_create(parser);
22436 }
22437 pm_arguments_node_arguments_append(parser->arena, arguments.arguments, arguments.block);
22438 }
22439
22440 arguments.block = UP(block);
22441 }
22442
22443 return UP(pm_call_node_aref_create(parser, node, &arguments));
22444}
22445
22446static PRISM_INLINE pm_node_t *
22447parse_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) {
22448 pm_token_t token = parser->current;
22449
22450 switch (token.type) {
22451 case PM_TOKEN_EQUAL:
22452 return parse_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22453 case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL:
22454 return parse_and_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22455 case PM_TOKEN_PIPE_PIPE_EQUAL:
22456 return parse_or_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22457 case PM_TOKEN_AMPERSAND_EQUAL:
22458 case PM_TOKEN_CARET_EQUAL:
22459 case PM_TOKEN_GREATER_GREATER_EQUAL:
22460 case PM_TOKEN_LESS_LESS_EQUAL:
22461 case PM_TOKEN_MINUS_EQUAL:
22462 case PM_TOKEN_PERCENT_EQUAL:
22463 case PM_TOKEN_PIPE_EQUAL:
22464 case PM_TOKEN_PLUS_EQUAL:
22465 case PM_TOKEN_SLASH_EQUAL:
22466 case PM_TOKEN_STAR_EQUAL:
22467 case PM_TOKEN_STAR_STAR_EQUAL:
22468 return parse_operator_equal(parser, node, previous_binding_power, binding_power, flags, depth);
22469 case PM_TOKEN_AMPERSAND_AMPERSAND:
22470 case PM_TOKEN_KEYWORD_AND: {
22471 parser_lex(parser);
22472
22473 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));
22474 return UP(pm_and_node_create(parser, node, &token, right));
22475 }
22476 case PM_TOKEN_KEYWORD_OR:
22477 case PM_TOKEN_PIPE_PIPE: {
22478 parser_lex(parser);
22479
22480 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));
22481 return UP(pm_or_node_create(parser, node, &token, right));
22482 }
22483 case PM_TOKEN_EQUAL_TILDE:
22484 return parse_match(parser, node, binding_power, flags, depth);
22485 case PM_TOKEN_UAMPERSAND:
22486 case PM_TOKEN_USTAR:
22487 case PM_TOKEN_USTAR_STAR:
22488 // The only times this will occur are when we are in an error state,
22489 // but we'll put them in here so that errors can propagate.
22490 case PM_TOKEN_BANG_EQUAL:
22491 case PM_TOKEN_BANG_TILDE:
22492 case PM_TOKEN_EQUAL_EQUAL:
22493 case PM_TOKEN_EQUAL_EQUAL_EQUAL:
22494 case PM_TOKEN_LESS_EQUAL_GREATER:
22495 case PM_TOKEN_CARET:
22496 case PM_TOKEN_PIPE:
22497 case PM_TOKEN_AMPERSAND:
22498 case PM_TOKEN_GREATER_GREATER:
22499 case PM_TOKEN_LESS_LESS:
22500 case PM_TOKEN_MINUS:
22501 case PM_TOKEN_PLUS:
22502 case PM_TOKEN_PERCENT:
22503 case PM_TOKEN_SLASH:
22504 case PM_TOKEN_STAR:
22505 case PM_TOKEN_STAR_STAR: {
22506 parser_lex(parser);
22507 pm_token_t operator = parser->previous;
22508 switch (PM_NODE_TYPE(node)) {
22509 case PM_RESCUE_MODIFIER_NODE: {
22511 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22512 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22513 }
22514 break;
22515 }
22516 case PM_AND_NODE: {
22517 pm_and_node_t *cast = (pm_and_node_t *) node;
22518 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22519 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22520 }
22521 break;
22522 }
22523 case PM_OR_NODE: {
22524 pm_or_node_t *cast = (pm_or_node_t *) node;
22525 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22526 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22527 }
22528 break;
22529 }
22530 default:
22531 break;
22532 }
22533
22534 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));
22535 return UP(pm_call_node_binary_create(parser, node, &token, argument, 0));
22536 }
22537 case PM_TOKEN_GREATER:
22538 case PM_TOKEN_GREATER_EQUAL:
22539 case PM_TOKEN_LESS:
22540 case PM_TOKEN_LESS_EQUAL: {
22541 if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) {
22542 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON);
22543 }
22544
22545 parser_lex(parser);
22546 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));
22547 return UP(pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON));
22548 }
22549 case PM_TOKEN_AMPERSAND_DOT:
22550 case PM_TOKEN_DOT:
22551 return parse_call(parser, node, previous_binding_power, flags, depth);
22552 case PM_TOKEN_DOT_DOT:
22553 case PM_TOKEN_DOT_DOT_DOT: {
22554 parser_lex(parser);
22555
22556 pm_node_t *right = NULL;
22557 if (token_begins_expression_p(parser->current.type)) {
22558 right = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22559 }
22560
22561 return UP(pm_range_node_create(parser, node, &token, right));
22562 }
22563 case PM_TOKEN_KEYWORD_IF_MODIFIER: {
22564 pm_token_t keyword = parser->current;
22565 parser_lex(parser);
22566
22567 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));
22568 return UP(pm_if_node_modifier_create(parser, node, &keyword, predicate));
22569 }
22570 case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: {
22571 pm_token_t keyword = parser->current;
22572 parser_lex(parser);
22573
22574 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));
22575 return UP(pm_unless_node_modifier_create(parser, node, &keyword, predicate));
22576 }
22577 case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: {
22578 parser_lex(parser);
22579 pm_statements_node_t *statements = pm_statements_node_create(parser);
22580 pm_statements_node_body_append(parser, statements, node, true);
22581
22582 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));
22583 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));
22584 }
22585 case PM_TOKEN_KEYWORD_WHILE_MODIFIER: {
22586 parser_lex(parser);
22587 pm_statements_node_t *statements = pm_statements_node_create(parser);
22588 pm_statements_node_body_append(parser, statements, node, true);
22589
22590 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));
22591 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));
22592 }
22593 case PM_TOKEN_QUESTION_MARK:
22594 return UP(parse_ternary(parser, node, flags, depth));
22595 case PM_TOKEN_COLON_COLON:
22596 return parse_constant_path_infix(parser, node, previous_binding_power, flags, depth);
22597 case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: {
22598 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
22599 parser_lex(parser);
22600 accept1(parser, PM_TOKEN_NEWLINE);
22601
22602 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));
22603 context_pop(parser);
22604
22605 return UP(pm_rescue_modifier_node_create(parser, node, &token, value));
22606 }
22607 case PM_TOKEN_BRACKET_LEFT:
22608 return parse_aref(parser, node, previous_binding_power, flags, depth);
22609 case PM_TOKEN_KEYWORD_IN: {
22610 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22611 parser->pattern_matching_newlines = true;
22612
22613 pm_token_t operator = parser->current;
22614 parser->command_start = false;
22615 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22616 parser_lex(parser);
22617
22618 pm_constant_id_set_t captures = { 0 };
22619 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));
22620
22621 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22622
22623 return UP(pm_match_predicate_node_create(parser, node, pattern, &operator));
22624 }
22625 case PM_TOKEN_EQUAL_GREATER: {
22626 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22627 parser->pattern_matching_newlines = true;
22628
22629 pm_token_t operator = parser->current;
22630 parser->command_start = false;
22631 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22632 parser_lex(parser);
22633
22634 pm_constant_id_set_t captures = { 0 };
22635 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));
22636
22637 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22638
22639 return UP(pm_match_required_node_create(parser, node, pattern, &operator));
22640 }
22641 default:
22642 assert(false && "unreachable");
22643 return NULL;
22644 }
22645}
22646
22647#undef PM_PARSE_PATTERN_SINGLE
22648#undef PM_PARSE_PATTERN_TOP
22649#undef PM_PARSE_PATTERN_MULTI
22650
22663static bool
22664parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) {
22665 pm_binding_power_t left = pm_binding_powers[parser->current.type].left;
22666
22667 switch (PM_NODE_TYPE(node)) {
22668 case PM_MULTI_WRITE_NODE:
22669 case PM_RETURN_NODE:
22670 case PM_BREAK_NODE:
22671 case PM_NEXT_NODE:
22672 return left > PM_BINDING_POWER_MODIFIER;
22673 case PM_CLASS_VARIABLE_WRITE_NODE:
22674 case PM_CONSTANT_PATH_WRITE_NODE:
22675 case PM_CONSTANT_WRITE_NODE:
22676 case PM_GLOBAL_VARIABLE_WRITE_NODE:
22677 case PM_INSTANCE_VARIABLE_WRITE_NODE:
22678 case PM_LOCAL_VARIABLE_WRITE_NODE:
22679 return PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && left > PM_BINDING_POWER_MODIFIER;
22680 case PM_CALL_NODE: {
22681 // Calls with an implicit array on the right-hand side are
22682 // statements and can only be followed by modifiers.
22683 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)) {
22684 return left > PM_BINDING_POWER_MODIFIER;
22685 }
22686
22687 // Command-style calls (including block commands like
22688 // `foo bar do end`) can only be followed by composition
22689 // (and/or) and modifier (if/unless/etc.) operators.
22690 if (pm_command_call_value_p(parser, node)) {
22691 return left > PM_BINDING_POWER_COMPOSITION;
22692 }
22693
22694 // A block call (command with do-block, or any call chained
22695 // from one) can only be followed by call chaining (., ::,
22696 // &.), composition (and/or), and modifier operators.
22697 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22698 }
22699 case PM_SUPER_NODE:
22700 case PM_YIELD_NODE:
22701 // Command-style super/yield (without parens) can only be followed
22702 // by composition and modifier operators.
22703 if (pm_command_call_value_p(parser, node)) {
22704 return left > PM_BINDING_POWER_COMPOSITION;
22705 }
22706
22707 /* A super carrying a do-block is a block call, so it may also be
22708 * followed by call chaining (`.`, `::`, `&.`). */
22709 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22710 case PM_DEF_NODE:
22711 // An endless method whose body is a command-style call (e.g.,
22712 // `def f = foo bar`) is a command assignment and can only be
22713 // followed by modifiers.
22714 return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(parser, node);
22715 case PM_RESCUE_MODIFIER_NODE:
22716 // A rescue modifier whose handler is a pattern match (=> or in)
22717 // produces a statement and cannot be followed by operators above
22718 // the modifier level.
22719 if (left > PM_BINDING_POWER_MODIFIER) {
22721 pm_node_t *rescue_expression = cast->rescue_expression;
22722 return PM_NODE_TYPE_P(rescue_expression, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(rescue_expression, PM_MATCH_PREDICATE_NODE);
22723 }
22724 return false;
22725 default:
22726 return false;
22727 }
22728}
22729
22738static pm_node_t *
22739parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
22740 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
22741 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
22742 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
22743 }
22744
22745 pm_node_t *node = parse_expression_prefix(parser, binding_power, flags, diag_id, depth);
22746
22747 // Some prefix nodes are statements and can only be followed by modifiers
22748 // (if/unless/while/until/rescue) or nothing at all. We check these cheaply
22749 // here before entering the infix loop.
22750 switch (PM_NODE_TYPE(node)) {
22751 case PM_ERROR_RECOVERY_NODE:
22752 return node;
22753 case PM_PRE_EXECUTION_NODE:
22754 return node;
22755 case PM_POST_EXECUTION_NODE:
22756 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
22757 case PM_ALIAS_METHOD_NODE:
22758 case PM_UNDEF_NODE:
22759 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
22760 return node;
22761 }
22762 break;
22763 case PM_CALL_NODE:
22764 case PM_SUPER_NODE:
22765 case PM_YIELD_NODE:
22766 case PM_DEF_NODE:
22767 if (parse_expression_terminator(parser, node)) {
22768 return node;
22769 }
22770 break;
22771 case PM_SYMBOL_NODE:
22772 if (pm_symbol_node_label_p(parser, node)) {
22773 return node;
22774 }
22775 break;
22776 default:
22777 break;
22778 }
22779
22780 // Look and see if the next token can be parsed as an infix operator. If it
22781 // can, then we'll parse it using parse_expression_infix.
22782 pm_binding_powers_t current_binding_powers;
22783 pm_token_type_t current_token_type;
22784
22785 while (
22786 current_token_type = parser->current.type,
22787 current_binding_powers = pm_binding_powers[current_token_type],
22788 binding_power <= current_binding_powers.left &&
22789 current_binding_powers.binary
22790 ) {
22791 node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, flags, (uint16_t) (depth + 1));
22792 if (parse_expression_terminator(parser, node)) return node;
22793
22794 // If the operator is nonassoc and we should not be able to parse the
22795 // upcoming infix operator, break.
22796 if (current_binding_powers.nonassoc) {
22797 // If we are about to parse another non-associative operator at the
22798 // same precedence as the one we just parsed, then we need to add an
22799 // error. This covers chaining the same operator (`1 == 2 == 3`) as
22800 // well as different operators that share a precedence, since they
22801 // are equally non-associative with one another (`1 == 2 != 3`,
22802 // `1...2..3`).
22803 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22804 if (next_binding_powers.nonassoc && next_binding_powers.left == current_binding_powers.left) {
22805 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));
22806 break;
22807 }
22808
22809 // If this is an endless range, then we need to reject a couple of
22810 // additional operators because it violates the normal operator
22811 // precedence rules. Those patterns are:
22812 //
22813 // 1.. & 2
22814 // 1.. * 2
22815 //
22816 if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) {
22817 if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) {
22818 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));
22819 break;
22820 }
22821
22822 if (PM_BINDING_POWER_TERM <= next_binding_powers.left) {
22823 break;
22824 }
22825 } else if (current_binding_powers.left <= next_binding_powers.left) {
22826 break;
22827 }
22828 }
22829
22830 if (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) {
22831 // A command-style method call is only accepted on method chains.
22832 // Thus, we check whether the parsed node can continue method chains.
22833 // The method chain can continue if the parsed node is one of the following five kinds:
22834 // (1) index access: foo[1]
22835 // (2) attribute access: foo.bar
22836 // (3) method call with parenthesis: foo.bar(1)
22837 // (4) method call with a block: foo.bar do end
22838 // (5) constant path: foo::Bar
22839 switch (node->type) {
22840 case PM_CALL_NODE: {
22841 pm_call_node_t *cast = (pm_call_node_t *)node;
22842 if (
22843 // (1) foo[1]
22844 !(
22845 cast->call_operator_loc.length == 0 &&
22846 cast->message_loc.length > 0 &&
22847 parser->start[cast->message_loc.start] == '[' &&
22848 parser->start[cast->message_loc.start + cast->message_loc.length - 1] == ']'
22849 ) &&
22850 // (2) foo.bar
22851 !(
22852 cast->call_operator_loc.length > 0 &&
22853 cast->arguments == NULL &&
22854 cast->block == NULL &&
22855 cast->opening_loc.length == 0
22856 ) &&
22857 // (3) foo.bar(1)
22858 !(
22859 cast->call_operator_loc.length > 0 &&
22860 cast->opening_loc.length > 0
22861 ) &&
22862 // (4) foo.bar do end
22863 !(
22864 cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
22865 )
22866 ) {
22867 flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
22868 }
22869 break;
22870 }
22871 // (5) foo::Bar
22872 case PM_CONSTANT_PATH_NODE:
22873 break;
22874 default:
22875 flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
22876 break;
22877 }
22878 }
22879
22880 if (context_terminator(parser->current_context->context, &parser->current)) {
22881 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22882 if (
22883 !next_binding_powers.binary ||
22884 binding_power > next_binding_powers.left ||
22885 (PM_NODE_TYPE_P(node, PM_CALL_NODE) && pm_call_node_command_p((pm_call_node_t *) node))
22886 ) {
22887 return node;
22888 }
22889 }
22890 }
22891
22892 return node;
22893}
22894
22899static pm_statements_node_t *
22900wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
22901 if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) {
22902 if (statements == NULL) {
22903 statements = pm_statements_node_create(parser);
22904 }
22905
22906 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22907 pm_arguments_node_arguments_append(
22908 parser->arena,
22909 arguments,
22910 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)))
22911 );
22912
22913 pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create(
22914 parser,
22915 arguments,
22916 pm_parser_constant_id_constant(parser, "print", 5)
22917 )), true);
22918 }
22919
22920 if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) {
22921 if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) {
22922 if (statements == NULL) {
22923 statements = pm_statements_node_create(parser);
22924 }
22925
22926 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22927 pm_arguments_node_arguments_append(
22928 parser->arena,
22929 arguments,
22930 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)))
22931 );
22932
22933 pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2));
22934 pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, UP(receiver), "split", arguments);
22935
22936 pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create(
22937 parser,
22938 pm_parser_constant_id_constant(parser, "$F", 2),
22939 UP(call)
22940 );
22941
22942 pm_statements_node_body_prepend(parser->arena, statements, UP(write));
22943 }
22944
22945 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22946 pm_arguments_node_arguments_append(
22947 parser->arena,
22948 arguments,
22949 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)))
22950 );
22951
22952 if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) {
22953 pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser);
22954 pm_keyword_hash_node_elements_append(parser->arena, keywords, UP(pm_assoc_node_create(
22955 parser,
22956 UP(pm_symbol_node_synthesized_create(parser, "chomp")),
22957 NULL,
22958 UP(pm_true_node_synthesized_create(parser))
22959 )));
22960
22961 pm_arguments_node_arguments_append(parser->arena, arguments, UP(keywords));
22962 pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS);
22963 }
22964
22965 pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
22966 pm_statements_node_body_append(parser, wrapped_statements, UP(pm_while_node_synthesized_create(
22967 parser,
22968 UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))),
22969 statements
22970 )), true);
22971
22972 statements = wrapped_statements;
22973 }
22974
22975 return statements;
22976}
22977
22981static pm_node_t *
22982parse_program(pm_parser_t *parser) {
22983 // If the current scope is NULL, then we want to push a new top level scope.
22984 // The current scope could exist in the event that we are parsing an eval
22985 // and the user has passed into scopes that already exist.
22986 if (parser->current_scope == NULL) {
22987 pm_parser_scope_push(parser, true);
22988 }
22989
22990 pm_node_list_t current_block_exits = { 0 };
22991 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
22992
22993 parser_lex(parser);
22994 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN, 0);
22995
22996 if (statements != NULL && !parser->parsing_eval) {
22997 // If we have statements, then the top-level statement should be
22998 // explicitly checked as well. We have to do this here because
22999 // everywhere else we check all but the last statement.
23000 assert(statements->body.size > 0);
23001 pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]);
23002 }
23003
23004 pm_constant_id_list_t locals;
23005 pm_locals_order(parser, &parser->current_scope->locals, &locals, true);
23006 pm_parser_scope_pop(parser);
23007
23008 // At the top level, see if we need to wrap the statements in a program
23009 // node with a while loop based on the options.
23010 if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
23011 statements = wrap_statements(parser, statements);
23012 } else {
23013 flush_block_exits(parser, previous_block_exits);
23014 }
23015
23016 // If this is an empty file, then we're still going to parse all of the
23017 // statements in order to gather up all of the comments and such. Here we'll
23018 // correct the location information.
23019 if (statements == NULL) {
23020 statements = pm_statements_node_create(parser);
23021 statements->base.location = (pm_location_t) { 0 };
23022 }
23023
23024 return UP(pm_program_node_create(parser, &locals, statements));
23025}
23026
23027/******************************************************************************/
23028/* External functions */
23029/******************************************************************************/
23030
23040static const char *
23041pm_strnstr(const char *big, const char *little, size_t big_length) {
23042 size_t little_length = strlen(little);
23043
23044 for (const char *max = big + big_length - little_length; big <= max; big++) {
23045 if (*big == *little && memcmp(big, little, little_length) == 0) return big;
23046 }
23047
23048 return NULL;
23049}
23050
23051#ifdef _WIN32
23052#define pm_parser_warn_shebang_carriage_return(parser, start, length) ((void) 0)
23053#else
23059static void
23060pm_parser_warn_shebang_carriage_return(pm_parser_t *parser, const uint8_t *start, size_t length) {
23061 if (length > 2 && start[length - 2] == '\r' && start[length - 1] == '\n') {
23062 pm_parser_warn(parser, U32(start - parser->start), U32(length), PM_WARN_SHEBANG_CARRIAGE_RETURN);
23063 }
23064}
23065#endif
23066
23071static void
23072pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const char *engine, size_t length) {
23073 const char *switches = pm_strnstr(engine, " -", length);
23074 if (switches == NULL) return;
23075
23076 pm_options_t next_options = *options;
23077 options->shebang_callback(
23078 &next_options,
23079 (const uint8_t *) (switches + 1),
23080 length - ((size_t) (switches - engine)) - 1,
23081 options->shebang_callback_data
23082 );
23083
23084 size_t encoding_length;
23085 if ((encoding_length = pm_string_length(&next_options.encoding)) > 0) {
23086 const uint8_t *encoding_source = pm_string_source(&next_options.encoding);
23087 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
23088 }
23089
23090 parser->command_line = next_options.command_line;
23091 parser->frozen_string_literal = next_options.frozen_string_literal;
23092}
23093
23097void
23098pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) {
23099 assert(arena != NULL);
23100 assert(source != NULL);
23101
23102 *parser = (pm_parser_t) {
23103 .arena = arena,
23104 .metadata_arena = { 0 },
23105 .node_id = 0,
23106 .lex_state = PM_LEX_STATE_BEG,
23107 .enclosure_nesting = 0,
23108 .lambda_enclosure_nesting = -1,
23109 .brace_nesting = 0,
23110 .do_loop_stack = 0,
23111 .accepts_block_stack = 0,
23112 .lex_modes = {
23113 .index = 0,
23114 .stack = {{ .mode = PM_LEX_DEFAULT }},
23115 .current = &parser->lex_modes.stack[0],
23116 },
23117 .start = source,
23118 .end = source + size,
23119 .previous = { .type = PM_TOKEN_EOF, .start = source, .end = source },
23120 .current = { .type = PM_TOKEN_EOF, .start = source, .end = source },
23121 .next_start = NULL,
23122 .heredoc_end = NULL,
23123 .data_loc = { 0 },
23124 .comment_list = { 0 },
23125 .magic_comment_list = { 0 },
23126 .warning_list = { 0 },
23127 .error_list = { 0 },
23128 .current_scope = NULL,
23129 .current_context = NULL,
23130 .encoding = PM_ENCODING_UTF_8_ENTRY,
23131 .encoding_changed_callback = NULL,
23132 .encoding_comment_start = source,
23133 .lex_callback = { 0 },
23134 .filepath = PM_STRING_EMPTY,
23135 .constant_pool = { 0 },
23136 .line_offsets = { 0 },
23137 .integer = { 0 },
23138 .current_string = PM_STRING_EMPTY,
23139 .start_line = 1,
23140 .explicit_encoding = NULL,
23141 .command_line = 0,
23142 .parsing_eval = false,
23143 .partial_script = false,
23144 .attach_comments = false,
23145 .command_start = true,
23146 .recovering = false,
23147 .continuable = true,
23148 .encoding_locked = false,
23149 .encoding_changed = false,
23150 .pattern_matching_newlines = false,
23151 .in_keyword_arg = false,
23152 .current_block_exits = NULL,
23153 .semantic_token_seen = false,
23154 .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET,
23155 .warn_mismatched_indentation = true
23156 };
23157
23158 /* Pre-size the arenas based on input size to reduce the number of block
23159 * allocations (and the kernel page zeroing they trigger). The ratios were
23160 * measured empirically: AST arena ~3.3x input, metadata arena ~1.1x input.
23161 * The reserve call is a no-op when the capacity is at or below the default
23162 * arena block size, so small inputs don't waste an extra allocation. */
23163 if (size <= SIZE_MAX / 4) pm_arena_reserve(arena, size * 4);
23164 if (size <= SIZE_MAX / 5 * 4) pm_arena_reserve(&parser->metadata_arena, size + size / 4);
23165
23166 /* Initialize the constant pool. Measured across 1532 Ruby stdlib files, the
23167 * bytes/constant ratio has a median of ~56 and a 90th percentile of ~135.
23168 * We use 120 as a balance between over-allocation waste and resize
23169 * frequency. Resizes are cheap with arena allocation, so we lean toward
23170 * under-estimating. */
23171 uint32_t constant_size = ((uint32_t) size) / 120;
23172 pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size < 4 ? 4 : constant_size);
23173
23174 /* Initialize the line offset list. Similar to the constant pool, we are
23175 * going to estimate the number of newlines that we will need based on the
23176 * size of the input. */
23177 size_t newline_size = size / 22;
23178 pm_line_offset_list_init(&parser->metadata_arena, &parser->line_offsets, newline_size < 4 ? 4 : newline_size);
23179
23180 // If options were provided to this parse, establish them here.
23181 if (options != NULL) {
23182 // filepath option
23183 if (pm_string_length(&options->filepath) > 0) {
23184 parser->filepath = options->filepath;
23185 }
23186
23187 // line option
23188 parser->start_line = options->line;
23189
23190 // encoding option
23191 size_t encoding_length = pm_string_length(&options->encoding);
23192 if (encoding_length > 0) {
23193 const uint8_t *encoding_source = pm_string_source(&options->encoding);
23194 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
23195 }
23196
23197 // encoding_locked option
23198 parser->encoding_locked = options->encoding_locked;
23199
23200 // frozen_string_literal option
23201 parser->frozen_string_literal = options->frozen_string_literal;
23202
23203 // command_line option
23204 parser->command_line = options->command_line;
23205
23206 // version option
23207 parser->version = options->version;
23208
23209 // partial_script
23210 parser->partial_script = options->partial_script;
23211
23212 // attach_comments
23213 parser->attach_comments = options->attach_comments;
23214
23215 // scopes option
23216 parser->parsing_eval = options->scopes_count > 0;
23217 if (parser->parsing_eval) parser->warn_mismatched_indentation = false;
23218
23219 for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
23220 const pm_options_scope_t *scope = pm_options_scope(options, scope_index);
23221 pm_parser_scope_push(parser, scope_index == 0);
23222
23223 // Scopes given from the outside are not allowed to have numbered
23224 // parameters.
23225 parser->current_scope->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED;
23226
23227 for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
23228 const pm_string_t *local = pm_options_scope_local(scope, local_index);
23229
23230 const uint8_t *source = pm_string_source(local);
23231 size_t length = pm_string_length(local);
23232
23233 uint8_t *allocated = (uint8_t *) pm_arena_alloc(&parser->metadata_arena, length, 1);
23234 memcpy(allocated, source, length);
23235 pm_parser_local_add_owned(parser, allocated, length);
23236 }
23237 }
23238 }
23239
23240 // Now that we have established the user-provided options, check if
23241 // a version was given and parse as the latest version otherwise.
23242 if (parser->version == PM_OPTIONS_VERSION_UNSET) {
23243 parser->version = PM_OPTIONS_VERSION_LATEST;
23244 }
23245
23246 pm_accepts_block_stack_push(parser, true);
23247
23248 // Skip past the UTF-8 BOM if it exists.
23249 if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) {
23250 parser->current.end += 3;
23251 parser->encoding_comment_start += 3;
23252
23253 if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
23254 parser->encoding = PM_ENCODING_UTF_8_ENTRY;
23255 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
23256 }
23257 }
23258
23259 // If the -x command line flag is set, or the first shebang of the file does
23260 // not include "ruby", then we'll search for a shebang that does include
23261 // "ruby" and start parsing from there.
23262 bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
23263
23264 // If the first two bytes of the source are a shebang, then we will do a bit
23265 // of extra processing.
23266 //
23267 // First, we'll indicate that the encoding comment is at the end of the
23268 // shebang. This means that when a shebang is present the encoding comment
23269 // can begin on the second line.
23270 //
23271 // Second, we will check if the shebang includes "ruby". If it does, then we
23272 // we will start parsing from there. We will also potentially warning the
23273 // user if there is a carriage return at the end of the shebang. We will
23274 // also potentially call the shebang callback if this is the main script to
23275 // allow the caller to parse the shebang and find any command-line options.
23276 // If the shebang does not include "ruby" and this is the main script being
23277 // parsed, then we will start searching the file for a shebang that does
23278 // contain "ruby" as if -x were passed on the command line.
23279 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
23280 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end);
23281
23282 if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') {
23283 const char *engine;
23284
23285 if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) {
23286 if (newline != NULL) {
23287 parser->encoding_comment_start = newline + 1;
23288
23289 if (options == NULL || options->main_script) {
23290 pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1);
23291 }
23292 }
23293
23294 if (options != NULL && options->main_script && options->shebang_callback != NULL) {
23295 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start)));
23296 }
23297
23298 search_shebang = false;
23299 } else if (options != NULL && options->main_script && !parser->parsing_eval) {
23300 search_shebang = true;
23301 }
23302 }
23303
23304 // Here we're going to find the first shebang that includes "ruby" and start
23305 // parsing from there.
23306 if (search_shebang) {
23307 // If a shebang that includes "ruby" is not found, then we're going to a
23308 // a load error to the list of errors on the parser.
23309 bool found_shebang = false;
23310
23311 // This is going to point to the start of each line as we check it.
23312 // We'll maintain a moving window looking at each line at they come.
23313 const uint8_t *cursor = parser->start;
23314
23315 // The newline pointer points to the end of the current line that we're
23316 // considering. If it is NULL, then we're at the end of the file.
23317 const uint8_t *newline = next_newline(cursor, parser->end - cursor);
23318
23319 while (newline != NULL) {
23320 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
23321
23322 cursor = newline + 1;
23323 newline = next_newline(cursor, parser->end - cursor);
23324
23325 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - cursor);
23326 if (length > 2 && cursor[0] == '#' && cursor[1] == '!') {
23327 const char *engine;
23328 if ((engine = pm_strnstr((const char *) cursor, "ruby", length)) != NULL) {
23329 found_shebang = true;
23330
23331 if (newline != NULL) {
23332 pm_parser_warn_shebang_carriage_return(parser, cursor, length + 1);
23333 parser->encoding_comment_start = newline + 1;
23334 }
23335
23336 if (options != NULL && options->shebang_callback != NULL) {
23337 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) cursor)));
23338 }
23339
23340 break;
23341 }
23342 }
23343 }
23344
23345 if (found_shebang) {
23346 parser->previous = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23347 parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23348 } else {
23349 pm_parser_err(parser, 0, 0, PM_ERR_SCRIPT_NOT_FOUND);
23350 pm_line_offset_list_clear(&parser->line_offsets);
23351 }
23352 }
23353
23354 // The encoding comment can start after any amount of inline whitespace, so
23355 // here we'll advance it to the first non-inline-whitespace character so
23356 // that it is ready for future comparisons.
23357 parser->encoding_comment_start += pm_strspn_inline_whitespace(parser->encoding_comment_start, parser->end - parser->encoding_comment_start);
23358}
23359
23368pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) {
23369 pm_parser_t *parser = (pm_parser_t *) xmalloc(sizeof(pm_parser_t));
23370 if (parser == NULL) abort();
23371
23372 pm_parser_init(arena, parser, source, size, options);
23373 return parser;
23374}
23375
23379void
23380pm_parser_cleanup(pm_parser_t *parser) {
23381 pm_string_cleanup(&parser->filepath);
23382 pm_arena_cleanup(&parser->metadata_arena);
23383
23384 while (parser->current_scope != NULL) {
23385 // Normally, popping the scope doesn't free the locals since it is
23386 // assumed that ownership has transferred to the AST. However if we have
23387 // scopes while we're freeing the parser, it's likely they came from
23388 // eval scopes and we need to free them explicitly here.
23389 pm_parser_scope_pop(parser);
23390 }
23391
23392 while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
23393 lex_mode_pop(parser);
23394 }
23395}
23396
23400void
23402 pm_parser_cleanup(parser);
23403 xfree_sized(parser, sizeof(pm_parser_t));
23404}
23405
23411static bool
23412pm_parse_err_is_fatal(pm_diagnostic_id_t diag_id) {
23413 switch (diag_id) {
23414 case PM_ERR_ARRAY_EXPRESSION_AFTER_STAR:
23415 case PM_ERR_BEGIN_UPCASE_BRACE:
23416 case PM_ERR_CLASS_VARIABLE_BARE:
23417 case PM_ERR_END_UPCASE_BRACE:
23418 case PM_ERR_ESCAPE_INVALID_HEXADECIMAL:
23419 case PM_ERR_ESCAPE_INVALID_UNICODE_LIST:
23420 case PM_ERR_ESCAPE_INVALID_UNICODE_SHORT:
23421 case PM_ERR_EXPRESSION_NOT_WRITABLE:
23422 case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF:
23423 case PM_ERR_FLOAT_PARSE:
23424 case PM_ERR_GLOBAL_VARIABLE_BARE:
23425 case PM_ERR_HASH_KEY:
23426 case PM_ERR_HEREDOC_IDENTIFIER:
23427 case PM_ERR_INSTANCE_VARIABLE_BARE:
23428 case PM_ERR_INVALID_BLOCK_EXIT:
23429 case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT:
23430 case PM_ERR_INVALID_FLOAT_EXPONENT:
23431 case PM_ERR_INVALID_NUMBER_BINARY:
23432 case PM_ERR_INVALID_NUMBER_DECIMAL:
23433 case PM_ERR_INVALID_NUMBER_HEXADECIMAL:
23434 case PM_ERR_INVALID_NUMBER_OCTAL:
23435 case PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING:
23436 case PM_ERR_NO_LOCAL_VARIABLE:
23437 case PM_ERR_PARAMETER_ORDER:
23438 case PM_ERR_STATEMENT_UNDEF:
23439 case PM_ERR_VOID_EXPRESSION:
23440 return true;
23441 default:
23442 return false;
23443 }
23444}
23445
23479static void
23480pm_parse_continuable(pm_parser_t *parser) {
23481 // If there are no errors then there is nothing to continue.
23482 if (parser->error_list.size == 0) {
23483 parser->continuable = false;
23484 return;
23485 }
23486
23487 if (!parser->continuable) return;
23488
23489 size_t source_length = (size_t) (parser->end - parser->start);
23490
23491 // First pass: check if there are any non-stray, non-fatal errors.
23492 bool has_non_stray_error = false;
23493 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23494 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)) {
23495 has_non_stray_error = true;
23496 break;
23497 }
23498 }
23499
23500 // Second pass: check each error. We track the minimum source position
23501 // among non-stray, non-fatal errors seen so far in list order, which
23502 // lets us detect cascade stray tokens.
23503 size_t non_stray_min_start = SIZE_MAX;
23504
23505 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23506 size_t error_start = (size_t) error->location.start;
23507 size_t error_end = error_start + (size_t) error->location.length;
23508 bool at_eof = error_end >= source_length;
23509
23510 // Fatal errors are non-continuable unless they occur at EOF.
23511 if (pm_parse_err_is_fatal(error->diag_id) && !at_eof) {
23512 parser->continuable = false;
23513 return;
23514 }
23515
23516 // Track non-stray, non-fatal error positions in list order.
23517 if (error->diag_id != PM_ERR_UNEXPECTED_TOKEN_IGNORE &&
23518 error->diag_id != PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT) {
23519 if (error_start < non_stray_min_start) non_stray_min_start = error_start;
23520 continue;
23521 }
23522
23523 // This is a stray token. Determine if it is a cascade effect
23524 // of a preceding error or genuinely stray.
23525
23526 // Rule (a): a non-stray error was seen earlier in the list at a
23527 // strictly earlier position — this stray is a cascade effect.
23528 if (non_stray_min_start < error_start) continue;
23529
23530 // Rule (b): this stray is at EOF with valid code before it.
23531 // Single-byte stray tokens at EOF (like `\` for line continuation)
23532 // are likely truncated tokens. Multi-byte stray tokens (like the
23533 // keyword `end`) need additional evidence that they are cascade
23534 // effects (i.e. non-stray errors exist elsewhere).
23535 if (at_eof && error_start > 0) {
23536 // Exception: closing delimiters at EOF are genuinely stray.
23537 if (error->location.length == 1) {
23538 const uint8_t *byte = parser->start + error_start;
23539 if (*byte == ')' || *byte == ']' || *byte == '}') {
23540 parser->continuable = false;
23541 return;
23542 }
23543
23544 // Single-byte non-delimiter stray at EOF: cascade.
23545 continue;
23546 }
23547
23548 // Multi-byte stray at EOF: cascade only if there are
23549 // non-stray errors (evidence of a preceding parse failure).
23550 if (has_non_stray_error) continue;
23551 }
23552
23553 // Rule (c): a stray `=` at the start of a line could be the
23554 // beginning of an embedded document (`=begin`). The remaining
23555 // bytes after `=` parse as an identifier, so the error is not
23556 // at EOF, but the construct is genuinely incomplete.
23557 if (error->location.length == 1) {
23558 const uint8_t *byte = parser->start + error_start;
23559 if (*byte == '=' && (error_start == 0 || *(byte - 1) == '\n')) continue;
23560 }
23561
23562 // This stray token is genuinely non-continuable.
23563 parser->continuable = false;
23564 return;
23565 }
23566}
23567
23571pm_node_t *
23573 pm_node_t *node = parse_program(parser);
23574 pm_parse_continuable(parser);
23575 if (parser->attach_comments) pm_comments_attach(parser, node);
23576 return node;
23577}
23578
23585pm_node_t *
23586pm_parse_stream(pm_parser_t **parser, pm_arena_t *arena, pm_source_t *source, const pm_options_t *options) {
23587 bool eof = pm_source_stream_read(source);
23588
23589 pm_parser_t *tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23590 pm_node_t *node = pm_parse(tmp);
23591
23592 while (!eof && tmp->error_list.size > 0) {
23593 eof = pm_source_stream_read(source);
23594
23595 pm_parser_free(tmp);
23596 pm_arena_cleanup(arena);
23597
23598 tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23599 node = pm_parse(tmp);
23600 }
23601
23602 *parser = tmp;
23603 return node;
23604}
23605
23606#undef PM_CASE_KEYWORD
23607#undef PM_CASE_OPERATOR
23608#undef PM_CASE_WRITABLE
23609#undef PM_STRING_EMPTY
23610
23611// We optionally support serializing to a binary string. For systems that don't
23612// want or need this functionality, it can be turned off with the
23613// PRISM_EXCLUDE_SERIALIZATION define.
23614#ifndef PRISM_EXCLUDE_SERIALIZATION
23615
23616static PRISM_INLINE void
23617pm_serialize_header(pm_buffer_t *buffer) {
23618 pm_buffer_append_string(buffer, "PRISM", 5);
23619 pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR);
23620 pm_buffer_append_byte(buffer, PRISM_VERSION_MINOR);
23621 pm_buffer_append_byte(buffer, PRISM_VERSION_PATCH);
23622 pm_buffer_append_byte(buffer, PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS ? 1 : 0);
23623}
23624
23628void
23629pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
23630 pm_serialize_header(buffer);
23631 pm_serialize_content(parser, node, buffer);
23632 pm_buffer_append_byte(buffer, '\0');
23633}
23634
23639void
23640pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23641 pm_options_t options = { 0 };
23642 pm_options_read(&options, data);
23643
23644 pm_arena_t arena = { 0 };
23645 pm_parser_t parser;
23646 pm_parser_init(&arena, &parser, source, size, &options);
23647
23648 pm_node_t *node = pm_parse(&parser);
23649
23650 pm_serialize_header(buffer);
23651 pm_serialize_content(&parser, node, buffer);
23652 pm_buffer_append_byte(buffer, '\0');
23653
23654 pm_parser_cleanup(&parser);
23655 pm_arena_cleanup(&arena);
23656 pm_options_cleanup(&options);
23657}
23658
23663void
23664pm_serialize_parse_stream(pm_buffer_t *buffer, pm_source_t *source, const char *data) {
23665 pm_arena_t arena = { 0 };
23666 pm_parser_t *parser;
23667 pm_options_t options = { 0 };
23668 pm_options_read(&options, data);
23669
23670 pm_node_t *node = pm_parse_stream(&parser, &arena, source, &options);
23671 pm_serialize_header(buffer);
23672 pm_serialize_content(parser, node, buffer);
23673 pm_buffer_append_byte(buffer, '\0');
23674
23675 pm_parser_free(parser);
23676 pm_arena_cleanup(&arena);
23677 pm_options_cleanup(&options);
23678}
23679
23688int8_t
23689pm_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) {
23690 pm_options_t options = { 0 };
23691 pm_options_read(&options, data);
23692
23693 pm_arena_t arena = { 0 };
23694 pm_parser_t parser;
23695 pm_parser_init(&arena, &parser, source, size, &options);
23696
23697 pm_parse(&parser);
23698
23699 int8_t result = -1;
23700 if (parser.error_list.size > 0) {
23701 const char *encoding_name = parser.encoding->name;
23702 pm_buffer_append_string(buffer, encoding_name, strlen(encoding_name));
23703 pm_buffer_append_byte(buffer, '\0');
23704
23705 result = (int8_t) pm_errors_format(&parser, buffer, format_type);
23706 }
23707
23708 pm_parser_cleanup(&parser);
23709 pm_arena_cleanup(&arena);
23710 pm_options_cleanup(&options);
23711
23712 return result;
23713}
23714
23718void
23719pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23720 pm_options_t options = { 0 };
23721 pm_options_read(&options, data);
23722
23723 pm_arena_t arena = { 0 };
23724 pm_parser_t parser;
23725 pm_parser_init(&arena, &parser, source, size, &options);
23726
23727 pm_parse(&parser);
23728 pm_serialize_header(buffer);
23729 pm_serialize_encoding(parser.encoding, buffer);
23730 pm_buffer_append_varsint(buffer, parser.start_line);
23731 pm_serialize_line_offset_list(&parser.line_offsets, buffer);
23732 pm_serialize_comment_list(&parser.comment_list, buffer);
23733
23734 pm_parser_cleanup(&parser);
23735 pm_arena_cleanup(&arena);
23736 pm_options_cleanup(&options);
23737}
23738
23739#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
#define xcalloc
Old name of ruby_xcalloc.
Definition xmalloc.h:55
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:23368
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:23401
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:23572
#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:1762
pm_node_t * block
The optional block attached to the call.
Definition prism.c:1773
bool has_forwarding
The flag indicating whether this arguments list has forwarding argument.
Definition prism.c:1776
pm_location_t opening_loc
The optional location of the opening parenthesis or bracket.
Definition prism.c:1764
pm_arguments_node_t * arguments
The lazily-allocated optional arguments node.
Definition prism.c:1767
pm_location_t closing_loc
The optional location of the closing parenthesis or bracket.
Definition prism.c:1770
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:12651
bool binary
Whether or not this token can be used as a binary operator.
Definition prism.c:12659
pm_binding_power_t left
The left binding power.
Definition prism.c:12653
bool nonassoc
Whether or not this token can be used as non-associative binary operator.
Definition prism.c:12665
pm_binding_power_t right
The right binding power.
Definition prism.c:12656
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
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:21546
bool shared
Whether the content being parsed points into the original source.
Definition prism.c:21561
pm_constant_id_set_t names
The set of capture names found so far.
Definition prism.c:21554
pm_call_node_t * call
The call node wrapping the regular expression node (for =~).
Definition prism.c:21548
pm_match_write_node_t * match
The match write node being built, or NULL if no captures found yet.
Definition prism.c:21551
In order to properly set a regular expression's encoding and to validate the byte sequence for the un...
Definition prism.c:9834
pm_buffer_t regexp_buffer
The buffer holding the regexp source.
Definition prism.c:9839
pm_token_buffer_t base
The embedded base buffer.
Definition prism.c:9836
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:9808
pm_buffer_t buffer
The buffer that we're using to keep track of the string content.
Definition prism.c:9813
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:9819
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