Ruby 4.1.0dev (2026-09-22 revision 1f717ae3d6ca76015582dc610e892be1aacb905b)
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_LOCATION_START(location_) ((location_)->start)
117#define PM_LOCATION_END(location_) ((location_)->start + (location_)->length)
118
119#define PM_TOKEN_START(parser_, token_) U32((token_)->start - (parser_)->start)
120#define PM_TOKEN_END(parser_, token_) U32((token_)->end - (parser_)->start)
121#define PM_TOKEN_LENGTH(token_) U32((token_)->end - (token_)->start)
122#define PM_TOKENS_LENGTH(left_, right_) U32((right_)->end - (left_)->start)
123
124#define PM_NODE_START(node_) (UP(node_)->location.start)
125#define PM_NODE_LENGTH(node_) (UP(node_)->location.length)
126#define PM_NODE_END(node_) (UP(node_)->location.start + UP(node_)->location.length)
127#define PM_NODES_LENGTH(left_, right_) (PM_NODE_END(right_) - PM_NODE_START(left_))
128
129#define PM_TOKEN_NODE_LENGTH(parser_, token_, node_) (PM_NODE_END(node_) - PM_TOKEN_START(parser_, token_))
130#define PM_NODE_TOKEN_LENGTH(parser_, node_, token_) (PM_TOKEN_END(parser_, token_) - PM_NODE_START(node_))
131
132#define PM_NODE_START_SET_NODE(left_, right_) (PM_NODE_START(left_) = PM_NODE_START(right_))
133#define PM_NODE_START_SET_TOKEN(parser_, node_, token_) (PM_NODE_START(node_) = PM_TOKEN_START(parser_, token_))
134#define PM_NODE_LENGTH_SET_NODE(left_, right_) (PM_NODE_LENGTH(left_) = PM_NODE_END(right_) - PM_NODE_START(left_))
135#define PM_NODE_LENGTH_SET_TOKEN(parser_, node_, token_) (PM_NODE_LENGTH(node_) = PM_TOKEN_END(parser_, token_) - PM_NODE_START(node_))
136#define PM_NODE_LENGTH_SET_LOCATION(node_, location_) (PM_NODE_LENGTH(node_) = PM_LOCATION_END(location_) - PM_NODE_START(node_))
137
146pm_location_init(uint32_t start, uint32_t length) {
147 pm_location_t location = { .start = start, .length = length };
148 return location;
149}
150
151#define PM_LOCATION_INIT(start_, length_) pm_location_init((start_), (length_))
152#define PM_LOCATION_INIT_UNSET PM_LOCATION_INIT(0, 0)
153#define PM_LOCATION_INIT_TOKEN(parser_, token_) PM_LOCATION_INIT(PM_TOKEN_START(parser_, token_), PM_TOKEN_LENGTH(token_))
154#define PM_LOCATION_INIT_NODE(node_) UP(node_)->location
155
156#define PM_LOCATION_INIT_TOKENS(parser_, left_, right_) PM_LOCATION_INIT(PM_TOKEN_START(parser_, left_), PM_TOKENS_LENGTH(left_, right_))
157#define PM_LOCATION_INIT_NODES(left_, right_) PM_LOCATION_INIT(PM_NODE_START(left_), PM_NODES_LENGTH(left_, right_))
158#define PM_LOCATION_INIT_TOKEN_NODE(parser_, token_, node_) PM_LOCATION_INIT(PM_TOKEN_START(parser_, token_), PM_TOKEN_NODE_LENGTH(parser_, token_, node_))
159#define PM_LOCATION_INIT_NODE_TOKEN(parser_, node_, token_) PM_LOCATION_INIT(PM_NODE_START(node_), PM_NODE_TOKEN_LENGTH(parser_, node_, token_))
160
161#define TOK2LOC(parser_, token_) PM_LOCATION_INIT_TOKEN(parser_, token_)
162#define NTOK2LOC(parser_, token_) ((token_) == NULL ? PM_LOCATION_INIT_UNSET : TOK2LOC(parser_, token_))
163#define NTOK2PTR(token_) ((token_).start == NULL ? NULL : &(token_))
164
165/******************************************************************************/
166/* Lex mode manipulations */
167/******************************************************************************/
168
173static PRISM_INLINE uint8_t
174lex_mode_incrementor(const uint8_t start) {
175 switch (start) {
176 case '(':
177 case '[':
178 case '{':
179 case '<':
180 return start;
181 default:
182 return '\0';
183 }
184}
185
190static PRISM_INLINE uint8_t
191lex_mode_terminator(const uint8_t start) {
192 switch (start) {
193 case '(':
194 return ')';
195 case '[':
196 return ']';
197 case '{':
198 return '}';
199 case '<':
200 return '>';
201 default:
202 return start;
203 }
204}
205
211static bool
212lex_mode_push(pm_parser_t *parser, pm_lex_mode_t lex_mode) {
213 lex_mode.prev = parser->lex_modes.current;
214 parser->lex_modes.index++;
215
216 if (parser->lex_modes.index > PM_LEX_STACK_SIZE - 1) {
217 parser->lex_modes.current = (pm_lex_mode_t *) xmalloc(sizeof(pm_lex_mode_t));
218 if (parser->lex_modes.current == NULL) return false;
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 return true;
227}
228
232static PRISM_INLINE bool
233lex_mode_push_list(pm_parser_t *parser, bool interpolation, uint8_t delimiter) {
234 uint8_t incrementor = lex_mode_incrementor(delimiter);
235 uint8_t terminator = lex_mode_terminator(delimiter);
236
237 pm_lex_mode_t lex_mode = {
238 .mode = PM_LEX_LIST,
239 .as.list = {
240 .nesting = 0,
241 .interpolation = interpolation,
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 return lex_mode_push(parser, lex_mode);
273}
274
280static PRISM_INLINE bool
281lex_mode_push_list_eof(pm_parser_t *parser) {
282 return lex_mode_push_list(parser, false, '\0');
283}
284
288static PRISM_INLINE bool
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 return lex_mode_push(parser, lex_mode);
319}
320
324static PRISM_INLINE bool
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 return lex_mode_push(parser, lex_mode);
364}
365
371static PRISM_INLINE bool
372lex_mode_push_string_eof(pm_parser_t *parser) {
373 return 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 uint32_t
891pm_locals_hash(pm_constant_id_t name) {
892 name = ((name >> 16) ^ name) * 0x45d9f3b;
893 name = ((name >> 16) ^ name) * 0x45d9f3b;
894 name = (name >> 16) ^ name;
895 return name;
896}
897
902static void
903pm_locals_resize(pm_locals_t *locals) {
904 uint32_t next_capacity = locals->capacity == 0 ? 4 : (locals->capacity * 2);
905 assert(next_capacity > locals->capacity);
906
907 pm_local_t *next_locals = xcalloc(next_capacity, sizeof(pm_local_t));
908 if (next_locals == NULL) abort();
909
910 if (next_capacity < PM_LOCALS_HASH_THRESHOLD) {
911 if (locals->size > 0) {
912 memcpy(next_locals, locals->locals, locals->size * sizeof(pm_local_t));
913 }
914 } else {
915 // If we just switched from a list to a hash, then we need to fill in
916 // the hash values of all of the locals.
917 bool hash_needed = (locals->capacity <= PM_LOCALS_HASH_THRESHOLD);
918 uint32_t mask = next_capacity - 1;
919
920 for (uint32_t index = 0; index < locals->capacity; index++) {
921 pm_local_t *local = &locals->locals[index];
922
923 if (local->name != PM_CONSTANT_ID_UNSET) {
924 if (hash_needed) local->hash = pm_locals_hash(local->name);
925
926 uint32_t hash = local->hash;
927 while (next_locals[hash & mask].name != PM_CONSTANT_ID_UNSET) hash++;
928 next_locals[hash & mask] = *local;
929 }
930 }
931 }
932
933 pm_locals_free(locals);
934 locals->locals = next_locals;
935 locals->capacity = next_capacity;
936}
937
953static bool
954pm_locals_write(pm_locals_t *locals, pm_constant_id_t name, uint32_t start, uint32_t length, uint32_t reads) {
955 if (locals->size >= (locals->capacity / 4 * 3)) {
956 pm_locals_resize(locals);
957 }
958
959 locals->bloom |= (1u << (name & 31));
960
961 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
962 for (uint32_t index = 0; index < locals->capacity; index++) {
963 pm_local_t *local = &locals->locals[index];
964
965 if (local->name == PM_CONSTANT_ID_UNSET) {
966 *local = (pm_local_t) {
967 .name = name,
968 .location = { .start = start, .length = length },
969 .index = locals->size++,
970 .reads = reads,
971 .hash = 0
972 };
973 return true;
974 } else if (local->name == name) {
975 return false;
976 }
977 }
978 } else {
979 uint32_t mask = locals->capacity - 1;
980 uint32_t hash = pm_locals_hash(name);
981 uint32_t initial_hash = hash;
982
983 do {
984 pm_local_t *local = &locals->locals[hash & mask];
985
986 if (local->name == PM_CONSTANT_ID_UNSET) {
987 *local = (pm_local_t) {
988 .name = name,
989 .location = { .start = start, .length = length },
990 .index = locals->size++,
991 .reads = reads,
992 .hash = initial_hash
993 };
994 return true;
995 } else if (local->name == name) {
996 return false;
997 } else {
998 hash++;
999 }
1000 } while ((hash & mask) != initial_hash);
1001 }
1002
1003 assert(false && "unreachable");
1004 return true;
1005}
1006
1011static uint32_t
1012pm_locals_find(pm_locals_t *locals, pm_constant_id_t name) {
1013 if (!(locals->bloom & (1u << (name & 31)))) return UINT32_MAX;
1014
1015 if (locals->capacity < PM_LOCALS_HASH_THRESHOLD) {
1016 for (uint32_t index = 0; index < locals->size; index++) {
1017 pm_local_t *local = &locals->locals[index];
1018 if (local->name == name) return index;
1019 }
1020 } else {
1021 uint32_t mask = locals->capacity - 1;
1022 uint32_t hash = pm_locals_hash(name);
1023 uint32_t initial_hash = hash & mask;
1024
1025 do {
1026 pm_local_t *local = &locals->locals[hash & mask];
1027
1028 if (local->name == PM_CONSTANT_ID_UNSET) {
1029 return UINT32_MAX;
1030 } else if (local->name == name) {
1031 return hash & mask;
1032 } else {
1033 hash++;
1034 }
1035 } while ((hash & mask) != initial_hash);
1036 }
1037
1038 return UINT32_MAX;
1039}
1040
1045static void
1046pm_locals_read(pm_locals_t *locals, pm_constant_id_t name) {
1047 uint32_t index = pm_locals_find(locals, name);
1048 assert(index != UINT32_MAX);
1049
1050 pm_local_t *local = &locals->locals[index];
1051 assert(local->reads < UINT32_MAX);
1052
1053 local->reads++;
1054}
1055
1060static void
1061pm_locals_unread(pm_locals_t *locals, pm_constant_id_t name) {
1062 uint32_t index = pm_locals_find(locals, name);
1063 assert(index != UINT32_MAX);
1064
1065 pm_local_t *local = &locals->locals[index];
1066 assert(local->reads > 0);
1067
1068 local->reads--;
1069}
1070
1074static uint32_t
1075pm_locals_reads(pm_locals_t *locals, pm_constant_id_t name) {
1076 uint32_t index = pm_locals_find(locals, name);
1077 assert(index != UINT32_MAX);
1078
1079 return locals->locals[index].reads;
1080}
1081
1090static void
1091pm_locals_order(pm_parser_t *parser, pm_locals_t *locals, pm_constant_id_list_t *list, bool toplevel) {
1092 pm_constant_id_list_init_capacity(parser->arena, list, locals->size);
1093
1094 // If we're still below the threshold for switching to a hash, then we only
1095 // need to loop over the locals until we hit the size because the locals are
1096 // stored in a list.
1097 uint32_t capacity = locals->capacity < PM_LOCALS_HASH_THRESHOLD ? locals->size : locals->capacity;
1098
1099 // We will only warn for unused variables if we're not at the top level, or
1100 // if we're parsing a file outside of eval or -e.
1101 bool warn_unused = !toplevel || (!parser->parsing_eval && !PM_PARSER_COMMAND_LINE_OPTION_E(parser));
1102
1103 for (uint32_t index = 0; index < capacity; index++) {
1104 pm_local_t *local = &locals->locals[index];
1105
1106 if (local->name != PM_CONSTANT_ID_UNSET) {
1107 pm_constant_id_list_insert(list, (size_t) local->index, local->name);
1108
1109 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))) {
1110 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, local->name);
1111
1112 if (constant->length >= 1 && *constant->start != '_') {
1113 PM_PARSER_WARN_FORMAT(
1114 parser,
1115 local->location.start,
1116 local->location.length,
1117 PM_WARN_UNUSED_LOCAL_VARIABLE,
1118 (int) constant->length,
1119 (const char *) constant->start
1120 );
1121 }
1122 }
1123 }
1124 }
1125}
1126
1127/******************************************************************************/
1128/* Node-related functions */
1129/******************************************************************************/
1130
1135pm_parser_constant_id_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
1136 /* Fast path: if this is the same token as the last lookup (same pointer
1137 * range), return the cached result. */
1138 if (start == parser->constant_cache.start && end == parser->constant_cache.end) {
1139 return parser->constant_cache.id;
1140 }
1141
1142 pm_constant_id_t id = pm_constant_pool_insert_shared(&parser->metadata_arena, &parser->constant_pool, start, (size_t) (end - start));
1143
1144 parser->constant_cache.start = start;
1145 parser->constant_cache.end = end;
1146 parser->constant_cache.id = id;
1147
1148 return id;
1149}
1150
1155pm_parser_constant_id_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
1156 return pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, start, length);
1157}
1158
1163pm_parser_constant_id_constant(pm_parser_t *parser, const char *start, size_t length) {
1164 return pm_constant_pool_insert_constant(&parser->metadata_arena, &parser->constant_pool, (const uint8_t *) start, length);
1165}
1166
1171pm_parser_constant_id_token(pm_parser_t *parser, const pm_token_t *token) {
1172 return pm_parser_constant_id_raw(parser, token->start, token->end);
1173}
1174
1179#define PM_CASE_VOID_VALUE PM_RETURN_NODE: case PM_BREAK_NODE: case PM_NEXT_NODE: \
1180 case PM_REDO_NODE: case PM_RETRY_NODE: case PM_MATCH_REQUIRED_NODE
1181
1187static pm_node_t *
1188pm_check_value_expression(pm_parser_t *parser, pm_node_t *node) {
1189 pm_node_t *void_node = NULL;
1190
1191 while (node != NULL) {
1192 switch (PM_NODE_TYPE(node)) {
1193 case PM_CASE_VOID_VALUE:
1194 return void_node != NULL ? void_node : node;
1195 case PM_MATCH_PREDICATE_NODE:
1196 return NULL;
1197 case PM_BEGIN_NODE: {
1198 pm_begin_node_t *cast = (pm_begin_node_t *) node;
1199
1200 if (cast->ensure_clause != NULL) {
1201 if (cast->rescue_clause != NULL) {
1202 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->rescue_clause));
1203 if (vn != NULL) return vn;
1204 }
1205
1206 if (cast->statements != NULL) {
1207 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1208 if (vn != NULL) return vn;
1209 }
1210
1211 node = UP(cast->ensure_clause);
1212 } else if (cast->rescue_clause != NULL) {
1213 // https://bugs.ruby-lang.org/issues/21669
1214 if (cast->else_clause == NULL || parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1215 if (cast->statements == NULL) return NULL;
1216
1217 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1218 if (vn == NULL) return NULL;
1219 if (void_node == NULL) void_node = vn;
1220 }
1221
1222 for (pm_rescue_node_t *rescue_clause = cast->rescue_clause; rescue_clause != NULL; rescue_clause = rescue_clause->subsequent) {
1223 pm_node_t *vn = pm_check_value_expression(parser, UP(rescue_clause->statements));
1224
1225 if (vn == NULL) {
1226 // https://bugs.ruby-lang.org/issues/21669
1227 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1228 return NULL;
1229 }
1230 void_node = NULL;
1231 break;
1232 }
1233 }
1234
1235 if (cast->else_clause != NULL) {
1236 node = UP(cast->else_clause);
1237
1238 // https://bugs.ruby-lang.org/issues/21669
1239 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1240 pm_node_t *vn = pm_check_value_expression(parser, node);
1241 if (vn != NULL) return vn;
1242 }
1243 } else {
1244 return void_node;
1245 }
1246 } else {
1247 node = UP(cast->statements);
1248 }
1249
1250 break;
1251 }
1252 case PM_CASE_NODE: {
1253 // https://bugs.ruby-lang.org/issues/21669
1254 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1255 return NULL;
1256 }
1257
1258 pm_case_node_t *cast = (pm_case_node_t *) node;
1259 if (cast->else_clause == NULL) return NULL;
1260
1261 pm_node_t *condition;
1262 PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1263 assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
1264
1265 pm_when_node_t *cast = (pm_when_node_t *) condition;
1266 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1267 if (vn == NULL) return NULL;
1268 if (void_node == NULL) void_node = vn;
1269 }
1270
1271 node = UP(cast->else_clause);
1272 break;
1273 }
1274 case PM_CASE_MATCH_NODE: {
1275 // https://bugs.ruby-lang.org/issues/21669
1276 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
1277 return NULL;
1278 }
1279
1281 if (cast->else_clause == NULL) return NULL;
1282
1283 pm_node_t *condition;
1284 PM_NODE_LIST_FOREACH(&cast->conditions, index, condition) {
1285 assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
1286
1287 pm_in_node_t *cast = (pm_in_node_t *) condition;
1288 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1289 if (vn == NULL) return NULL;
1290 if (void_node == NULL) void_node = vn;
1291 }
1292
1293 node = UP(cast->else_clause);
1294 break;
1295 }
1296 case PM_ENSURE_NODE: {
1297 pm_ensure_node_t *cast = (pm_ensure_node_t *) node;
1298 node = UP(cast->statements);
1299 break;
1300 }
1301 case PM_PARENTHESES_NODE: {
1303 node = UP(cast->body);
1304 break;
1305 }
1306 case PM_STATEMENTS_NODE: {
1308
1309 // https://bugs.ruby-lang.org/issues/21669
1310 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1) {
1311 pm_node_t *body_part;
1312 PM_NODE_LIST_FOREACH(&cast->body, index, body_part) {
1313 switch (PM_NODE_TYPE(body_part)) {
1314 case PM_CASE_VOID_VALUE:
1315 if (void_node == NULL) {
1316 void_node = body_part;
1317 }
1318 return void_node;
1319 default: break;
1320 }
1321 }
1322 }
1323
1324 node = cast->body.nodes[cast->body.size - 1];
1325 break;
1326 }
1327 case PM_IF_NODE: {
1328 pm_if_node_t *cast = (pm_if_node_t *) node;
1329 if (cast->statements == NULL || cast->subsequent == NULL) {
1330 return NULL;
1331 }
1332 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1333 if (vn == NULL) {
1334 return NULL;
1335 }
1336 if (void_node == NULL) {
1337 void_node = vn;
1338 }
1339 node = cast->subsequent;
1340 break;
1341 }
1342 case PM_UNLESS_NODE: {
1343 pm_unless_node_t *cast = (pm_unless_node_t *) node;
1344 if (cast->statements == NULL || cast->else_clause == NULL) {
1345 return NULL;
1346 }
1347 pm_node_t *vn = pm_check_value_expression(parser, UP(cast->statements));
1348 if (vn == NULL) {
1349 return NULL;
1350 }
1351 if (void_node == NULL) {
1352 void_node = vn;
1353 }
1354 node = UP(cast->else_clause);
1355 break;
1356 }
1357 case PM_ELSE_NODE: {
1358 pm_else_node_t *cast = (pm_else_node_t *) node;
1359 node = UP(cast->statements);
1360 break;
1361 }
1362 case PM_AND_NODE:
1363 case PM_OR_NODE:
1364 // The left operand of an and/or node was already checked for a
1365 // value when the node was created, so descending into it again
1366 // would re-report the same void value and, in a chain such as
1367 // `a && a && ...`, walk the whole left branch on every operator,
1368 // which is quadratic in the length of the chain.
1369 return NULL;
1370 case PM_LOCAL_VARIABLE_WRITE_NODE: {
1372
1373 pm_scope_t *scope = parser->current_scope;
1374 for (uint32_t depth = 0; depth < cast->depth; depth++) scope = scope->previous;
1375
1376 pm_locals_read(&scope->locals, cast->name);
1377 return NULL;
1378 }
1379 default:
1380 return NULL;
1381 }
1382 }
1383
1384 return NULL;
1385}
1386
1387static PRISM_INLINE void
1388pm_assert_value_expression(pm_parser_t *parser, pm_node_t *node) {
1389 pm_node_t *void_node = pm_check_value_expression(parser, node);
1390 if (void_node != NULL) {
1391 pm_parser_err_node(parser, void_node, PM_ERR_VOID_EXPRESSION);
1392 }
1393}
1394
1398static void
1399pm_void_statement_check(pm_parser_t *parser, const pm_node_t *node) {
1400 const char *type = NULL;
1401 int length = 0;
1402
1403 switch (PM_NODE_TYPE(node)) {
1404 case PM_BACK_REFERENCE_READ_NODE:
1405 case PM_CLASS_VARIABLE_READ_NODE:
1406 case PM_GLOBAL_VARIABLE_READ_NODE:
1407 case PM_INSTANCE_VARIABLE_READ_NODE:
1408 case PM_LOCAL_VARIABLE_READ_NODE:
1409 case PM_NUMBERED_REFERENCE_READ_NODE:
1410 type = "a variable";
1411 length = 10;
1412 break;
1413 case PM_CALL_NODE: {
1414 const pm_call_node_t *cast = (const pm_call_node_t *) node;
1415 if (cast->call_operator_loc.length > 0 || cast->message_loc.length == 0) break;
1416
1417 const pm_constant_t *message = pm_constant_pool_id_to_constant(&parser->constant_pool, cast->name);
1418 switch (message->length) {
1419 case 1:
1420 switch (message->start[0]) {
1421 case '+':
1422 case '-':
1423 case '*':
1424 case '/':
1425 case '%':
1426 case '|':
1427 case '^':
1428 case '&':
1429 case '>':
1430 case '<':
1431 type = (const char *) message->start;
1432 length = 1;
1433 break;
1434 }
1435 break;
1436 case 2:
1437 switch (message->start[1]) {
1438 case '=':
1439 if (message->start[0] == '<' || message->start[0] == '>' || message->start[0] == '!' || message->start[0] == '=') {
1440 type = (const char *) message->start;
1441 length = 2;
1442 }
1443 break;
1444 case '@':
1445 if (message->start[0] == '+' || message->start[0] == '-') {
1446 type = (const char *) message->start;
1447 length = 2;
1448 }
1449 break;
1450 case '*':
1451 if (message->start[0] == '*') {
1452 type = (const char *) message->start;
1453 length = 2;
1454 }
1455 break;
1456 }
1457 break;
1458 case 3:
1459 if (memcmp(message->start, "<=>", 3) == 0) {
1460 type = "<=>";
1461 length = 3;
1462 }
1463 break;
1464 }
1465
1466 break;
1467 }
1468 case PM_CONSTANT_PATH_NODE:
1469 type = "::";
1470 length = 2;
1471 break;
1472 case PM_CONSTANT_READ_NODE:
1473 type = "a constant";
1474 length = 10;
1475 break;
1476 case PM_DEFINED_NODE:
1477 type = "defined?";
1478 length = 8;
1479 break;
1480 case PM_FALSE_NODE:
1481 type = "false";
1482 length = 5;
1483 break;
1484 case PM_FLOAT_NODE:
1485 case PM_IMAGINARY_NODE:
1486 case PM_INTEGER_NODE:
1487 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1488 case PM_INTERPOLATED_STRING_NODE:
1489 case PM_RATIONAL_NODE:
1490 case PM_REGULAR_EXPRESSION_NODE:
1491 case PM_SOURCE_ENCODING_NODE:
1492 case PM_SOURCE_FILE_NODE:
1493 case PM_SOURCE_LINE_NODE:
1494 case PM_STRING_NODE:
1495 case PM_SYMBOL_NODE:
1496 type = "a literal";
1497 length = 9;
1498 break;
1499 case PM_NIL_NODE:
1500 type = "nil";
1501 length = 3;
1502 break;
1503 case PM_RANGE_NODE: {
1504 const pm_range_node_t *cast = (const pm_range_node_t *) node;
1505
1506 if (PM_NODE_FLAG_P(cast, PM_RANGE_FLAGS_EXCLUDE_END)) {
1507 type = "...";
1508 length = 3;
1509 } else {
1510 type = "..";
1511 length = 2;
1512 }
1513
1514 break;
1515 }
1516 case PM_SELF_NODE:
1517 type = "self";
1518 length = 4;
1519 break;
1520 case PM_TRUE_NODE:
1521 type = "true";
1522 length = 4;
1523 break;
1524 default:
1525 break;
1526 }
1527
1528 if (type != NULL) {
1529 PM_PARSER_WARN_NODE_FORMAT(parser, node, PM_WARN_VOID_STATEMENT, length, type);
1530 }
1531}
1532
1537static void
1538pm_void_statements_check(pm_parser_t *parser, const pm_statements_node_t *node, bool last_value) {
1539 assert(node->body.size > 0);
1540 const size_t size = node->body.size - (last_value ? 1 : 0);
1541 for (size_t index = 0; index < size; index++) {
1542 pm_void_statement_check(parser, node->body.nodes[index]);
1543 }
1544}
1545
1551typedef enum {
1552 PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL,
1553 PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP,
1554 PM_CONDITIONAL_PREDICATE_TYPE_NOT
1555} pm_conditional_predicate_type_t;
1556
1560static void
1561pm_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) {
1562 switch (type) {
1563 case PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL:
1564 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "condition");
1565 break;
1566 case PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP:
1567 PM_PARSER_WARN_NODE_FORMAT(parser, node, diag_id, prefix, "flip-flop");
1568 break;
1569 case PM_CONDITIONAL_PREDICATE_TYPE_NOT:
1570 break;
1571 }
1572}
1573
1578static bool
1579pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) {
1580 switch (PM_NODE_TYPE(node)) {
1581 case PM_ARRAY_NODE: {
1582 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1583
1584 const pm_array_node_t *cast = (const pm_array_node_t *) node;
1585 for (size_t index = 0; index < cast->elements.size; index++) {
1586 if (!pm_conditional_predicate_warn_write_literal_p(cast->elements.nodes[index])) return false;
1587 }
1588
1589 return true;
1590 }
1591 case PM_HASH_NODE: {
1592 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) return true;
1593
1594 const pm_hash_node_t *cast = (const pm_hash_node_t *) node;
1595 for (size_t index = 0; index < cast->elements.size; index++) {
1596 const pm_node_t *element = cast->elements.nodes[index];
1597 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) return false;
1598
1599 const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
1600 if (!pm_conditional_predicate_warn_write_literal_p(assoc->key) || !pm_conditional_predicate_warn_write_literal_p(assoc->value)) return false;
1601 }
1602
1603 return true;
1604 }
1605 case PM_FALSE_NODE:
1606 case PM_FLOAT_NODE:
1607 case PM_IMAGINARY_NODE:
1608 case PM_INTEGER_NODE:
1609 case PM_NIL_NODE:
1610 case PM_RATIONAL_NODE:
1611 case PM_REGULAR_EXPRESSION_NODE:
1612 case PM_SOURCE_ENCODING_NODE:
1613 case PM_SOURCE_FILE_NODE:
1614 case PM_SOURCE_LINE_NODE:
1615 case PM_STRING_NODE:
1616 case PM_SYMBOL_NODE:
1617 case PM_TRUE_NODE:
1618 return true;
1619 default:
1620 return false;
1621 }
1622}
1623
1628static PRISM_INLINE void
1629pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) {
1630 if (pm_conditional_predicate_warn_write_literal_p(node)) {
1631 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);
1632 }
1633}
1634
1647static void
1648pm_conditional_predicate(pm_parser_t *parser, pm_node_t *node, pm_conditional_predicate_type_t type) {
1649 switch (PM_NODE_TYPE(node)) {
1650 case PM_AND_NODE: {
1651 pm_and_node_t *cast = (pm_and_node_t *) node;
1652 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1653 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1654 break;
1655 }
1656 case PM_OR_NODE: {
1657 pm_or_node_t *cast = (pm_or_node_t *) node;
1658 pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1659 pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
1660 break;
1661 }
1662 case PM_PARENTHESES_NODE: {
1664
1665 if ((cast->body != NULL) && PM_NODE_TYPE_P(cast->body, PM_STATEMENTS_NODE)) {
1666 pm_statements_node_t *statements = (pm_statements_node_t *) cast->body;
1667 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1668 }
1669
1670 break;
1671 }
1672 case PM_BEGIN_NODE: {
1673 pm_begin_node_t *cast = (pm_begin_node_t *) node;
1674 if (cast->statements != NULL) {
1675 pm_statements_node_t *statements = cast->statements;
1676 if (statements->body.size == 1) pm_conditional_predicate(parser, statements->body.nodes[0], type);
1677 }
1678 break;
1679 }
1680 case PM_RANGE_NODE: {
1681 pm_range_node_t *cast = (pm_range_node_t *) node;
1682
1683 if (cast->left != NULL) pm_conditional_predicate(parser, cast->left, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1684 if (cast->right != NULL) pm_conditional_predicate(parser, cast->right, PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP);
1685
1686 // Here we change the range node into a flip flop node. We can do
1687 // this since the nodes are exactly the same except for the type.
1688 // We're only asserting against the size when we should probably
1689 // assert against the entire layout, but we'll assume tests will
1690 // catch this.
1691 assert(sizeof(pm_range_node_t) == sizeof(pm_flip_flop_node_t));
1692 node->type = PM_FLIP_FLOP_NODE;
1693
1694 break;
1695 }
1696 case PM_REGULAR_EXPRESSION_NODE:
1697 // Here we change the regular expression node into a match last line
1698 // node. We can do this since the nodes are exactly the same except
1699 // for the type.
1701 node->type = PM_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_DEFAULT, "regex ");
1705 }
1706
1707 break;
1708 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
1709 // Here we change the interpolated regular expression node into an
1710 // interpolated match last line node. We can do this since the nodes
1711 // are exactly the same except for the type.
1713 node->type = PM_INTERPOLATED_MATCH_LAST_LINE_NODE;
1714
1715 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1716 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "regex ");
1717 }
1718
1719 break;
1720 case PM_INTEGER_NODE:
1721 if (type == PM_CONDITIONAL_PREDICATE_TYPE_FLIP_FLOP) {
1722 if (!PM_PARSER_COMMAND_LINE_OPTION_E(parser)) {
1723 pm_parser_warn_node(parser, node, PM_WARN_INTEGER_IN_FLIP_FLOP);
1724 }
1725 } else {
1726 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1727 }
1728 break;
1729 case PM_STRING_NODE:
1730 case PM_SOURCE_FILE_NODE:
1731 case PM_INTERPOLATED_STRING_NODE:
1732 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_DEFAULT, "string ");
1733 break;
1734 case PM_SYMBOL_NODE:
1735 case PM_INTERPOLATED_SYMBOL_NODE:
1736 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "symbol ");
1737 break;
1738 case PM_SOURCE_LINE_NODE:
1739 case PM_SOURCE_ENCODING_NODE:
1740 case PM_FLOAT_NODE:
1741 case PM_RATIONAL_NODE:
1742 case PM_IMAGINARY_NODE:
1743 pm_parser_warn_conditional_predicate_literal(parser, node, type, PM_WARN_LITERAL_IN_CONDITION_VERBOSE, "");
1744 break;
1745 case PM_CLASS_VARIABLE_WRITE_NODE:
1746 pm_conditional_predicate_warn_write_literal(parser, ((pm_class_variable_write_node_t *) node)->value);
1747 break;
1748 case PM_CONSTANT_WRITE_NODE:
1749 pm_conditional_predicate_warn_write_literal(parser, ((pm_constant_write_node_t *) node)->value);
1750 break;
1751 case PM_GLOBAL_VARIABLE_WRITE_NODE:
1752 pm_conditional_predicate_warn_write_literal(parser, ((pm_global_variable_write_node_t *) node)->value);
1753 break;
1754 case PM_INSTANCE_VARIABLE_WRITE_NODE:
1755 pm_conditional_predicate_warn_write_literal(parser, ((pm_instance_variable_write_node_t *) node)->value);
1756 break;
1757 case PM_LOCAL_VARIABLE_WRITE_NODE:
1758 pm_conditional_predicate_warn_write_literal(parser, ((pm_local_variable_write_node_t *) node)->value);
1759 break;
1760 case PM_MULTI_WRITE_NODE:
1761 pm_conditional_predicate_warn_write_literal(parser, ((pm_multi_write_node_t *) node)->value);
1762 break;
1763 default:
1764 break;
1765 }
1766}
1767
1790
1794static PRISM_INLINE const pm_location_t *
1795pm_arguments_end(pm_arguments_t *arguments) {
1796 if (arguments->block != NULL) {
1797 uint32_t end = PM_NODE_END(arguments->block);
1798
1799 if (arguments->closing_loc.length > 0) {
1800 uint32_t arguments_end = PM_LOCATION_END(&arguments->closing_loc);
1801 if (arguments_end > end) {
1802 return &arguments->closing_loc;
1803 }
1804 }
1805 return &arguments->block->location;
1806 }
1807 if (arguments->closing_loc.length > 0) {
1808 return &arguments->closing_loc;
1809 }
1810 if (arguments->arguments != NULL) {
1811 return &arguments->arguments->base.location;
1812 }
1813 if (arguments->opening_loc.length > 0) {
1814 return &arguments->opening_loc;
1815 }
1816 return NULL;
1817}
1818
1823static void
1824pm_arguments_validate_block(pm_parser_t *parser, pm_arguments_t *arguments, pm_block_node_t *block) {
1825 // First, check that we have arguments and that we don't have a closing
1826 // location for them.
1827 if (arguments->arguments == NULL || arguments->closing_loc.length > 0) {
1828 return;
1829 }
1830
1831 // Next, check that we don't have a single parentheses argument. This would
1832 // look like:
1833 //
1834 // foo (1) {}
1835 //
1836 // In this case, it's actually okay for the block to be attached to the
1837 // call, even though it looks like it's attached to the argument.
1838 if (arguments->arguments->arguments.size == 1 && PM_NODE_TYPE_P(arguments->arguments->arguments.nodes[0], PM_PARENTHESES_NODE)) {
1839 return;
1840 }
1841
1842 // If we didn't hit a case before this check, then at this point we need to
1843 // add a syntax error.
1844 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_UNEXPECTED_BLOCK);
1845}
1846
1847/******************************************************************************/
1848/* Basic character checks */
1849/******************************************************************************/
1850
1857static PRISM_INLINE size_t
1858char_is_identifier_start(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
1859 if (n <= 0) return 0;
1860
1861 if (parser->encoding_changed) {
1862 size_t width;
1863
1864 if ((width = parser->encoding->alpha_char(b, n)) != 0) {
1865 return width;
1866 } else if (*b == '_') {
1867 return 1;
1868 } else if (*b >= 0x80) {
1869 return parser->encoding->char_width(b, n);
1870 } else {
1871 return 0;
1872 }
1873 } else if (*b < 0x80) {
1874 return (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHABETIC_BIT ? 1 : 0) || (*b == '_');
1875 } else {
1876 return pm_encoding_utf_8_char_width(b, n);
1877 }
1878}
1879
1884static PRISM_INLINE size_t
1885char_is_identifier_utf8(const uint8_t *b, ptrdiff_t n) {
1886 if (n <= 0) {
1887 return 0;
1888 } else if (*b < 0x80) {
1889 return (*b == '_') || (pm_encoding_unicode_table[*b] & PRISM_ENCODING_ALPHANUMERIC_BIT ? 1 : 0);
1890 } else {
1891 return pm_encoding_utf_8_char_width(b, n);
1892 }
1893}
1894
1908#if defined(PRISM_HAS_NEON)
1909#include <arm_neon.h>
1910
1911static PRISM_INLINE size_t
1912scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
1913 const uint8_t *cursor = start;
1914
1915 // Nibble-based lookup tables for classifying [a-zA-Z0-9_].
1916 // Each high nibble is assigned a unique bit; the low nibble table
1917 // contains the OR of bits for all high nibbles that have an
1918 // identifier character at that low nibble position. A byte is an
1919 // identifier character iff (low_lut[lo] & high_lut[hi]) != 0.
1920 static const uint8_t low_lut_data[16] = {
1921 0x15, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F,
1922 0x1F, 0x1F, 0x1E, 0x0A, 0x0A, 0x0A, 0x0A, 0x0E
1923 };
1924 static const uint8_t high_lut_data[16] = {
1925 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10,
1926 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1927 };
1928 const uint8x16_t low_lut = vld1q_u8(low_lut_data);
1929 const uint8x16_t high_lut = vld1q_u8(high_lut_data);
1930 const uint8x16_t mask_0f = vdupq_n_u8(0x0F);
1931
1932 while (cursor + 16 <= end) {
1933 uint8x16_t v = vld1q_u8(cursor);
1934
1935 uint8x16_t lo_class = vqtbl1q_u8(low_lut, vandq_u8(v, mask_0f));
1936 uint8x16_t hi_class = vqtbl1q_u8(high_lut, vshrq_n_u8(v, 4));
1937 uint8x16_t ident = vandq_u8(lo_class, hi_class);
1938
1939 // Fast check: if the per-byte minimum is nonzero, every byte matched.
1940 if (vminvq_u8(ident) != 0) {
1941 cursor += 16;
1942 continue;
1943 }
1944
1945 // Find the first non-identifier byte (zero in ident).
1946 uint8x16_t is_zero = vceqq_u8(ident, vdupq_n_u8(0));
1947 uint64_t lo = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 0);
1948
1949 if (lo != 0) {
1950 cursor += pm_ctzll(lo) / 8;
1951 } else {
1952 uint64_t hi = vgetq_lane_u64(vreinterpretq_u64_u8(is_zero), 1);
1953 cursor += 8 + pm_ctzll(hi) / 8;
1954 }
1955
1956 return (size_t) (cursor - start);
1957 }
1958
1959 return (size_t) (cursor - start);
1960}
1961
1962#elif defined(PRISM_HAS_SSSE3)
1963#include <tmmintrin.h>
1964
1965static PRISM_INLINE size_t
1966scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
1967 const uint8_t *cursor = start;
1968
1969 while (cursor + 16 <= end) {
1970 __m128i v = _mm_loadu_si128((const __m128i *) cursor);
1971 __m128i zero = _mm_setzero_si128();
1972
1973 // Unsigned range check via saturating subtraction:
1974 // byte >= lo ⟺ saturate(lo - byte) == 0
1975 // byte <= hi ⟺ saturate(byte - hi) == 0
1976
1977 // Fold case: OR with 0x20 maps A-Z to a-z.
1978 __m128i lowered = _mm_or_si128(v, _mm_set1_epi8(0x20));
1979 __m128i letter = _mm_and_si128(
1980 _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x61), lowered), zero),
1981 _mm_cmpeq_epi8(_mm_subs_epu8(lowered, _mm_set1_epi8(0x7A)), zero));
1982
1983 __m128i digit = _mm_and_si128(
1984 _mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(0x30), v), zero),
1985 _mm_cmpeq_epi8(_mm_subs_epu8(v, _mm_set1_epi8(0x39)), zero));
1986
1987 __m128i underscore = _mm_cmpeq_epi8(v, _mm_set1_epi8(0x5F));
1988
1989 __m128i ident = _mm_or_si128(_mm_or_si128(letter, digit), underscore);
1990 int mask = _mm_movemask_epi8(ident);
1991
1992 if (mask == 0xFFFF) {
1993 cursor += 16;
1994 continue;
1995 }
1996
1997 cursor += pm_ctzll((uint64_t) (~mask & 0xFFFF));
1998 return (size_t) (cursor - start);
1999 }
2000
2001 return (size_t) (cursor - start);
2002}
2003
2004// The SWAR path uses pm_ctzll to find the first non-matching byte within a
2005// word, which only yields the correct byte index on little-endian targets.
2006// We gate on a positive little-endian check so that unknown-endianness
2007// platforms safely fall through to the no-op fallback.
2008#elif defined(PRISM_HAS_SWAR)
2009
2019static PRISM_INLINE size_t
2020scan_identifier_ascii(const uint8_t *start, const uint8_t *end) {
2021 static const uint64_t ones = 0x0101010101010101ULL;
2022 static const uint64_t highs = 0x8080808080808080ULL;
2023 const uint8_t *cursor = start;
2024
2025 while (cursor + 8 <= end) {
2026 uint64_t word;
2027 memcpy(&word, cursor, 8);
2028
2029 // Bail on any non-ASCII byte.
2030 if (word & highs) break;
2031
2032 uint64_t digit = ((word | highs) - ones * 0x30) & ((ones * 0x39 | highs) - word) & highs;
2033
2034 // Fold upper- and lowercase together by forcing bit 5 (OR 0x20),
2035 // then check the lowercase range once. A-Z maps to a-z; the
2036 // only non-letter byte that could alias into [0x61,0x7A] is one
2037 // whose original value was in [0x41,0x5A] — which is exactly
2038 // the uppercase letters we want to match.
2039 uint64_t lowered = word | (ones * 0x20);
2040 uint64_t letter = ((lowered | highs) - ones * 0x61) & ((ones * 0x7A | highs) - lowered) & highs;
2041
2042 // Standard SWAR "has zero byte" idiom on (word XOR 0x5F) to find
2043 // bytes equal to underscore. Safe from cross-byte borrows because
2044 // the ASCII guard above ensures all bytes are < 0x80.
2045 uint64_t xor_us = word ^ (ones * 0x5F);
2046 uint64_t underscore = (xor_us - ones) & ~xor_us & highs;
2047
2048 uint64_t ident = digit | letter | underscore;
2049
2050 if (ident == highs) {
2051 cursor += 8;
2052 continue;
2053 }
2054
2055 // Find the first non-identifier byte. On little-endian the first
2056 // byte sits in the least-significant position.
2057 uint64_t not_ident = ~ident & highs;
2058 cursor += pm_ctzll(not_ident) / 8;
2059 return (size_t) (cursor - start);
2060 }
2061
2062 return (size_t) (cursor - start);
2063}
2064
2065#else
2066
2067// No-op fallback for big-endian or other unsupported platforms.
2068// The caller's byte-at-a-time loop handles everything.
2069#define scan_identifier_ascii(start, end) ((size_t) 0)
2070
2071#endif
2072
2078static PRISM_INLINE size_t
2079char_is_identifier(const pm_parser_t *parser, const uint8_t *b, ptrdiff_t n) {
2080 if (n <= 0) {
2081 return 0;
2082 } else if (parser->encoding_changed) {
2083 size_t width;
2084
2085 if ((width = parser->encoding->alnum_char(b, n)) != 0) {
2086 return width;
2087 } else if (*b == '_') {
2088 return 1;
2089 } else if (*b >= 0x80) {
2090 return parser->encoding->char_width(b, n);
2091 } else {
2092 return 0;
2093 }
2094 } else {
2095 return char_is_identifier_utf8(b, n);
2096 }
2097}
2098
2099// Here we're defining a perfect hash for the characters that are allowed in
2100// global names. This is used to quickly check the next character after a $ to
2101// see if it's a valid character for a global name.
2102#define BIT(c, idx) (((c) / 32 - 1 == idx) ? (1U << ((c) % 32)) : 0)
2103#define PUNCT(idx) ( \
2104 BIT('~', idx) | BIT('*', idx) | BIT('$', idx) | BIT('?', idx) | \
2105 BIT('!', idx) | BIT('@', idx) | BIT('/', idx) | BIT('\\', idx) | \
2106 BIT(';', idx) | BIT(',', idx) | BIT('.', idx) | BIT('=', idx) | \
2107 BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \
2108 BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \
2109 BIT('0', idx))
2110
2111const unsigned int pm_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { PUNCT(0), PUNCT(1), PUNCT(2) };
2112
2113#undef BIT
2114#undef PUNCT
2115
2116static PRISM_INLINE bool
2117char_is_global_name_punctuation(const uint8_t b) {
2118 const unsigned int i = (const unsigned int) b;
2119 if (i <= 0x20 || 0x7e < i) return false;
2120
2121 return (pm_global_name_punctuation_hash[(i - 0x20) / 32] >> (i % 32)) & 1;
2122}
2123
2124static PRISM_INLINE bool
2125token_is_setter_name(pm_token_t *token) {
2126 return (
2127 (token->type == PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL) ||
2128 ((token->type == PM_TOKEN_IDENTIFIER) &&
2129 (token->end - token->start >= 2) &&
2130 (token->end[-1] == '='))
2131 );
2132}
2133
2137static bool
2138pm_local_is_keyword(const char *source, size_t length) {
2139#define KEYWORD(name) if (memcmp(source, name, length) == 0) return true
2140
2141 switch (length) {
2142 case 2:
2143 switch (source[0]) {
2144 case 'd': KEYWORD("do"); return false;
2145 case 'i': KEYWORD("if"); KEYWORD("in"); return false;
2146 case 'o': KEYWORD("or"); return false;
2147 default: return false;
2148 }
2149 case 3:
2150 switch (source[0]) {
2151 case 'a': KEYWORD("and"); return false;
2152 case 'd': KEYWORD("def"); return false;
2153 case 'e': KEYWORD("end"); return false;
2154 case 'f': KEYWORD("for"); return false;
2155 case 'n': KEYWORD("nil"); KEYWORD("not"); return false;
2156 default: return false;
2157 }
2158 case 4:
2159 switch (source[0]) {
2160 case 'c': KEYWORD("case"); return false;
2161 case 'e': KEYWORD("else"); return false;
2162 case 'n': KEYWORD("next"); return false;
2163 case 'r': KEYWORD("redo"); return false;
2164 case 's': KEYWORD("self"); return false;
2165 case 't': KEYWORD("then"); KEYWORD("true"); return false;
2166 case 'w': KEYWORD("when"); return false;
2167 default: return false;
2168 }
2169 case 5:
2170 switch (source[0]) {
2171 case 'a': KEYWORD("alias"); return false;
2172 case 'b': KEYWORD("begin"); KEYWORD("break"); return false;
2173 case 'c': KEYWORD("class"); return false;
2174 case 'e': KEYWORD("elsif"); return false;
2175 case 'f': KEYWORD("false"); return false;
2176 case 'r': KEYWORD("retry"); return false;
2177 case 's': KEYWORD("super"); return false;
2178 case 'u': KEYWORD("undef"); KEYWORD("until"); return false;
2179 case 'w': KEYWORD("while"); return false;
2180 case 'y': KEYWORD("yield"); return false;
2181 default: return false;
2182 }
2183 case 6:
2184 switch (source[0]) {
2185 case 'e': KEYWORD("ensure"); return false;
2186 case 'm': KEYWORD("module"); return false;
2187 case 'r': KEYWORD("rescue"); KEYWORD("return"); return false;
2188 case 'u': KEYWORD("unless"); return false;
2189 default: return false;
2190 }
2191 case 8:
2192 KEYWORD("__LINE__");
2193 KEYWORD("__FILE__");
2194 return false;
2195 case 12:
2196 KEYWORD("__ENCODING__");
2197 return false;
2198 default:
2199 return false;
2200 }
2201
2202#undef KEYWORD
2203}
2204
2205/******************************************************************************/
2206/* Node flag handling functions */
2207/******************************************************************************/
2208
2212static PRISM_INLINE void
2213pm_node_flag_set(pm_node_t *node, pm_node_flags_t flag) {
2214 node->flags |= flag;
2215}
2216
2220static PRISM_INLINE void
2221pm_node_flag_unset(pm_node_t *node, pm_node_flags_t flag) {
2222 node->flags &= (pm_node_flags_t) ~flag;
2223}
2224
2228static PRISM_INLINE void
2229pm_node_flag_set_repeated_parameter(pm_node_t *node) {
2230 assert(PM_NODE_TYPE(node) == PM_BLOCK_LOCAL_VARIABLE_NODE ||
2231 PM_NODE_TYPE(node) == PM_BLOCK_PARAMETER_NODE ||
2232 PM_NODE_TYPE(node) == PM_KEYWORD_REST_PARAMETER_NODE ||
2233 PM_NODE_TYPE(node) == PM_OPTIONAL_KEYWORD_PARAMETER_NODE ||
2234 PM_NODE_TYPE(node) == PM_OPTIONAL_PARAMETER_NODE ||
2235 PM_NODE_TYPE(node) == PM_REQUIRED_KEYWORD_PARAMETER_NODE ||
2236 PM_NODE_TYPE(node) == PM_REQUIRED_PARAMETER_NODE ||
2237 PM_NODE_TYPE(node) == PM_REST_PARAMETER_NODE);
2238
2239 pm_node_flag_set(node, PM_PARAMETER_FLAGS_REPEATED_PARAMETER);
2240}
2241
2242/******************************************************************************/
2243/* Node creation functions */
2244/******************************************************************************/
2245
2251#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)
2252
2256static PRISM_INLINE pm_node_flags_t
2257pm_regular_expression_flags_create(pm_parser_t *parser, const pm_token_t *closing) {
2258 pm_node_flags_t flags = 0;
2259
2260 if (closing->type == PM_TOKEN_REGEXP_END) {
2261 pm_buffer_t unknown_flags = { 0 };
2262
2263 // The closing delimiter is normally a single byte, so the options
2264 // follow it. A `\r\n` newline delimiter is two bytes, however, so we
2265 // skip past it to avoid misreading the trailing `\n` as an option.
2266 const uint8_t *flag = closing->start + 1;
2267 if ((closing->end - closing->start) >= 2 && closing->start[0] == '\r' && closing->start[1] == '\n') {
2268 flag++;
2269 }
2270
2271 for (; flag < closing->end; flag++) {
2272 switch (*flag) {
2273 case 'i': flags |= PM_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE; break;
2274 case 'm': flags |= PM_REGULAR_EXPRESSION_FLAGS_MULTI_LINE; break;
2275 case 'x': flags |= PM_REGULAR_EXPRESSION_FLAGS_EXTENDED; break;
2276 case 'o': flags |= PM_REGULAR_EXPRESSION_FLAGS_ONCE; break;
2277
2278 case 'e': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_EUC_JP); break;
2279 case 'n': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT); break;
2280 case 's': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J); break;
2281 case 'u': flags = (pm_node_flags_t) (((pm_node_flags_t) (flags & PM_REGULAR_EXPRESSION_ENCODING_MASK)) | PM_REGULAR_EXPRESSION_FLAGS_UTF_8); break;
2282
2283 default: pm_buffer_append_byte(&unknown_flags, *flag);
2284 }
2285 }
2286
2287 size_t unknown_flags_length = pm_buffer_length(&unknown_flags);
2288 if (unknown_flags_length != 0) {
2289 const char *word = unknown_flags_length >= 2 ? "options" : "option";
2290 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_REGEXP_UNKNOWN_OPTIONS, word, unknown_flags_length, pm_buffer_value(&unknown_flags));
2291 }
2292 pm_buffer_cleanup(&unknown_flags);
2293 }
2294
2295 return flags;
2296}
2297
2298#undef PM_REGULAR_EXPRESSION_ENCODING_MASK
2299
2300static pm_statements_node_t *
2301pm_statements_node_create(pm_parser_t *parser);
2302
2303static void
2304pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline);
2305
2306static size_t
2307pm_statements_node_body_length(pm_statements_node_t *node);
2308
2313static PRISM_INLINE void
2314pm_integer_arena_move(pm_arena_t *arena, pm_integer_t *integer) {
2315 if (integer->values != NULL) {
2316 size_t byte_size = integer->length * sizeof(uint32_t);
2317 uint32_t *old_values = integer->values;
2318 integer->values = (uint32_t *) pm_arena_memdup(arena, old_values, byte_size, PRISM_ALIGNOF(uint32_t));
2319 xfree(old_values);
2320 }
2321}
2322
2326static pm_error_recovery_node_t *
2327pm_error_recovery_node_create(pm_parser_t *parser, uint32_t start, uint32_t length) {
2328 return pm_error_recovery_node_new(
2329 parser->arena,
2330 ++parser->node_id,
2331 0,
2332 ((pm_location_t) { .start = start, .length = length }),
2333 NULL
2334 );
2335}
2336
2340static pm_error_recovery_node_t *
2341pm_error_recovery_node_create_unexpected(pm_parser_t *parser, pm_node_t *unexpected) {
2342 return pm_error_recovery_node_new(
2343 parser->arena,
2344 ++parser->node_id,
2345 0,
2346 unexpected->location,
2347 unexpected
2348 );
2349}
2350
2354static pm_alias_global_variable_node_t *
2355pm_alias_global_variable_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
2356 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
2357
2358 return pm_alias_global_variable_node_new(
2359 parser->arena,
2360 ++parser->node_id,
2361 0,
2362 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name),
2363 new_name,
2364 old_name,
2365 TOK2LOC(parser, keyword)
2366 );
2367}
2368
2372static pm_alias_method_node_t *
2373pm_alias_method_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_node_t *new_name, pm_node_t *old_name) {
2374 assert(keyword->type == PM_TOKEN_KEYWORD_ALIAS);
2375
2376 return pm_alias_method_node_new(
2377 parser->arena,
2378 ++parser->node_id,
2379 0,
2380 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, old_name),
2381 new_name,
2382 old_name,
2383 TOK2LOC(parser, keyword)
2384 );
2385}
2386
2390static pm_alternation_pattern_node_t *
2391pm_alternation_pattern_node_create(pm_parser_t *parser, pm_node_t *left, pm_node_t *right, const pm_token_t *operator) {
2392 return pm_alternation_pattern_node_new(
2393 parser->arena,
2394 ++parser->node_id,
2395 0,
2396 PM_LOCATION_INIT_NODES(left, right),
2397 left,
2398 right,
2399 TOK2LOC(parser, operator)
2400 );
2401}
2402
2406static pm_and_node_t *
2407pm_and_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
2408 pm_assert_value_expression(parser, left);
2409
2410 return pm_and_node_new(
2411 parser->arena,
2412 ++parser->node_id,
2413 0,
2414 PM_LOCATION_INIT_NODES(left, right),
2415 left,
2416 right,
2417 TOK2LOC(parser, operator)
2418 );
2419}
2420
2424static pm_arguments_node_t *
2425pm_arguments_node_create(pm_parser_t *parser) {
2426 return pm_arguments_node_new(
2427 parser->arena,
2428 ++parser->node_id,
2429 0,
2430 PM_LOCATION_INIT_UNSET,
2431 ((pm_node_list_t) { 0 })
2432 );
2433}
2434
2438static size_t
2439pm_arguments_node_size(pm_arguments_node_t *node) {
2440 return node->arguments.size;
2441}
2442
2446static void
2447pm_arguments_node_arguments_append(pm_arena_t *arena, pm_arguments_node_t *node, pm_node_t *argument) {
2448 if (pm_arguments_node_size(node) == 0) {
2449 PM_NODE_START_SET_NODE(node, argument);
2450 }
2451
2452 if (PM_NODE_END(node) < PM_NODE_END(argument)) {
2453 PM_NODE_LENGTH_SET_NODE(node, argument);
2454 }
2455
2456 pm_node_list_append(arena, &node->arguments, argument);
2457
2458 if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) {
2459 if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
2460 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_MULTIPLE_SPLATS);
2461 } else {
2462 pm_node_flag_set(UP(node), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT);
2463 }
2464 }
2465}
2466
2470static pm_array_node_t *
2471pm_array_node_create(pm_parser_t *parser, const pm_token_t *opening) {
2472 if (opening == NULL) {
2473 return pm_array_node_new(
2474 parser->arena,
2475 ++parser->node_id,
2476 PM_NODE_FLAG_STATIC_LITERAL,
2477 PM_LOCATION_INIT_UNSET,
2478 ((pm_node_list_t) { 0 }),
2479 ((pm_location_t) { 0 }),
2480 ((pm_location_t) { 0 })
2481 );
2482 } else {
2483 return pm_array_node_new(
2484 parser->arena,
2485 ++parser->node_id,
2486 PM_NODE_FLAG_STATIC_LITERAL,
2487 PM_LOCATION_INIT_TOKEN(parser, opening),
2488 ((pm_node_list_t) { 0 }),
2489 TOK2LOC(parser, opening),
2490 TOK2LOC(parser, opening)
2491 );
2492 }
2493}
2494
2498static PRISM_INLINE void
2499pm_array_node_elements_append(pm_arena_t *arena, pm_array_node_t *node, pm_node_t *element) {
2500 if (!node->elements.size && !node->opening_loc.length) {
2501 PM_NODE_START_SET_NODE(node, element);
2502 }
2503
2504 pm_node_list_append(arena, &node->elements, element);
2505 PM_NODE_LENGTH_SET_NODE(node, element);
2506
2507 // If the element is not a static literal, then the array is not a static
2508 // literal. Turn that flag off.
2509 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)) {
2510 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
2511 }
2512
2513 if (PM_NODE_TYPE_P(element, PM_SPLAT_NODE)) {
2514 pm_node_flag_set(UP(node), PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT);
2515 }
2516}
2517
2521static void
2522pm_array_node_close_set(const pm_parser_t *parser, pm_array_node_t *node, const pm_token_t *closing) {
2523 assert(closing->type == PM_TOKEN_BRACKET_RIGHT || closing->type == PM_TOKEN_STRING_END || closing->type == 0);
2524 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
2525 node->closing_loc = TOK2LOC(parser, closing);
2526}
2527
2532static pm_array_pattern_node_t *
2533pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *nodes) {
2534 pm_array_pattern_node_t *node = pm_array_pattern_node_new(
2535 parser->arena,
2536 ++parser->node_id,
2537 0,
2538 PM_LOCATION_INIT_NODES(nodes->nodes[0], nodes->nodes[nodes->size - 1]),
2539 NULL,
2540 ((pm_node_list_t) { 0 }),
2541 NULL,
2542 ((pm_node_list_t) { 0 }),
2543 ((pm_location_t) { 0 }),
2544 ((pm_location_t) { 0 })
2545 );
2546
2547 // For now we're going to just copy over each pointer manually. This could be
2548 // much more efficient, as we could instead resize the node list.
2549 bool found_rest = false;
2550 pm_node_t *child;
2551
2552 PM_NODE_LIST_FOREACH(nodes, index, child) {
2553 if (!found_rest && (PM_NODE_TYPE_P(child, PM_SPLAT_NODE) || PM_NODE_TYPE_P(child, PM_IMPLICIT_REST_NODE))) {
2554 node->rest = child;
2555 found_rest = true;
2556 } else if (found_rest) {
2557 pm_node_list_append(parser->arena, &node->posts, child);
2558 } else {
2559 pm_node_list_append(parser->arena, &node->requireds, child);
2560 }
2561 }
2562
2563 return node;
2564}
2565
2569static pm_array_pattern_node_t *
2570pm_array_pattern_node_rest_create(pm_parser_t *parser, pm_node_t *rest) {
2571 return pm_array_pattern_node_new(
2572 parser->arena,
2573 ++parser->node_id,
2574 0,
2575 PM_LOCATION_INIT_NODE(rest),
2576 NULL,
2577 ((pm_node_list_t) { 0 }),
2578 rest,
2579 ((pm_node_list_t) { 0 }),
2580 ((pm_location_t) { 0 }),
2581 ((pm_location_t) { 0 })
2582 );
2583}
2584
2589static pm_array_pattern_node_t *
2590pm_array_pattern_node_constant_create(pm_parser_t *parser, pm_node_t *constant, const pm_token_t *opening, const pm_token_t *closing) {
2591 return pm_array_pattern_node_new(
2592 parser->arena,
2593 ++parser->node_id,
2594 0,
2595 PM_LOCATION_INIT_NODE_TOKEN(parser, constant, closing),
2596 constant,
2597 ((pm_node_list_t) { 0 }),
2598 NULL,
2599 ((pm_node_list_t) { 0 }),
2600 TOK2LOC(parser, opening),
2601 TOK2LOC(parser, closing)
2602 );
2603}
2604
2609static pm_array_pattern_node_t *
2610pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
2611 return pm_array_pattern_node_new(
2612 parser->arena,
2613 ++parser->node_id,
2614 0,
2615 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
2616 NULL,
2617 ((pm_node_list_t) { 0 }),
2618 NULL,
2619 ((pm_node_list_t) { 0 }),
2620 TOK2LOC(parser, opening),
2621 TOK2LOC(parser, closing)
2622 );
2623}
2624
2625static PRISM_INLINE void
2626pm_array_pattern_node_requireds_append(pm_arena_t *arena, pm_array_pattern_node_t *node, pm_node_t *inner) {
2627 pm_node_list_append(arena, &node->requireds, inner);
2628}
2629
2633static pm_assoc_node_t *
2634pm_assoc_node_create(pm_parser_t *parser, pm_node_t *key, const pm_token_t *operator, pm_node_t *value) {
2635 uint32_t end;
2636
2637 if (value != NULL && PM_NODE_END(value) > PM_NODE_END(key)) {
2638 end = PM_NODE_END(value);
2639 } else if (operator != NULL) {
2640 end = PM_TOKEN_END(parser, operator);
2641 } else {
2642 end = PM_NODE_END(key);
2643 }
2644
2645 // Hash string keys will be frozen, so we can mark them as frozen here so
2646 // that the compiler picks them up and also when we check for static literal
2647 // on the keys it gets factored in.
2648 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
2649 key->flags |= PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL;
2650 }
2651
2652 // If the key and value of this assoc node are both static literals, then
2653 // we can mark this node as a static literal.
2654 pm_node_flags_t flags = 0;
2655 if (
2656 !PM_NODE_TYPE_P(key, PM_ARRAY_NODE) && !PM_NODE_TYPE_P(key, PM_HASH_NODE) && !PM_NODE_TYPE_P(key, PM_RANGE_NODE) &&
2657 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)
2658 ) {
2659 flags = key->flags & value->flags & PM_NODE_FLAG_STATIC_LITERAL;
2660 }
2661
2662 return pm_assoc_node_new(
2663 parser->arena,
2664 ++parser->node_id,
2665 flags,
2666 ((pm_location_t) { .start = PM_NODE_START(key), .length = U32(end - PM_NODE_START(key)) }),
2667 key,
2668 value,
2669 NTOK2LOC(parser, operator)
2670 );
2671}
2672
2676static pm_assoc_splat_node_t *
2677pm_assoc_splat_node_create(pm_parser_t *parser, pm_node_t *value, const pm_token_t *operator) {
2678 assert(operator->type == PM_TOKEN_USTAR_STAR);
2679
2680 return pm_assoc_splat_node_new(
2681 parser->arena,
2682 ++parser->node_id,
2683 0,
2684 (value == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, value),
2685 value,
2686 TOK2LOC(parser, operator)
2687 );
2688}
2689
2693static pm_back_reference_read_node_t *
2694pm_back_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
2695 assert(name->type == PM_TOKEN_BACK_REFERENCE);
2696
2697 return pm_back_reference_read_node_new(
2698 parser->arena,
2699 ++parser->node_id,
2700 0,
2701 PM_LOCATION_INIT_TOKEN(parser, name),
2702 pm_parser_constant_id_token(parser, name)
2703 );
2704}
2705
2709static pm_begin_node_t *
2710pm_begin_node_create(pm_parser_t *parser, const pm_token_t *begin_keyword, pm_statements_node_t *statements) {
2711 uint32_t start = begin_keyword == NULL ? 0 : PM_TOKEN_START(parser, begin_keyword);
2712 uint32_t end = statements == NULL ? (begin_keyword == NULL ? 0 : PM_TOKEN_END(parser, begin_keyword)) : PM_NODE_END(statements);
2713
2714 return pm_begin_node_new(
2715 parser->arena,
2716 ++parser->node_id,
2717 0,
2718 ((pm_location_t) { .start = start, .length = U32(end - start) }),
2719 NTOK2LOC(parser, begin_keyword),
2720 statements,
2721 NULL,
2722 NULL,
2723 NULL,
2724 ((pm_location_t) { 0 })
2725 );
2726}
2727
2731static void
2732pm_begin_node_rescue_clause_set(pm_begin_node_t *node, pm_rescue_node_t *rescue_clause) {
2733 if (node->begin_keyword_loc.length == 0) {
2734 PM_NODE_START_SET_NODE(node, rescue_clause);
2735 }
2736 PM_NODE_LENGTH_SET_NODE(node, rescue_clause);
2737 node->rescue_clause = rescue_clause;
2738}
2739
2743static void
2744pm_begin_node_else_clause_set(pm_begin_node_t *node, pm_else_node_t *else_clause) {
2745 if ((node->begin_keyword_loc.length == 0) && PM_NODE_START(node) == 0) {
2746 PM_NODE_START_SET_NODE(node, else_clause);
2747 }
2748 PM_NODE_LENGTH_SET_NODE(node, else_clause);
2749 node->else_clause = else_clause;
2750}
2751
2755static void
2756pm_begin_node_ensure_clause_set(pm_begin_node_t *node, pm_ensure_node_t *ensure_clause) {
2757 if ((node->begin_keyword_loc.length == 0) && PM_NODE_START(node) == 0) {
2758 PM_NODE_START_SET_NODE(node, ensure_clause);
2759 }
2760 PM_NODE_LENGTH_SET_NODE(node, ensure_clause);
2761 node->ensure_clause = ensure_clause;
2762}
2763
2767static void
2768pm_begin_node_end_keyword_set(const pm_parser_t *parser, pm_begin_node_t *node, const pm_token_t *end_keyword) {
2769 assert(end_keyword->type == PM_TOKEN_KEYWORD_END || end_keyword->type == 0);
2770 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
2771 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
2772}
2773
2777static pm_block_argument_node_t *
2778pm_block_argument_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
2779 assert(operator->type == PM_TOKEN_UAMPERSAND);
2780
2781 return pm_block_argument_node_new(
2782 parser->arena,
2783 ++parser->node_id,
2784 0,
2785 (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression),
2786 expression,
2787 TOK2LOC(parser, operator)
2788 );
2789}
2790
2794static pm_block_node_t *
2795pm_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) {
2796 return pm_block_node_new(
2797 parser->arena,
2798 ++parser->node_id,
2799 0,
2800 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
2801 *locals,
2802 parameters,
2803 body,
2804 TOK2LOC(parser, opening),
2805 TOK2LOC(parser, closing)
2806 );
2807}
2808
2812static pm_block_parameter_node_t *
2813pm_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator) {
2814 assert(operator->type == PM_TOKEN_UAMPERSAND || operator->type == PM_TOKEN_AMPERSAND);
2815
2816 return pm_block_parameter_node_new(
2817 parser->arena,
2818 ++parser->node_id,
2819 0,
2820 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
2821 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
2822 NTOK2LOC(parser, name),
2823 TOK2LOC(parser, operator)
2824 );
2825}
2826
2830static pm_block_parameters_node_t *
2831pm_block_parameters_node_create(pm_parser_t *parser, pm_parameters_node_t *parameters, const pm_token_t *opening) {
2832 uint32_t start;
2833 if (opening != NULL) {
2834 start = PM_TOKEN_START(parser, opening);
2835 } else if (parameters != NULL) {
2836 start = PM_NODE_START(parameters);
2837 } else {
2838 start = 0;
2839 }
2840
2841 uint32_t end;
2842 if (parameters != NULL) {
2843 end = PM_NODE_END(parameters);
2844 } else if (opening != NULL) {
2845 end = PM_TOKEN_END(parser, opening);
2846 } else {
2847 end = 0;
2848 }
2849
2850 return pm_block_parameters_node_new(
2851 parser->arena,
2852 ++parser->node_id,
2853 0,
2854 ((pm_location_t) { .start = start, .length = U32(end - start) }),
2855 parameters,
2856 ((pm_node_list_t) { 0 }),
2857 NTOK2LOC(parser, opening),
2858 ((pm_location_t) { 0 })
2859 );
2860}
2861
2865static void
2866pm_block_parameters_node_closing_set(const pm_parser_t *parser, pm_block_parameters_node_t *node, const pm_token_t *closing) {
2867 assert(closing->type == PM_TOKEN_PIPE || closing->type == PM_TOKEN_PARENTHESIS_RIGHT || closing->type == 0);
2868 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
2869 node->closing_loc = TOK2LOC(parser, closing);
2870}
2871
2875static pm_block_local_variable_node_t *
2876pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name) {
2877 return pm_block_local_variable_node_new(
2878 parser->arena,
2879 ++parser->node_id,
2880 0,
2881 PM_LOCATION_INIT_TOKEN(parser, name),
2882 pm_parser_constant_id_token(parser, name)
2883 );
2884}
2885
2889static void
2890pm_block_parameters_node_append_local(pm_arena_t *arena, pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) {
2891 pm_node_list_append(arena, &node->locals, UP(local));
2892
2893 if (PM_NODE_LENGTH(node) == 0) {
2894 PM_NODE_START_SET_NODE(node, local);
2895 }
2896
2897 PM_NODE_LENGTH_SET_NODE(node, local);
2898}
2899
2903static pm_break_node_t *
2904pm_break_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
2905 assert(keyword->type == PM_TOKEN_KEYWORD_BREAK);
2906
2907 return pm_break_node_new(
2908 parser->arena,
2909 ++parser->node_id,
2910 0,
2911 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
2912 arguments,
2913 TOK2LOC(parser, keyword)
2914 );
2915}
2916
2917// There are certain flags that we want to use internally but don't want to
2918// expose because they are not relevant beyond parsing. Therefore we'll define
2919// them here and not define them in config.yml/a header file.
2920static const pm_node_flags_t PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY = (1 << 2);
2921
2922static const pm_node_flags_t PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY = ((PM_CALL_NODE_FLAGS_LAST - 1) << 1);
2923static const pm_node_flags_t PM_CALL_NODE_FLAGS_COMPARISON = ((PM_CALL_NODE_FLAGS_LAST - 1) << 2);
2924static const pm_node_flags_t PM_CALL_NODE_FLAGS_INDEX = ((PM_CALL_NODE_FLAGS_LAST - 1) << 3);
2925
2931static pm_call_node_t *
2932pm_call_node_create(pm_parser_t *parser, pm_node_flags_t flags) {
2933 return pm_call_node_new(
2934 parser->arena,
2935 ++parser->node_id,
2936 flags,
2937 PM_LOCATION_INIT_UNSET,
2938 NULL,
2939 ((pm_location_t) { 0 }),
2940 0,
2941 ((pm_location_t) { 0 }),
2942 ((pm_location_t) { 0 }),
2943 NULL,
2944 ((pm_location_t) { 0 }),
2945 ((pm_location_t) { 0 }),
2946 NULL
2947 );
2948}
2949
2954static PRISM_INLINE pm_node_flags_t
2955pm_call_node_ignore_visibility_flag(const pm_node_t *receiver) {
2956 return PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY : 0;
2957}
2958
2963static pm_call_node_t *
2964pm_call_node_aref_create(pm_parser_t *parser, pm_node_t *receiver, pm_arguments_t *arguments) {
2965 pm_assert_value_expression(parser, receiver);
2966
2967 pm_node_flags_t flags = pm_call_node_ignore_visibility_flag(receiver);
2968 if (arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_ARGUMENT_NODE)) {
2969 flags |= PM_CALL_NODE_FLAGS_INDEX;
2970 }
2971
2972 pm_call_node_t *node = pm_call_node_create(parser, flags);
2973
2974 PM_NODE_START_SET_NODE(node, receiver);
2975
2976 const pm_location_t *end = pm_arguments_end(arguments);
2977 assert(end != NULL && "unreachable");
2978 PM_NODE_LENGTH_SET_LOCATION(node, end);
2979
2980 node->receiver = receiver;
2981 node->message_loc.start = arguments->opening_loc.start;
2982 node->message_loc.length = (arguments->closing_loc.start + arguments->closing_loc.length) - arguments->opening_loc.start;
2983
2984 node->opening_loc = arguments->opening_loc;
2985 node->arguments = arguments->arguments;
2986 node->closing_loc = arguments->closing_loc;
2987 node->block = arguments->block;
2988
2989 node->name = pm_parser_constant_id_constant(parser, "[]", 2);
2990 return node;
2991}
2992
2996static pm_call_node_t *
2997pm_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) {
2998 pm_assert_value_expression(parser, receiver);
2999 pm_assert_value_expression(parser, argument);
3000
3001 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver) | flags);
3002
3003 PM_NODE_START_SET_NODE(node, PM_NODE_START(receiver) < PM_NODE_START(argument) ? receiver : argument);
3004 PM_NODE_LENGTH_SET_NODE(node, PM_NODE_END(receiver) > PM_NODE_END(argument) ? receiver : argument);
3005
3006 node->receiver = receiver;
3007 node->message_loc = TOK2LOC(parser, operator);
3008
3009 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
3010 pm_arguments_node_arguments_append(parser->arena, arguments, argument);
3011 node->arguments = arguments;
3012
3013 node->name = pm_parser_constant_id_token(parser, operator);
3014 return node;
3015}
3016
3017static const uint8_t * parse_operator_symbol_name(const pm_token_t *);
3018
3022static pm_call_node_t *
3023pm_call_node_call_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_token_t *message, pm_arguments_t *arguments) {
3024 pm_assert_value_expression(parser, receiver);
3025
3026 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3027
3028 PM_NODE_START_SET_NODE(node, receiver);
3029 const pm_location_t *end = pm_arguments_end(arguments);
3030 if (end == NULL) {
3031 PM_NODE_LENGTH_SET_TOKEN(parser, node, message);
3032 } else {
3033 PM_NODE_LENGTH_SET_LOCATION(node, end);
3034 }
3035
3036 node->receiver = receiver;
3037 node->call_operator_loc = TOK2LOC(parser, operator);
3038 node->message_loc = TOK2LOC(parser, message);
3039 node->opening_loc = arguments->opening_loc;
3040 node->arguments = arguments->arguments;
3041 node->closing_loc = arguments->closing_loc;
3042 node->block = arguments->block;
3043
3044 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
3045 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
3046 }
3047
3052 node->name = pm_parser_constant_id_raw(parser, message->start, parse_operator_symbol_name(message));
3053 return node;
3054}
3055
3059static pm_call_node_t *
3060pm_call_node_call_synthesized_create(pm_parser_t *parser, pm_node_t *receiver, const char *message, pm_arguments_node_t *arguments) {
3061 pm_call_node_t *node = pm_call_node_create(parser, 0);
3062 node->base.location = (pm_location_t) { .start = 0, .length = U32(parser->end - parser->start) };
3063
3064 node->receiver = receiver;
3065 node->arguments = arguments;
3066
3067 node->name = pm_parser_constant_id_constant(parser, message, strlen(message));
3068 return node;
3069}
3070
3075static pm_call_node_t *
3076pm_call_node_fcall_create(pm_parser_t *parser, pm_token_t *message, pm_arguments_t *arguments) {
3077 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3078
3079 PM_NODE_START_SET_TOKEN(parser, node, message);
3080 const pm_location_t *end = pm_arguments_end(arguments);
3081 assert(end != NULL && "unreachable");
3082 PM_NODE_LENGTH_SET_LOCATION(node, end);
3083
3084 node->message_loc = TOK2LOC(parser, message);
3085 node->opening_loc = arguments->opening_loc;
3086 node->arguments = arguments->arguments;
3087 node->closing_loc = arguments->closing_loc;
3088 node->block = arguments->block;
3089
3090 node->name = pm_parser_constant_id_token(parser, message);
3091 return node;
3092}
3093
3098static pm_call_node_t *
3099pm_call_node_fcall_synthesized_create(pm_parser_t *parser, pm_arguments_node_t *arguments, pm_constant_id_t name) {
3100 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3101
3102 node->base.location = (pm_location_t) { 0 };
3103 node->arguments = arguments;
3104
3105 node->name = name;
3106 return node;
3107}
3108
3112static pm_call_node_t *
3113pm_call_node_not_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *message, pm_arguments_t *arguments) {
3114 pm_assert_value_expression(parser, receiver);
3115 if (receiver != NULL) pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
3116
3117 pm_call_node_t *node = pm_call_node_create(parser, receiver == NULL ? 0 : pm_call_node_ignore_visibility_flag(receiver));
3118
3119 PM_NODE_START_SET_TOKEN(parser, node, message);
3120 if (arguments->closing_loc.length > 0) {
3121 PM_NODE_LENGTH_SET_LOCATION(node, &arguments->closing_loc);
3122 } else {
3123 assert(receiver != NULL);
3124 PM_NODE_LENGTH_SET_NODE(node, receiver);
3125 }
3126
3127 node->receiver = receiver;
3128 node->message_loc = TOK2LOC(parser, message);
3129 node->opening_loc = arguments->opening_loc;
3130 node->arguments = arguments->arguments;
3131 node->closing_loc = arguments->closing_loc;
3132
3133 node->name = pm_parser_constant_id_constant(parser, "!", 1);
3134 return node;
3135}
3136
3140static pm_call_node_t *
3141pm_call_node_shorthand_create(pm_parser_t *parser, pm_node_t *receiver, pm_token_t *operator, pm_arguments_t *arguments) {
3142 pm_assert_value_expression(parser, receiver);
3143
3144 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3145
3146 PM_NODE_START_SET_NODE(node, receiver);
3147 const pm_location_t *end = pm_arguments_end(arguments);
3148 assert(end != NULL && "unreachable");
3149 PM_NODE_LENGTH_SET_LOCATION(node, end);
3150
3151 node->receiver = receiver;
3152 node->call_operator_loc = TOK2LOC(parser, operator);
3153 node->opening_loc = arguments->opening_loc;
3154 node->arguments = arguments->arguments;
3155 node->closing_loc = arguments->closing_loc;
3156 node->block = arguments->block;
3157
3158 if (operator->type == PM_TOKEN_AMPERSAND_DOT) {
3159 pm_node_flag_set(UP(node), PM_CALL_NODE_FLAGS_SAFE_NAVIGATION);
3160 }
3161
3162 node->name = pm_parser_constant_id_constant(parser, "call", 4);
3163 return node;
3164}
3165
3169static pm_call_node_t *
3170pm_call_node_unary_create(pm_parser_t *parser, pm_token_t *operator, pm_node_t *receiver, const char *name) {
3171 pm_assert_value_expression(parser, receiver);
3172
3173 pm_call_node_t *node = pm_call_node_create(parser, pm_call_node_ignore_visibility_flag(receiver));
3174
3175 PM_NODE_START_SET_TOKEN(parser, node, operator);
3176 PM_NODE_LENGTH_SET_NODE(node, receiver);
3177
3178 node->receiver = receiver;
3179 node->message_loc = TOK2LOC(parser, operator);
3180
3181 node->name = pm_parser_constant_id_constant(parser, name, strlen(name));
3182 return node;
3183}
3184
3189static pm_call_node_t *
3190pm_call_node_variable_call_create(pm_parser_t *parser, pm_token_t *message) {
3191 pm_call_node_t *node = pm_call_node_create(parser, PM_CALL_NODE_FLAGS_IGNORE_VISIBILITY);
3192
3193 node->base.location = TOK2LOC(parser, message);
3194 node->message_loc = TOK2LOC(parser, message);
3195
3196 node->name = pm_parser_constant_id_token(parser, message);
3197 return node;
3198}
3199
3204static PRISM_INLINE bool
3205pm_call_node_writable_p(const pm_parser_t *parser, const pm_call_node_t *node) {
3206 return (
3207 (node->message_loc.length > 0) &&
3208 (parser->start[node->message_loc.start + node->message_loc.length - 1] != '!') &&
3209 (parser->start[node->message_loc.start + node->message_loc.length - 1] != '?') &&
3210 char_is_identifier_start(parser, parser->start + node->message_loc.start, (ptrdiff_t) node->message_loc.length) &&
3211 (node->opening_loc.length == 0) &&
3212 (node->arguments == NULL) &&
3213 (node->block == NULL)
3214 );
3215}
3216
3220static void
3221pm_call_write_read_name_init(pm_parser_t *parser, pm_constant_id_t *read_name, pm_constant_id_t *write_name) {
3222 pm_constant_t *write_constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *write_name);
3223
3224 if (write_constant->length > 0) {
3225 size_t length = write_constant->length - 1;
3226
3227 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
3228 memcpy(memory, write_constant->start, length);
3229
3230 *read_name = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, memory, length);
3231 } else {
3232 // We can get here if the message was missing because of a syntax error.
3233 *read_name = pm_parser_constant_id_constant(parser, "", 0);
3234 }
3235}
3236
3240static pm_call_and_write_node_t *
3241pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3242 assert(target->block == NULL);
3243 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3244
3245 pm_call_and_write_node_t *node = pm_call_and_write_node_new(
3246 parser->arena,
3247 ++parser->node_id,
3248 FL(target),
3249 PM_LOCATION_INIT_NODES(target, value),
3250 target->receiver,
3251 target->call_operator_loc,
3252 target->message_loc,
3253 0,
3254 target->name,
3255 TOK2LOC(parser, operator),
3256 value
3257 );
3258
3259 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3260
3261 // The target is no longer necessary because we've reused its children.
3262 // It is arena-allocated so no explicit free is needed.
3263
3264 return node;
3265}
3266
3271static void
3272pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) {
3273 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
3274 if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) {
3275 pm_node_t *node;
3276 PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) {
3277 if (PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE)) {
3278 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_INDEX_KEYWORDS);
3279 break;
3280 }
3281 }
3282 }
3283
3284 if (block != NULL) {
3285 pm_parser_err_node(parser, block, PM_ERR_UNEXPECTED_INDEX_BLOCK);
3286 }
3287 }
3288}
3289
3293static pm_index_and_write_node_t *
3294pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3295 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3296
3297 pm_index_arguments_check(parser, target->arguments, target->block);
3298
3299 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3300
3301 pm_index_and_write_node_t *node = pm_index_and_write_node_new(
3302 parser->arena,
3303 ++parser->node_id,
3304 FL(target),
3305 PM_LOCATION_INIT_NODES(target, value),
3306 target->receiver,
3307 target->call_operator_loc,
3308 target->opening_loc,
3309 target->arguments,
3310 target->closing_loc,
3311 (pm_block_argument_node_t *) target->block,
3312 TOK2LOC(parser, operator),
3313 value
3314 );
3315
3316 // The target is no longer necessary because we've reused its children.
3317 // It is arena-allocated so no explicit free is needed.
3318
3319 return node;
3320}
3321
3325static pm_call_operator_write_node_t *
3326pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3327 assert(target->block == NULL);
3328
3329 pm_call_operator_write_node_t *node = pm_call_operator_write_node_new(
3330 parser->arena,
3331 ++parser->node_id,
3332 FL(target),
3333 PM_LOCATION_INIT_NODES(target, value),
3334 target->receiver,
3335 target->call_operator_loc,
3336 target->message_loc,
3337 0,
3338 target->name,
3339 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
3340 TOK2LOC(parser, operator),
3341 value
3342 );
3343
3344 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3345
3346 // The target is no longer necessary because we've reused its children.
3347 // It is arena-allocated so no explicit free is needed.
3348
3349 return node;
3350}
3351
3355static pm_index_operator_write_node_t *
3356pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3357 pm_index_arguments_check(parser, target->arguments, target->block);
3358
3359 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3360
3361 pm_index_operator_write_node_t *node = pm_index_operator_write_node_new(
3362 parser->arena,
3363 ++parser->node_id,
3364 FL(target),
3365 PM_LOCATION_INIT_NODES(target, value),
3366 target->receiver,
3367 target->call_operator_loc,
3368 target->opening_loc,
3369 target->arguments,
3370 target->closing_loc,
3371 (pm_block_argument_node_t *) target->block,
3372 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
3373 TOK2LOC(parser, operator),
3374 value
3375 );
3376
3377 // The target is no longer necessary because we've reused its children.
3378 // It is arena-allocated so no explicit free is needed.
3379
3380 return node;
3381}
3382
3386static pm_call_or_write_node_t *
3387pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3388 assert(target->block == NULL);
3389 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3390
3391 pm_call_or_write_node_t *node = pm_call_or_write_node_new(
3392 parser->arena,
3393 ++parser->node_id,
3394 FL(target),
3395 PM_LOCATION_INIT_NODES(target, value),
3396 target->receiver,
3397 target->call_operator_loc,
3398 target->message_loc,
3399 0,
3400 target->name,
3401 TOK2LOC(parser, operator),
3402 value
3403 );
3404
3405 pm_call_write_read_name_init(parser, &node->read_name, &node->write_name);
3406
3407 // The target is no longer necessary because we've reused its children.
3408 // It is arena-allocated so no explicit free is needed.
3409
3410 return node;
3411}
3412
3416static pm_index_or_write_node_t *
3417pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3418 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3419
3420 pm_index_arguments_check(parser, target->arguments, target->block);
3421
3422 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3423
3424 pm_index_or_write_node_t *node = pm_index_or_write_node_new(
3425 parser->arena,
3426 ++parser->node_id,
3427 FL(target),
3428 PM_LOCATION_INIT_NODES(target, value),
3429 target->receiver,
3430 target->call_operator_loc,
3431 target->opening_loc,
3432 target->arguments,
3433 target->closing_loc,
3434 (pm_block_argument_node_t *) target->block,
3435 TOK2LOC(parser, operator),
3436 value
3437 );
3438
3439 // The target is no longer necessary because we've reused its children.
3440 // It is arena-allocated so no explicit free is needed.
3441
3442 return node;
3443}
3444
3449static pm_call_target_node_t *
3450pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3451 pm_call_target_node_t *node = pm_call_target_node_new(
3452 parser->arena,
3453 ++parser->node_id,
3454 FL(target),
3455 PM_LOCATION_INIT_NODE(target),
3456 target->receiver,
3457 target->call_operator_loc,
3458 target->name,
3459 target->message_loc
3460 );
3461
3462 /* It is possible to get here where we have parsed an invalid syntax tree
3463 * where the call operator was not present. In that case we will have a
3464 * problem because it is a required location. In this case we need to fill
3465 * it in with a fake location so that the syntax tree remains valid. */
3466 if (node->call_operator_loc.length == 0) {
3467 node->call_operator_loc = target->base.location;
3468 }
3469
3470 // The target is no longer necessary because we've reused its children.
3471 // It is arena-allocated so no explicit free is needed.
3472
3473 return node;
3474}
3475
3480static pm_index_target_node_t *
3481pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) {
3482 pm_index_arguments_check(parser, target->arguments, target->block);
3483 assert(!target->block || PM_NODE_TYPE_P(target->block, PM_BLOCK_ARGUMENT_NODE));
3484
3485 pm_index_target_node_t *node = pm_index_target_node_new(
3486 parser->arena,
3487 ++parser->node_id,
3488 FL(target) | PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE,
3489 PM_LOCATION_INIT_NODE(target),
3490 target->receiver,
3491 target->opening_loc,
3492 target->arguments,
3493 target->closing_loc,
3494 (pm_block_argument_node_t *) target->block
3495 );
3496
3497 // The target is no longer necessary because we've reused its children.
3498 // It is arena-allocated so no explicit free is needed.
3499
3500 return node;
3501}
3502
3506static pm_capture_pattern_node_t *
3507pm_capture_pattern_node_create(pm_parser_t *parser, pm_node_t *value, pm_local_variable_target_node_t *target, const pm_token_t *operator) {
3508 return pm_capture_pattern_node_new(
3509 parser->arena,
3510 ++parser->node_id,
3511 0,
3512 PM_LOCATION_INIT_NODES(value, target),
3513 value,
3514 target,
3515 TOK2LOC(parser, operator)
3516 );
3517}
3518
3522static pm_case_node_t *
3523pm_case_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate, const pm_token_t *end_keyword) {
3524 return pm_case_node_new(
3525 parser->arena,
3526 ++parser->node_id,
3527 0,
3528 PM_LOCATION_INIT_TOKENS(parser, case_keyword, end_keyword == NULL ? case_keyword : end_keyword),
3529 predicate,
3530 ((pm_node_list_t) { 0 }),
3531 NULL,
3532 TOK2LOC(parser, case_keyword),
3533 NTOK2LOC(parser, end_keyword)
3534 );
3535}
3536
3540static void
3541pm_case_node_condition_append(pm_arena_t *arena, pm_case_node_t *node, pm_node_t *condition) {
3542 assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
3543
3544 pm_node_list_append(arena, &node->conditions, condition);
3545 PM_NODE_LENGTH_SET_NODE(node, condition);
3546}
3547
3551static void
3552pm_case_node_else_clause_set(pm_case_node_t *node, pm_else_node_t *else_clause) {
3553 node->else_clause = else_clause;
3554 PM_NODE_LENGTH_SET_NODE(node, else_clause);
3555}
3556
3560static void
3561pm_case_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_node_t *node, const pm_token_t *end_keyword) {
3562 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
3563 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
3564}
3565
3569static pm_case_match_node_t *
3570pm_case_match_node_create(pm_parser_t *parser, const pm_token_t *case_keyword, pm_node_t *predicate) {
3571 return pm_case_match_node_new(
3572 parser->arena,
3573 ++parser->node_id,
3574 0,
3575 PM_LOCATION_INIT_TOKEN(parser, case_keyword),
3576 predicate,
3577 ((pm_node_list_t) { 0 }),
3578 NULL,
3579 TOK2LOC(parser, case_keyword),
3580 ((pm_location_t) { 0 })
3581 );
3582}
3583
3587static void
3588pm_case_match_node_condition_append(pm_arena_t *arena, pm_case_match_node_t *node, pm_node_t *condition) {
3589 assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
3590
3591 pm_node_list_append(arena, &node->conditions, condition);
3592 PM_NODE_LENGTH_SET_NODE(node, condition);
3593}
3594
3598static void
3599pm_case_match_node_else_clause_set(pm_case_match_node_t *node, pm_else_node_t *else_clause) {
3600 node->else_clause = else_clause;
3601 PM_NODE_LENGTH_SET_NODE(node, else_clause);
3602}
3603
3607static void
3608pm_case_match_node_end_keyword_loc_set(const pm_parser_t *parser, pm_case_match_node_t *node, const pm_token_t *end_keyword) {
3609 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
3610 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
3611}
3612
3616static pm_class_node_t *
3617pm_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) {
3618 return pm_class_node_new(
3619 parser->arena,
3620 ++parser->node_id,
3621 0,
3622 PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword),
3623 *locals,
3624 TOK2LOC(parser, class_keyword),
3625 constant_path,
3626 NTOK2LOC(parser, inheritance_operator),
3627 superclass,
3628 body,
3629 TOK2LOC(parser, end_keyword),
3630 pm_parser_constant_id_token(parser, name)
3631 );
3632}
3633
3637static pm_class_variable_and_write_node_t *
3638pm_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) {
3639 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3640
3641 return pm_class_variable_and_write_node_new(
3642 parser->arena,
3643 ++parser->node_id,
3644 0,
3645 PM_LOCATION_INIT_NODES(target, value),
3646 target->name,
3647 target->base.location,
3648 TOK2LOC(parser, operator),
3649 value
3650 );
3651}
3652
3656static pm_class_variable_operator_write_node_t *
3657pm_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) {
3658 return pm_class_variable_operator_write_node_new(
3659 parser->arena,
3660 ++parser->node_id,
3661 0,
3662 PM_LOCATION_INIT_NODES(target, value),
3663 target->name,
3664 target->base.location,
3665 TOK2LOC(parser, operator),
3666 value,
3667 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3668 );
3669}
3670
3674static pm_class_variable_or_write_node_t *
3675pm_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) {
3676 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3677
3678 return pm_class_variable_or_write_node_new(
3679 parser->arena,
3680 ++parser->node_id,
3681 0,
3682 PM_LOCATION_INIT_NODES(target, value),
3683 target->name,
3684 target->base.location,
3685 TOK2LOC(parser, operator),
3686 value
3687 );
3688}
3689
3693static pm_class_variable_read_node_t *
3694pm_class_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
3695 assert(token->type == PM_TOKEN_CLASS_VARIABLE);
3696
3697 return pm_class_variable_read_node_new(
3698 parser->arena,
3699 ++parser->node_id,
3700 0,
3701 PM_LOCATION_INIT_TOKEN(parser, token),
3702 pm_parser_constant_id_token(parser, token)
3703 );
3704}
3705
3712static PRISM_INLINE pm_node_flags_t
3713pm_implicit_array_write_flags(const pm_node_t *node, pm_node_flags_t flags) {
3714 if (PM_NODE_TYPE_P(node, PM_ARRAY_NODE) && ((const pm_array_node_t *) node)->opening_loc.length == 0) {
3715 return flags;
3716 }
3717 return 0;
3718}
3719
3723static pm_class_variable_write_node_t *
3724pm_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) {
3725 return pm_class_variable_write_node_new(
3726 parser->arena,
3727 ++parser->node_id,
3728 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3729 PM_LOCATION_INIT_NODES(read_node, value),
3730 read_node->name,
3731 read_node->base.location,
3732 value,
3733 TOK2LOC(parser, operator)
3734 );
3735}
3736
3740static pm_constant_path_and_write_node_t *
3741pm_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) {
3742 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3743
3744 return pm_constant_path_and_write_node_new(
3745 parser->arena,
3746 ++parser->node_id,
3747 0,
3748 PM_LOCATION_INIT_NODES(target, value),
3749 target,
3750 TOK2LOC(parser, operator),
3751 value
3752 );
3753}
3754
3758static pm_constant_path_operator_write_node_t *
3759pm_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) {
3760 return pm_constant_path_operator_write_node_new(
3761 parser->arena,
3762 ++parser->node_id,
3763 0,
3764 PM_LOCATION_INIT_NODES(target, value),
3765 target,
3766 TOK2LOC(parser, operator),
3767 value,
3768 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3769 );
3770}
3771
3775static pm_constant_path_or_write_node_t *
3776pm_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) {
3777 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3778
3779 return pm_constant_path_or_write_node_new(
3780 parser->arena,
3781 ++parser->node_id,
3782 0,
3783 PM_LOCATION_INIT_NODES(target, value),
3784 target,
3785 TOK2LOC(parser, operator),
3786 value
3787 );
3788}
3789
3793static pm_constant_path_node_t *
3794pm_constant_path_node_create(pm_parser_t *parser, pm_node_t *parent, const pm_token_t *delimiter, const pm_token_t *name_token) {
3795 pm_assert_value_expression(parser, parent);
3796
3797 pm_constant_id_t name = PM_CONSTANT_ID_UNSET;
3798 if (name_token->type == PM_TOKEN_CONSTANT) {
3799 name = pm_parser_constant_id_token(parser, name_token);
3800 }
3801
3802 return pm_constant_path_node_new(
3803 parser->arena,
3804 ++parser->node_id,
3805 0,
3806 (parent == NULL) ? PM_LOCATION_INIT_TOKENS(parser, delimiter, name_token) : PM_LOCATION_INIT_NODE_TOKEN(parser, parent, name_token),
3807 parent,
3808 name,
3809 TOK2LOC(parser, delimiter),
3810 TOK2LOC(parser, name_token)
3811 );
3812}
3813
3817static pm_constant_path_write_node_t *
3818pm_constant_path_write_node_create(pm_parser_t *parser, pm_constant_path_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3819 return pm_constant_path_write_node_new(
3820 parser->arena,
3821 ++parser->node_id,
3822 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3823 PM_LOCATION_INIT_NODES(target, value),
3824 target,
3825 TOK2LOC(parser, operator),
3826 value
3827 );
3828}
3829
3833static pm_constant_and_write_node_t *
3834pm_constant_and_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3835 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
3836
3837 return pm_constant_and_write_node_new(
3838 parser->arena,
3839 ++parser->node_id,
3840 0,
3841 PM_LOCATION_INIT_NODES(target, value),
3842 target->name,
3843 target->base.location,
3844 TOK2LOC(parser, operator),
3845 value
3846 );
3847}
3848
3852static pm_constant_operator_write_node_t *
3853pm_constant_operator_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3854 return pm_constant_operator_write_node_new(
3855 parser->arena,
3856 ++parser->node_id,
3857 0,
3858 PM_LOCATION_INIT_NODES(target, value),
3859 target->name,
3860 target->base.location,
3861 TOK2LOC(parser, operator),
3862 value,
3863 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
3864 );
3865}
3866
3870static pm_constant_or_write_node_t *
3871pm_constant_or_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3872 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
3873
3874 return pm_constant_or_write_node_new(
3875 parser->arena,
3876 ++parser->node_id,
3877 0,
3878 PM_LOCATION_INIT_NODES(target, value),
3879 target->name,
3880 target->base.location,
3881 TOK2LOC(parser, operator),
3882 value
3883 );
3884}
3885
3889static pm_constant_read_node_t *
3890pm_constant_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
3891 assert(name->type == PM_TOKEN_CONSTANT || name->type == 0);
3892
3893 return pm_constant_read_node_new(
3894 parser->arena,
3895 ++parser->node_id,
3896 0,
3897 PM_LOCATION_INIT_TOKEN(parser, name),
3898 pm_parser_constant_id_token(parser, name)
3899 );
3900}
3901
3905static pm_constant_write_node_t *
3906pm_constant_write_node_create(pm_parser_t *parser, pm_constant_read_node_t *target, const pm_token_t *operator, pm_node_t *value) {
3907 return pm_constant_write_node_new(
3908 parser->arena,
3909 ++parser->node_id,
3910 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
3911 PM_LOCATION_INIT_NODES(target, value),
3912 target->name,
3913 target->base.location,
3914 value,
3915 TOK2LOC(parser, operator)
3916 );
3917}
3918
3922static void
3923pm_def_node_receiver_check(pm_parser_t *parser, const pm_node_t *node) {
3924 switch (PM_NODE_TYPE(node)) {
3925 case PM_BEGIN_NODE: {
3926 const pm_begin_node_t *cast = (pm_begin_node_t *) node;
3927 if (cast->statements != NULL) pm_def_node_receiver_check(parser, UP(cast->statements));
3928 break;
3929 }
3930 case PM_PARENTHESES_NODE: {
3931 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
3932 if (cast->body != NULL) pm_def_node_receiver_check(parser, cast->body);
3933 break;
3934 }
3935 case PM_STATEMENTS_NODE: {
3936 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
3937 pm_def_node_receiver_check(parser, cast->body.nodes[cast->body.size - 1]);
3938 break;
3939 }
3940 case PM_ARRAY_NODE:
3941 case PM_FLOAT_NODE:
3942 case PM_IMAGINARY_NODE:
3943 case PM_INTEGER_NODE:
3944 case PM_INTERPOLATED_REGULAR_EXPRESSION_NODE:
3945 case PM_INTERPOLATED_STRING_NODE:
3946 case PM_INTERPOLATED_SYMBOL_NODE:
3947 case PM_INTERPOLATED_X_STRING_NODE:
3948 case PM_RATIONAL_NODE:
3949 case PM_REGULAR_EXPRESSION_NODE:
3950 case PM_SOURCE_ENCODING_NODE:
3951 case PM_SOURCE_FILE_NODE:
3952 case PM_SOURCE_LINE_NODE:
3953 case PM_STRING_NODE:
3954 case PM_SYMBOL_NODE:
3955 case PM_X_STRING_NODE:
3956 pm_parser_err_node(parser, node, PM_ERR_SINGLETON_FOR_LITERALS);
3957 break;
3958 default:
3959 break;
3960 }
3961}
3962
3966static pm_def_node_t *
3967pm_def_node_create(
3968 pm_parser_t *parser,
3969 pm_constant_id_t name,
3970 const pm_token_t *name_loc,
3971 pm_node_t *receiver,
3972 pm_parameters_node_t *parameters,
3973 pm_node_t *body,
3974 pm_constant_id_list_t *locals,
3975 const pm_token_t *def_keyword,
3976 const pm_token_t *operator,
3977 const pm_token_t *lparen,
3978 const pm_token_t *rparen,
3979 const pm_token_t *equal,
3980 const pm_token_t *end_keyword
3981) {
3982 if (receiver != NULL) {
3983 pm_def_node_receiver_check(parser, receiver);
3984 }
3985
3986 return pm_def_node_new(
3987 parser->arena,
3988 ++parser->node_id,
3989 0,
3990 (end_keyword == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, def_keyword, body) : PM_LOCATION_INIT_TOKENS(parser, def_keyword, end_keyword),
3991 name,
3992 TOK2LOC(parser, name_loc),
3993 receiver,
3994 parameters,
3995 body,
3996 *locals,
3997 TOK2LOC(parser, def_keyword),
3998 NTOK2LOC(parser, operator),
3999 NTOK2LOC(parser, lparen),
4000 NTOK2LOC(parser, rparen),
4001 NTOK2LOC(parser, equal),
4002 NTOK2LOC(parser, end_keyword)
4003 );
4004}
4005
4009static pm_defined_node_t *
4010pm_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) {
4011 return pm_defined_node_new(
4012 parser->arena,
4013 ++parser->node_id,
4014 0,
4015 (rparen == NULL) ? PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, value) : PM_LOCATION_INIT_TOKENS(parser, keyword, rparen),
4016 NTOK2LOC(parser, lparen),
4017 value,
4018 NTOK2LOC(parser, rparen),
4019 TOK2LOC(parser, keyword)
4020 );
4021}
4022
4026static pm_else_node_t *
4027pm_else_node_create(pm_parser_t *parser, const pm_token_t *else_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
4028 return pm_else_node_new(
4029 parser->arena,
4030 ++parser->node_id,
4031 0,
4032 ((end_keyword == NULL) && (statements != NULL)) ? PM_LOCATION_INIT_TOKEN_NODE(parser, else_keyword, statements) : PM_LOCATION_INIT_TOKENS(parser, else_keyword, end_keyword),
4033 TOK2LOC(parser, else_keyword),
4034 statements,
4035 NTOK2LOC(parser, end_keyword)
4036 );
4037}
4038
4042static pm_embedded_statements_node_t *
4043pm_embedded_statements_node_create(pm_parser_t *parser, const pm_token_t *opening, pm_statements_node_t *statements, const pm_token_t *closing) {
4044 return pm_embedded_statements_node_new(
4045 parser->arena,
4046 ++parser->node_id,
4047 0,
4048 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
4049 TOK2LOC(parser, opening),
4050 statements,
4051 TOK2LOC(parser, closing)
4052 );
4053}
4054
4058static pm_embedded_variable_node_t *
4059pm_embedded_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
4060 return pm_embedded_variable_node_new(
4061 parser->arena,
4062 ++parser->node_id,
4063 0,
4064 PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable),
4065 TOK2LOC(parser, operator),
4066 variable
4067 );
4068}
4069
4073static pm_ensure_node_t *
4074pm_ensure_node_create(pm_parser_t *parser, const pm_token_t *ensure_keyword, pm_statements_node_t *statements, const pm_token_t *end_keyword) {
4075 return pm_ensure_node_new(
4076 parser->arena,
4077 ++parser->node_id,
4078 0,
4079 PM_LOCATION_INIT_TOKENS(parser, ensure_keyword, end_keyword),
4080 TOK2LOC(parser, ensure_keyword),
4081 statements,
4082 TOK2LOC(parser, end_keyword)
4083 );
4084}
4085
4089static pm_false_node_t *
4090pm_false_node_create(pm_parser_t *parser, const pm_token_t *token) {
4091 assert(token->type == PM_TOKEN_KEYWORD_FALSE);
4092
4093 return pm_false_node_new(
4094 parser->arena,
4095 ++parser->node_id,
4096 PM_NODE_FLAG_STATIC_LITERAL,
4097 PM_LOCATION_INIT_TOKEN(parser, token)
4098 );
4099}
4100
4105static pm_find_pattern_node_t *
4106pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) {
4107 assert(nodes->size >= 2);
4108 pm_node_t *left = nodes->nodes[0];
4109 pm_node_t *right = nodes->nodes[nodes->size - 1];
4110
4111 assert(PM_NODE_TYPE_P(left, PM_SPLAT_NODE));
4112 assert(PM_NODE_TYPE_P(right, PM_SPLAT_NODE));
4113
4114 pm_find_pattern_node_t *node = pm_find_pattern_node_new(
4115 parser->arena,
4116 ++parser->node_id,
4117 0,
4118 PM_LOCATION_INIT_NODES(left, right),
4119 NULL,
4120 (pm_splat_node_t *) left,
4121 ((pm_node_list_t) { 0 }),
4122 (pm_splat_node_t *) right,
4123 ((pm_location_t) { 0 }),
4124 ((pm_location_t) { 0 })
4125 );
4126
4127 // For now we're going to just copy over each pointer manually. This could be
4128 // much more efficient, as we could instead resize the node list to only point
4129 // to 1...-1.
4130 for (size_t index = 1; index < nodes->size - 1; index++) {
4131 pm_node_list_append(parser->arena, &node->requireds, nodes->nodes[index]);
4132 }
4133
4134 return node;
4135}
4136
4141static double
4142pm_double_parse(pm_parser_t *parser, const pm_token_t *token) {
4143 ptrdiff_t diff = token->end - token->start;
4144 if (diff <= 0) return 0.0;
4145
4146 // First, get a buffer of the content.
4147 size_t length = (size_t) diff;
4148 const size_t buffer_size = sizeof(char) * (length + 1);
4149 char *buffer = xmalloc(buffer_size);
4150 memcpy((void *) buffer, token->start, length);
4151
4152 // Next, determine if we need to replace the decimal point because of
4153 // locale-specific options, and then normalize them if we have to.
4154 char decimal_point = *localeconv()->decimal_point;
4155 if (decimal_point != '.') {
4156 for (size_t index = 0; index < length; index++) {
4157 if (buffer[index] == '.') buffer[index] = decimal_point;
4158 }
4159 }
4160
4161 // Next, handle underscores by removing them from the buffer.
4162 for (size_t index = 0; index < length; index++) {
4163 if (buffer[index] == '_') {
4164 memmove((void *) (buffer + index), (void *) (buffer + index + 1), length - index);
4165 length--;
4166 }
4167 }
4168
4169 // Null-terminate the buffer so that strtod cannot read off the end.
4170 buffer[length] = '\0';
4171
4172 // Now, call strtod to parse the value. Note that CRuby has their own
4173 // version of strtod which avoids locales. We're okay using the locale-aware
4174 // version because we've already validated through the parser that the token
4175 // is in a valid format.
4176 errno = 0;
4177 char *eptr;
4178 double value = strtod(buffer, &eptr);
4179
4180 // This should never happen, because we've already checked that the token
4181 // is in a valid format. However it's good to be safe.
4182 if ((eptr != buffer + length) || (errno != 0 && errno != ERANGE)) {
4183 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, PM_ERR_FLOAT_PARSE);
4184 xfree_sized(buffer, buffer_size);
4185 return 0.0;
4186 }
4187
4188 // If errno is set, then it should only be ERANGE. At this point we need to
4189 // check if it's infinity (it should be).
4190 if (errno == ERANGE && PRISM_ISINF(value)) {
4191 int warn_width;
4192 const char *ellipsis;
4193
4194 if (length > 20) {
4195 warn_width = 20;
4196 ellipsis = "...";
4197 } else {
4198 warn_width = (int) length;
4199 ellipsis = "";
4200 }
4201
4202 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);
4203 value = (value < 0.0) ? -HUGE_VAL : HUGE_VAL;
4204 }
4205
4206 // Finally we can free the buffer and return the value.
4207 xfree_sized(buffer, buffer_size);
4208 return value;
4209}
4210
4214static pm_float_node_t *
4215pm_float_node_create(pm_parser_t *parser, const pm_token_t *token) {
4216 assert(token->type == PM_TOKEN_FLOAT);
4217
4218 return pm_float_node_new(
4219 parser->arena,
4220 ++parser->node_id,
4221 PM_NODE_FLAG_STATIC_LITERAL,
4222 PM_LOCATION_INIT_TOKEN(parser, token),
4223 pm_double_parse(parser, token)
4224 );
4225}
4226
4230static pm_imaginary_node_t *
4231pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
4232 assert(token->type == PM_TOKEN_FLOAT_IMAGINARY);
4233
4234 return pm_imaginary_node_new(
4235 parser->arena,
4236 ++parser->node_id,
4237 PM_NODE_FLAG_STATIC_LITERAL,
4238 PM_LOCATION_INIT_TOKEN(parser, token),
4239 UP(pm_float_node_create(parser, &((pm_token_t) {
4240 .type = PM_TOKEN_FLOAT,
4241 .start = token->start,
4242 .end = token->end - 1
4243 })))
4244 );
4245}
4246
4250static pm_rational_node_t *
4251pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
4252 assert(token->type == PM_TOKEN_FLOAT_RATIONAL);
4253
4254 pm_rational_node_t *node = pm_rational_node_new(
4255 parser->arena,
4256 ++parser->node_id,
4257 PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL,
4258 PM_LOCATION_INIT_TOKEN(parser, token),
4259 ((pm_integer_t) { 0 }),
4260 ((pm_integer_t) { 0 })
4261 );
4262
4263 const uint8_t *start = token->start;
4264 const uint8_t *end = token->end - 1; // r
4265
4266 while (start < end && *start == '0') start++; // 0.1 -> .1
4267 while (end > start && end[-1] == '0') end--; // 1.0 -> 1.
4268
4269 size_t length = (size_t) (end - start);
4270 if (length == 1) {
4271 node->denominator.value = 1;
4272 return node;
4273 }
4274
4275 const uint8_t *point = memchr(start, '.', length);
4276 assert(point && "should have a decimal point");
4277
4278 uint8_t *digits = xmalloc(length);
4279 if (digits == NULL) {
4280 fputs("[pm_float_node_rational_create] Failed to allocate memory", stderr);
4281 abort();
4282 }
4283
4284 memcpy(digits, start, (unsigned long) (point - start));
4285 memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1));
4286 pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1);
4287
4288 size_t fract_length = 0;
4289 for (const uint8_t *fract = point; fract < end; ++fract) {
4290 if (*fract != '_') ++fract_length;
4291 }
4292 digits[0] = '1';
4293 if (fract_length > 1) memset(digits + 1, '0', fract_length - 1);
4294 pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + fract_length);
4295 xfree_sized(digits, length);
4296
4297 pm_integers_reduce(&node->numerator, &node->denominator);
4298 pm_integer_arena_move(parser->arena, &node->numerator);
4299 pm_integer_arena_move(parser->arena, &node->denominator);
4300 return node;
4301}
4302
4307static pm_imaginary_node_t *
4308pm_float_node_rational_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
4309 assert(token->type == PM_TOKEN_FLOAT_RATIONAL_IMAGINARY);
4310
4311 return pm_imaginary_node_new(
4312 parser->arena,
4313 ++parser->node_id,
4314 PM_NODE_FLAG_STATIC_LITERAL,
4315 PM_LOCATION_INIT_TOKEN(parser, token),
4316 UP(pm_float_node_rational_create(parser, &((pm_token_t) {
4317 .type = PM_TOKEN_FLOAT_RATIONAL,
4318 .start = token->start,
4319 .end = token->end - 1
4320 })))
4321 );
4322}
4323
4327static pm_for_node_t *
4328pm_for_node_create(
4329 pm_parser_t *parser,
4330 pm_node_t *index,
4331 pm_node_t *collection,
4332 pm_statements_node_t *statements,
4333 const pm_token_t *for_keyword,
4334 const pm_token_t *in_keyword,
4335 const pm_token_t *do_keyword,
4336 const pm_token_t *end_keyword
4337) {
4338 return pm_for_node_new(
4339 parser->arena,
4340 ++parser->node_id,
4341 0,
4342 PM_LOCATION_INIT_TOKENS(parser, for_keyword, end_keyword),
4343 index,
4344 collection,
4345 statements,
4346 TOK2LOC(parser, for_keyword),
4347 TOK2LOC(parser, in_keyword),
4348 NTOK2LOC(parser, do_keyword),
4349 TOK2LOC(parser, end_keyword)
4350 );
4351}
4352
4356static pm_forwarding_arguments_node_t *
4357pm_forwarding_arguments_node_create(pm_parser_t *parser, const pm_token_t *token) {
4358 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4359
4360 return pm_forwarding_arguments_node_new(
4361 parser->arena,
4362 ++parser->node_id,
4363 0,
4364 PM_LOCATION_INIT_TOKEN(parser, token)
4365 );
4366}
4367
4371static pm_forwarding_parameter_node_t *
4372pm_forwarding_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
4373 assert(token->type == PM_TOKEN_UDOT_DOT_DOT);
4374
4375 return pm_forwarding_parameter_node_new(
4376 parser->arena,
4377 ++parser->node_id,
4378 0,
4379 PM_LOCATION_INIT_TOKEN(parser, token)
4380 );
4381}
4382
4386static pm_forwarding_super_node_t *
4387pm_forwarding_super_node_create(pm_parser_t *parser, const pm_token_t *token, pm_arguments_t *arguments) {
4388 assert(arguments->block == NULL || PM_NODE_TYPE_P(arguments->block, PM_BLOCK_NODE));
4389 assert(token->type == PM_TOKEN_KEYWORD_SUPER);
4390
4391 pm_block_node_t *block = NULL;
4392 if (arguments->block != NULL) {
4393 block = (pm_block_node_t *) arguments->block;
4394 }
4395
4396 return pm_forwarding_super_node_new(
4397 parser->arena,
4398 ++parser->node_id,
4399 0,
4400 (block == NULL) ? PM_LOCATION_INIT_TOKEN(parser, token) : PM_LOCATION_INIT_TOKEN_NODE(parser, token, block),
4401 PM_LOCATION_INIT_TOKEN(parser, token),
4402 block
4403 );
4404}
4405
4410static pm_hash_pattern_node_t *
4411pm_hash_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
4412 return pm_hash_pattern_node_new(
4413 parser->arena,
4414 ++parser->node_id,
4415 0,
4416 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
4417 NULL,
4418 ((pm_node_list_t) { 0 }),
4419 NULL,
4420 TOK2LOC(parser, opening),
4421 TOK2LOC(parser, closing)
4422 );
4423}
4424
4428static pm_hash_pattern_node_t *
4429pm_hash_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *elements, pm_node_t *rest) {
4430 uint32_t start;
4431 uint32_t end;
4432
4433 if (elements->size > 0) {
4434 if (rest) {
4435 start = MIN(PM_NODE_START(rest), PM_NODE_START(elements->nodes[0]));
4436 end = MAX(PM_NODE_END(rest), PM_NODE_END(elements->nodes[elements->size - 1]));
4437 } else {
4438 start = PM_NODE_START(elements->nodes[0]);
4439 end = PM_NODE_END(elements->nodes[elements->size - 1]);
4440 }
4441 } else {
4442 assert(rest != NULL);
4443 start = PM_NODE_START(rest);
4444 end = PM_NODE_END(rest);
4445 }
4446
4447 pm_hash_pattern_node_t *node = pm_hash_pattern_node_new(
4448 parser->arena,
4449 ++parser->node_id,
4450 0,
4451 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4452 NULL,
4453 ((pm_node_list_t) { 0 }),
4454 rest,
4455 ((pm_location_t) { 0 }),
4456 ((pm_location_t) { 0 })
4457 );
4458
4459 pm_node_list_concat(parser->arena, &node->elements, elements);
4460 return node;
4461}
4462
4466static pm_constant_id_t
4467pm_global_variable_write_name(pm_parser_t *parser, const pm_node_t *target) {
4468 switch (PM_NODE_TYPE(target)) {
4469 case PM_GLOBAL_VARIABLE_READ_NODE:
4470 return ((pm_global_variable_read_node_t *) target)->name;
4471 case PM_BACK_REFERENCE_READ_NODE:
4472 return ((pm_back_reference_read_node_t *) target)->name;
4473 case PM_NUMBERED_REFERENCE_READ_NODE:
4474 // This will only ever happen in the event of a syntax error, but we
4475 // still need to provide something for the node.
4476 return pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target));
4477 default:
4478 assert(false && "unreachable");
4479 return (pm_constant_id_t) -1;
4480 }
4481}
4482
4486static pm_global_variable_and_write_node_t *
4487pm_global_variable_and_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4488 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4489
4490 return pm_global_variable_and_write_node_new(
4491 parser->arena,
4492 ++parser->node_id,
4493 0,
4494 PM_LOCATION_INIT_NODES(target, value),
4495 pm_global_variable_write_name(parser, target),
4496 target->location,
4497 TOK2LOC(parser, operator),
4498 value
4499 );
4500}
4501
4505static pm_global_variable_operator_write_node_t *
4506pm_global_variable_operator_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4507 return pm_global_variable_operator_write_node_new(
4508 parser->arena,
4509 ++parser->node_id,
4510 0,
4511 PM_LOCATION_INIT_NODES(target, value),
4512 pm_global_variable_write_name(parser, target),
4513 target->location,
4514 TOK2LOC(parser, operator),
4515 value,
4516 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
4517 );
4518}
4519
4523static pm_global_variable_or_write_node_t *
4524pm_global_variable_or_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4525 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4526
4527 return pm_global_variable_or_write_node_new(
4528 parser->arena,
4529 ++parser->node_id,
4530 0,
4531 PM_LOCATION_INIT_NODES(target, value),
4532 pm_global_variable_write_name(parser, target),
4533 target->location,
4534 TOK2LOC(parser, operator),
4535 value
4536 );
4537}
4538
4542static pm_global_variable_read_node_t *
4543pm_global_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
4544 return pm_global_variable_read_node_new(
4545 parser->arena,
4546 ++parser->node_id,
4547 0,
4548 PM_LOCATION_INIT_TOKEN(parser, name),
4549 pm_parser_constant_id_token(parser, name)
4550 );
4551}
4552
4556static pm_global_variable_read_node_t *
4557pm_global_variable_read_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name) {
4558 return pm_global_variable_read_node_new(
4559 parser->arena,
4560 ++parser->node_id,
4561 0,
4562 PM_LOCATION_INIT_UNSET,
4563 name
4564 );
4565}
4566
4570static pm_global_variable_write_node_t *
4571pm_global_variable_write_node_create(pm_parser_t *parser, pm_node_t *target, const pm_token_t *operator, pm_node_t *value) {
4572 return pm_global_variable_write_node_new(
4573 parser->arena,
4574 ++parser->node_id,
4575 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
4576 PM_LOCATION_INIT_NODES(target, value),
4577 pm_global_variable_write_name(parser, target),
4578 target->location,
4579 value,
4580 TOK2LOC(parser, operator)
4581 );
4582}
4583
4587static pm_global_variable_write_node_t *
4588pm_global_variable_write_node_synthesized_create(pm_parser_t *parser, pm_constant_id_t name, pm_node_t *value) {
4589 return pm_global_variable_write_node_new(
4590 parser->arena,
4591 ++parser->node_id,
4592 0,
4593 PM_LOCATION_INIT_UNSET,
4594 name,
4595 ((pm_location_t) { 0 }),
4596 value,
4597 ((pm_location_t) { 0 })
4598 );
4599}
4600
4604static pm_hash_node_t *
4605pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) {
4606 assert(opening != NULL);
4607
4608 return pm_hash_node_new(
4609 parser->arena,
4610 ++parser->node_id,
4611 PM_NODE_FLAG_STATIC_LITERAL,
4612 PM_LOCATION_INIT_TOKEN(parser, opening),
4613 TOK2LOC(parser, opening),
4614 ((pm_node_list_t) { 0 }),
4615 ((pm_location_t) { 0 })
4616 );
4617}
4618
4622static PRISM_INLINE void
4623pm_hash_node_elements_append(pm_arena_t *arena, pm_hash_node_t *hash, pm_node_t *element) {
4624 pm_node_list_append(arena, &hash->elements, element);
4625
4626 bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE);
4627 if (static_literal) {
4628 pm_assoc_node_t *assoc = (pm_assoc_node_t *) element;
4629 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);
4630 static_literal = static_literal && PM_NODE_FLAG_P(assoc->key, PM_NODE_FLAG_STATIC_LITERAL);
4631 static_literal = static_literal && PM_NODE_FLAG_P(assoc, PM_NODE_FLAG_STATIC_LITERAL);
4632 }
4633
4634 if (!static_literal) {
4635 pm_node_flag_unset(UP(hash), PM_NODE_FLAG_STATIC_LITERAL);
4636 }
4637}
4638
4639static PRISM_INLINE void
4640pm_hash_node_closing_loc_set(const pm_parser_t *parser, pm_hash_node_t *hash, pm_token_t *token) {
4641 PM_NODE_LENGTH_SET_TOKEN(parser, hash, token);
4642 hash->closing_loc = TOK2LOC(parser, token);
4643}
4644
4648static pm_if_node_t *
4649pm_if_node_create(pm_parser_t *parser,
4650 const pm_token_t *if_keyword,
4651 pm_node_t *predicate,
4652 const pm_token_t *then_keyword,
4653 pm_statements_node_t *statements,
4654 pm_node_t *subsequent,
4655 const pm_token_t *end_keyword
4656) {
4657 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4658
4659 uint32_t start = PM_TOKEN_START(parser, if_keyword);
4660 uint32_t end;
4661
4662 if (end_keyword != NULL) {
4663 end = PM_TOKEN_END(parser, end_keyword);
4664 } else if (subsequent != NULL) {
4665 end = PM_NODE_END(subsequent);
4666 } else if (pm_statements_node_body_length(statements) != 0) {
4667 end = PM_NODE_END(statements);
4668 } else {
4669 end = PM_NODE_END(predicate);
4670 }
4671
4672 return pm_if_node_new(
4673 parser->arena,
4674 ++parser->node_id,
4675 PM_NODE_FLAG_NEWLINE,
4676 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4677 TOK2LOC(parser, if_keyword),
4678 predicate,
4679 NTOK2LOC(parser, then_keyword),
4680 statements,
4681 subsequent,
4682 NTOK2LOC(parser, end_keyword)
4683 );
4684}
4685
4689static pm_if_node_t *
4690pm_if_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *if_keyword, pm_node_t *predicate) {
4691 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4692
4693 pm_statements_node_t *statements = pm_statements_node_create(parser);
4694 pm_statements_node_body_append(parser, statements, statement, true);
4695
4696 return pm_if_node_new(
4697 parser->arena,
4698 ++parser->node_id,
4699 PM_NODE_FLAG_NEWLINE,
4700 PM_LOCATION_INIT_NODES(statement, predicate),
4701 TOK2LOC(parser, if_keyword),
4702 predicate,
4703 ((pm_location_t) { 0 }),
4704 statements,
4705 NULL,
4706 ((pm_location_t) { 0 })
4707 );
4708}
4709
4713static pm_if_node_t *
4714pm_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) {
4715 pm_assert_value_expression(parser, predicate);
4716 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
4717
4718 pm_statements_node_t *if_statements = pm_statements_node_create(parser);
4719 pm_statements_node_body_append(parser, if_statements, true_expression, true);
4720
4721 pm_statements_node_t *else_statements = pm_statements_node_create(parser);
4722 pm_statements_node_body_append(parser, else_statements, false_expression, true);
4723
4724 pm_else_node_t *else_node = pm_else_node_create(parser, colon, else_statements, NULL);
4725 return pm_if_node_new(
4726 parser->arena,
4727 ++parser->node_id,
4728 PM_NODE_FLAG_NEWLINE,
4729 PM_LOCATION_INIT_NODES(predicate, false_expression),
4730 ((pm_location_t) { 0 }),
4731 predicate,
4732 TOK2LOC(parser, qmark),
4733 if_statements,
4734 UP(else_node),
4735 ((pm_location_t) { 0 })
4736 );
4737}
4738
4739static PRISM_INLINE void
4740pm_if_node_end_keyword_loc_set(const pm_parser_t *parser, pm_if_node_t *node, const pm_token_t *keyword) {
4741 PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword);
4742 node->end_keyword_loc = TOK2LOC(parser, keyword);
4743}
4744
4745static PRISM_INLINE void
4746pm_else_node_end_keyword_loc_set(const pm_parser_t *parser, pm_else_node_t *node, const pm_token_t *keyword) {
4747 PM_NODE_LENGTH_SET_TOKEN(parser, node, keyword);
4748 node->end_keyword_loc = TOK2LOC(parser, keyword);
4749}
4750
4754static pm_implicit_node_t *
4755pm_implicit_node_create(pm_parser_t *parser, pm_node_t *value) {
4756 return pm_implicit_node_new(
4757 parser->arena,
4758 ++parser->node_id,
4759 0,
4760 PM_LOCATION_INIT_NODE(value),
4761 value
4762 );
4763}
4764
4768static pm_implicit_rest_node_t *
4769pm_implicit_rest_node_create(pm_parser_t *parser, const pm_token_t *token) {
4770 assert(token->type == PM_TOKEN_COMMA);
4771
4772 return pm_implicit_rest_node_new(
4773 parser->arena,
4774 ++parser->node_id,
4775 0,
4776 PM_LOCATION_INIT_TOKEN(parser, token)
4777 );
4778}
4779
4783static pm_integer_node_t *
4784pm_integer_node_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4785 assert(token->type == PM_TOKEN_INTEGER);
4786
4787 pm_integer_node_t *node = pm_integer_node_new(
4788 parser->arena,
4789 ++parser->node_id,
4790 base | PM_NODE_FLAG_STATIC_LITERAL,
4791 PM_LOCATION_INIT_TOKEN(parser, token),
4792 ((pm_integer_t) { 0 })
4793 );
4794
4795 if (parser->integer.lexed) {
4796 // The value was already computed during lexing.
4797 node->value.value = parser->integer.value;
4798 parser->integer.lexed = false;
4799 } else {
4800 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4801 switch (base) {
4802 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4803 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4804 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4805 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4806 default: assert(false && "unreachable"); break;
4807 }
4808
4809 pm_integer_parse(&node->value, integer_base, token->start, token->end);
4810 pm_integer_arena_move(parser->arena, &node->value);
4811 }
4812
4813 return node;
4814}
4815
4820static pm_imaginary_node_t *
4821pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4822 assert(token->type == PM_TOKEN_INTEGER_IMAGINARY);
4823
4824 return pm_imaginary_node_new(
4825 parser->arena,
4826 ++parser->node_id,
4827 PM_NODE_FLAG_STATIC_LITERAL,
4828 PM_LOCATION_INIT_TOKEN(parser, token),
4829 UP(pm_integer_node_create(parser, base, &((pm_token_t) {
4830 .type = PM_TOKEN_INTEGER,
4831 .start = token->start,
4832 .end = token->end - 1
4833 })))
4834 );
4835}
4836
4841static pm_rational_node_t *
4842pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4843 assert(token->type == PM_TOKEN_INTEGER_RATIONAL);
4844
4845 pm_rational_node_t *node = pm_rational_node_new(
4846 parser->arena,
4847 ++parser->node_id,
4848 base | PM_NODE_FLAG_STATIC_LITERAL,
4849 PM_LOCATION_INIT_TOKEN(parser, token),
4850 ((pm_integer_t) { 0 }),
4851 ((pm_integer_t) { .value = 1 })
4852 );
4853
4854 pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
4855 switch (base) {
4856 case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
4857 case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
4858 case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
4859 case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
4860 default: assert(false && "unreachable"); break;
4861 }
4862
4863 pm_integer_parse(&node->numerator, integer_base, token->start, token->end - 1);
4864 pm_integer_arena_move(parser->arena, &node->numerator);
4865
4866 return node;
4867}
4868
4873static pm_imaginary_node_t *
4874pm_integer_node_rational_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, const pm_token_t *token) {
4875 assert(token->type == PM_TOKEN_INTEGER_RATIONAL_IMAGINARY);
4876
4877 return pm_imaginary_node_new(
4878 parser->arena,
4879 ++parser->node_id,
4880 PM_NODE_FLAG_STATIC_LITERAL,
4881 PM_LOCATION_INIT_TOKEN(parser, token),
4882 UP(pm_integer_node_rational_create(parser, base, &((pm_token_t) {
4883 .type = PM_TOKEN_INTEGER_RATIONAL,
4884 .start = token->start,
4885 .end = token->end - 1
4886 })))
4887 );
4888}
4889
4893static pm_in_node_t *
4894pm_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) {
4895 uint32_t start = PM_TOKEN_START(parser, in_keyword);
4896 uint32_t end;
4897
4898 if (statements != NULL) {
4899 end = PM_NODE_END(statements);
4900 } else if (then_keyword != NULL) {
4901 end = PM_TOKEN_END(parser, then_keyword);
4902 } else {
4903 end = PM_NODE_END(pattern);
4904 }
4905
4906 return pm_in_node_new(
4907 parser->arena,
4908 ++parser->node_id,
4909 0,
4910 ((pm_location_t) { .start = start, .length = U32(end - start) }),
4911 pattern,
4912 statements,
4913 TOK2LOC(parser, in_keyword),
4914 NTOK2LOC(parser, then_keyword)
4915 );
4916}
4917
4921static pm_instance_variable_and_write_node_t *
4922pm_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) {
4923 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
4924
4925 return pm_instance_variable_and_write_node_new(
4926 parser->arena,
4927 ++parser->node_id,
4928 0,
4929 PM_LOCATION_INIT_NODES(target, value),
4930 target->name,
4931 target->base.location,
4932 TOK2LOC(parser, operator),
4933 value
4934 );
4935}
4936
4940static pm_instance_variable_operator_write_node_t *
4941pm_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) {
4942 return pm_instance_variable_operator_write_node_new(
4943 parser->arena,
4944 ++parser->node_id,
4945 0,
4946 PM_LOCATION_INIT_NODES(target, value),
4947 target->name,
4948 target->base.location,
4949 TOK2LOC(parser, operator),
4950 value,
4951 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1)
4952 );
4953}
4954
4958static pm_instance_variable_or_write_node_t *
4959pm_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) {
4960 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
4961
4962 return pm_instance_variable_or_write_node_new(
4963 parser->arena,
4964 ++parser->node_id,
4965 0,
4966 PM_LOCATION_INIT_NODES(target, value),
4967 target->name,
4968 target->base.location,
4969 TOK2LOC(parser, operator),
4970 value
4971 );
4972}
4973
4977static pm_instance_variable_read_node_t *
4978pm_instance_variable_read_node_create(pm_parser_t *parser, const pm_token_t *token) {
4979 assert(token->type == PM_TOKEN_INSTANCE_VARIABLE);
4980
4981 return pm_instance_variable_read_node_new(
4982 parser->arena,
4983 ++parser->node_id,
4984 0,
4985 PM_LOCATION_INIT_TOKEN(parser, token),
4986 pm_parser_constant_id_token(parser, token)
4987 );
4988}
4989
4994static pm_instance_variable_write_node_t *
4995pm_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) {
4996 return pm_instance_variable_write_node_new(
4997 parser->arena,
4998 ++parser->node_id,
4999 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5000 PM_LOCATION_INIT_NODES(read_node, value),
5001 read_node->name,
5002 read_node->base.location,
5003 value,
5004 TOK2LOC(parser, operator)
5005 );
5006}
5007
5013static void
5014pm_interpolated_node_append(pm_arena_t *arena, pm_node_t *node, pm_node_list_t *parts, pm_node_t *part) {
5015 switch (PM_NODE_TYPE(part)) {
5016 case PM_STRING_NODE:
5017 pm_node_flag_set(part, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
5018 break;
5019 case PM_EMBEDDED_STATEMENTS_NODE: {
5020 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
5021 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
5022
5023 if (embedded == NULL) {
5024 // If there are no statements or more than one statement, then
5025 // we lose the static literal flag.
5026 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
5027 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
5028 // If the embedded statement is a string, then we can keep the
5029 // static literal flag and mark the string as frozen.
5030 pm_node_flag_set(embedded, PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN);
5031 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
5032 // If the embedded statement is an interpolated string and it's
5033 // a static literal, then we can keep the static literal flag.
5034 } else {
5035 // Otherwise we lose the static literal flag.
5036 pm_node_flag_unset(node, PM_NODE_FLAG_STATIC_LITERAL);
5037 }
5038
5039 break;
5040 }
5041 case PM_EMBEDDED_VARIABLE_NODE:
5042 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
5043 break;
5044 default:
5045 assert(false && "unexpected node type");
5046 break;
5047 }
5048
5049 pm_node_list_append(arena, parts, part);
5050}
5051
5055static pm_interpolated_regular_expression_node_t *
5056pm_interpolated_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening) {
5057 return pm_interpolated_regular_expression_node_new(
5058 parser->arena,
5059 ++parser->node_id,
5060 PM_NODE_FLAG_STATIC_LITERAL,
5061 PM_LOCATION_INIT_TOKEN(parser, opening),
5062 TOK2LOC(parser, opening),
5063 ((pm_node_list_t) { 0 }),
5064 TOK2LOC(parser, opening)
5065 );
5066}
5067
5068static PRISM_INLINE void
5069pm_interpolated_regular_expression_node_append(pm_arena_t *arena, pm_interpolated_regular_expression_node_t *node, pm_node_t *part) {
5070 if (PM_NODE_START(node) > PM_NODE_START(part)) {
5071 PM_NODE_START_SET_NODE(node, part);
5072 }
5073 if (PM_NODE_END(node) < PM_NODE_END(part)) {
5074 PM_NODE_LENGTH_SET_NODE(node, part);
5075 }
5076
5077 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5078}
5079
5080static PRISM_INLINE void
5081pm_interpolated_regular_expression_node_closing_set(pm_parser_t *parser, pm_interpolated_regular_expression_node_t *node, const pm_token_t *closing) {
5082 node->closing_loc = TOK2LOC(parser, closing);
5083 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5084 pm_node_flag_set(UP(node), pm_regular_expression_flags_create(parser, closing));
5085}
5086
5110static PRISM_INLINE void
5111pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_node_t *node, pm_node_t *part) {
5112 pm_arena_t *arena = parser->arena;
5113#define CLEAR_FLAGS(node) \
5114 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))
5115
5116#define MUTABLE_FLAGS(node) \
5117 node->base.flags = (pm_node_flags_t) ((FL(node) | PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE) & ~PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN);
5118
5119 if (node->parts.size == 0 && node->opening_loc.length == 0) {
5120 PM_NODE_START_SET_NODE(node, part);
5121 }
5122
5123 if (PM_NODE_END(part) > PM_NODE_END(node)) {
5124 PM_NODE_LENGTH_SET_NODE(node, part);
5125 }
5126
5127 switch (PM_NODE_TYPE(part)) {
5128 case PM_STRING_NODE:
5129 // If inner string is not frozen, it stops being a static literal. We should *not* clear other flags,
5130 // because concatenating two frozen strings (`'foo' 'bar'`) is still frozen. This holds true for
5131 // as long as this interpolation only consists of other string literals.
5132 if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
5133 pm_node_flag_unset(UP(node), PM_NODE_FLAG_STATIC_LITERAL);
5134 }
5135 part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5136 break;
5137 case PM_INTERPOLATED_STRING_NODE:
5138 if (PM_NODE_FLAG_P(part, PM_NODE_FLAG_STATIC_LITERAL)) {
5139 // If the string that we're concatenating is a static literal,
5140 // then we can keep the static literal flag for this string.
5141 } else {
5142 // Otherwise, we lose the static literal flag here and we should
5143 // also clear the mutability flags.
5144 CLEAR_FLAGS(node);
5145 }
5146 break;
5147 case PM_EMBEDDED_STATEMENTS_NODE: {
5148 pm_embedded_statements_node_t *cast = (pm_embedded_statements_node_t *) part;
5149 pm_node_t *embedded = (cast->statements != NULL && cast->statements->body.size == 1) ? cast->statements->body.nodes[0] : NULL;
5150
5151 if (embedded == NULL) {
5152 // If we're embedding multiple statements or no statements, then
5153 // the string is not longer a static literal.
5154 CLEAR_FLAGS(node);
5155 } else if (PM_NODE_TYPE_P(embedded, PM_STRING_NODE)) {
5156 // If the embedded statement is a string, then we can make that
5157 // string as frozen and static literal, and not touch the static
5158 // literal status of this string.
5159 embedded->flags = (pm_node_flags_t) ((embedded->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5160
5161 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
5162 MUTABLE_FLAGS(node);
5163 }
5164 } else if (PM_NODE_TYPE_P(embedded, PM_INTERPOLATED_STRING_NODE) && PM_NODE_FLAG_P(embedded, PM_NODE_FLAG_STATIC_LITERAL)) {
5165 // If the embedded statement is an interpolated string, but that
5166 // string is marked as static literal, then we can keep our
5167 // static literal status for this string.
5168 if (PM_NODE_FLAG_P(node, PM_NODE_FLAG_STATIC_LITERAL)) {
5169 MUTABLE_FLAGS(node);
5170 }
5171 } else {
5172 // In all other cases, we lose the static literal flag here and
5173 // become mutable.
5174 CLEAR_FLAGS(node);
5175 }
5176
5177 break;
5178 }
5179 case PM_EMBEDDED_VARIABLE_NODE:
5180 // Embedded variables clear static literal, which means we also
5181 // should clear the mutability flags.
5182 CLEAR_FLAGS(node);
5183 break;
5184 case PM_X_STRING_NODE:
5185 case PM_INTERPOLATED_X_STRING_NODE:
5186 case PM_SYMBOL_NODE:
5187 case PM_INTERPOLATED_SYMBOL_NODE:
5188 // These will only happen in error cases. But we want to handle it
5189 // here so that we don't fail the assertion.
5190 CLEAR_FLAGS(node);
5191 pm_node_list_append(arena, &node->parts, UP(pm_error_recovery_node_create_unexpected(parser, part)));
5192 return;
5193 case PM_ERROR_RECOVERY_NODE:
5194 CLEAR_FLAGS(node);
5195 break;
5196 default:
5197 assert(false && "unexpected node type");
5198 break;
5199 }
5200
5201 pm_node_list_append(arena, &node->parts, part);
5202
5203#undef CLEAR_FLAGS
5204#undef MUTABLE_FLAGS
5205}
5206
5210static pm_interpolated_string_node_t *
5211pm_interpolated_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
5212 pm_node_flags_t flags = PM_NODE_FLAG_STATIC_LITERAL;
5213
5214 switch (parser->frozen_string_literal) {
5215 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
5216 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_MUTABLE;
5217 break;
5218 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
5219 flags |= PM_INTERPOLATED_STRING_NODE_FLAGS_FROZEN;
5220 break;
5221 }
5222
5223 uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening);
5224 uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing);
5225
5226 pm_interpolated_string_node_t *node = pm_interpolated_string_node_new(
5227 parser->arena,
5228 ++parser->node_id,
5229 flags,
5230 ((pm_location_t) { .start = start, .length = U32(end - start) }),
5231 NTOK2LOC(parser, opening),
5232 ((pm_node_list_t) { 0 }),
5233 NTOK2LOC(parser, closing)
5234 );
5235
5236 if (parts != NULL) {
5237 pm_node_t *part;
5238 PM_NODE_LIST_FOREACH(parts, index, part) {
5239 pm_interpolated_string_node_append(parser, node, part);
5240 }
5241 }
5242
5243 return node;
5244}
5245
5249static void
5250pm_interpolated_string_node_closing_set(const pm_parser_t *parser, pm_interpolated_string_node_t *node, const pm_token_t *closing) {
5251 node->closing_loc = TOK2LOC(parser, closing);
5252 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5253}
5254
5255static void
5256pm_interpolated_symbol_node_append(pm_arena_t *arena, pm_interpolated_symbol_node_t *node, pm_node_t *part) {
5257 if (node->parts.size == 0 && node->opening_loc.length == 0) {
5258 PM_NODE_START_SET_NODE(node, part);
5259 }
5260
5261 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5262
5263 if (PM_NODE_END(part) > PM_NODE_END(node)) {
5264 PM_NODE_LENGTH_SET_NODE(node, part);
5265 }
5266}
5267
5268static void
5269pm_interpolated_symbol_node_closing_loc_set(const pm_parser_t *parser, pm_interpolated_symbol_node_t *node, const pm_token_t *closing) {
5270 node->closing_loc = TOK2LOC(parser, closing);
5271 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5272}
5273
5277static pm_interpolated_symbol_node_t *
5278pm_interpolated_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_node_list_t *parts, const pm_token_t *closing) {
5279 uint32_t start = opening == NULL ? 0 : PM_TOKEN_START(parser, opening);
5280 uint32_t end = closing == NULL ? 0 : PM_TOKEN_END(parser, closing);
5281
5282 pm_interpolated_symbol_node_t *node = pm_interpolated_symbol_node_new(
5283 parser->arena,
5284 ++parser->node_id,
5285 PM_NODE_FLAG_STATIC_LITERAL,
5286 ((pm_location_t) { .start = start, .length = U32(end - start) }),
5287 NTOK2LOC(parser, opening),
5288 ((pm_node_list_t) { 0 }),
5289 NTOK2LOC(parser, closing)
5290 );
5291
5292 if (parts != NULL) {
5293 pm_node_t *part;
5294 PM_NODE_LIST_FOREACH(parts, index, part) {
5295 pm_interpolated_symbol_node_append(parser->arena, node, part);
5296 }
5297 }
5298
5299 return node;
5300}
5301
5305static pm_interpolated_x_string_node_t *
5306pm_interpolated_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
5307 return pm_interpolated_x_string_node_new(
5308 parser->arena,
5309 ++parser->node_id,
5310 0,
5311 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
5312 TOK2LOC(parser, opening),
5313 ((pm_node_list_t) { 0 }),
5314 TOK2LOC(parser, closing)
5315 );
5316}
5317
5318static PRISM_INLINE void
5319pm_interpolated_xstring_node_append(pm_arena_t *arena, pm_interpolated_x_string_node_t *node, pm_node_t *part) {
5320 pm_interpolated_node_append(arena, UP(node), &node->parts, part);
5321 PM_NODE_LENGTH_SET_NODE(node, part);
5322}
5323
5324static PRISM_INLINE void
5325pm_interpolated_xstring_node_closing_set(const pm_parser_t *parser, pm_interpolated_x_string_node_t *node, const pm_token_t *closing) {
5326 node->closing_loc = TOK2LOC(parser, closing);
5327 PM_NODE_LENGTH_SET_TOKEN(parser, node, closing);
5328}
5329
5333static pm_it_local_variable_read_node_t *
5334pm_it_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
5335 return pm_it_local_variable_read_node_new(
5336 parser->arena,
5337 ++parser->node_id,
5338 0,
5339 PM_LOCATION_INIT_TOKEN(parser, name)
5340 );
5341}
5342
5346static pm_it_parameters_node_t *
5347pm_it_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing) {
5348 return pm_it_parameters_node_new(
5349 parser->arena,
5350 ++parser->node_id,
5351 0,
5352 PM_LOCATION_INIT_TOKENS(parser, opening, closing)
5353 );
5354}
5355
5359static pm_keyword_hash_node_t *
5360pm_keyword_hash_node_create(pm_parser_t *parser) {
5361 return pm_keyword_hash_node_new(
5362 parser->arena,
5363 ++parser->node_id,
5364 PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS,
5365 PM_LOCATION_INIT_UNSET,
5366 ((pm_node_list_t) { 0 })
5367 );
5368}
5369
5373static void
5374pm_keyword_hash_node_elements_append(pm_arena_t *arena, pm_keyword_hash_node_t *hash, pm_node_t *element) {
5375 // If the element being added is not an AssocNode or does not have a symbol
5376 // key, then we want to turn the SYMBOL_KEYS flag off.
5377 if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE) || !PM_NODE_TYPE_P(((pm_assoc_node_t *) element)->key, PM_SYMBOL_NODE)) {
5378 pm_node_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS);
5379 }
5380
5381 pm_node_list_append(arena, &hash->elements, element);
5382 if (PM_NODE_LENGTH(hash) == 0) {
5383 PM_NODE_START_SET_NODE(hash, element);
5384 }
5385 PM_NODE_LENGTH_SET_NODE(hash, element);
5386}
5387
5391static pm_required_keyword_parameter_node_t *
5392pm_required_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name) {
5393 return pm_required_keyword_parameter_node_new(
5394 parser->arena,
5395 ++parser->node_id,
5396 0,
5397 PM_LOCATION_INIT_TOKEN(parser, name),
5398 pm_parser_constant_id_raw(parser, name->start, name->end - 1),
5399 TOK2LOC(parser, name)
5400 );
5401}
5402
5406static pm_optional_keyword_parameter_node_t *
5407pm_optional_keyword_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, pm_node_t *value) {
5408 return pm_optional_keyword_parameter_node_new(
5409 parser->arena,
5410 ++parser->node_id,
5411 0,
5412 PM_LOCATION_INIT_TOKEN_NODE(parser, name, value),
5413 pm_parser_constant_id_raw(parser, name->start, name->end - 1),
5414 TOK2LOC(parser, name),
5415 value
5416 );
5417}
5418
5422static pm_keyword_rest_parameter_node_t *
5423pm_keyword_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
5424 return pm_keyword_rest_parameter_node_new(
5425 parser->arena,
5426 ++parser->node_id,
5427 0,
5428 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
5429 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
5430 NTOK2LOC(parser, name),
5431 TOK2LOC(parser, operator)
5432 );
5433}
5434
5438static pm_lambda_node_t *
5439pm_lambda_node_create(
5440 pm_parser_t *parser,
5441 pm_constant_id_list_t *locals,
5442 const pm_token_t *operator,
5443 const pm_token_t *opening,
5444 const pm_token_t *closing,
5445 pm_node_t *parameters,
5446 pm_node_t *body
5447) {
5448 return pm_lambda_node_new(
5449 parser->arena,
5450 ++parser->node_id,
5451 0,
5452 PM_LOCATION_INIT_TOKENS(parser, operator, closing),
5453 *locals,
5454 TOK2LOC(parser, operator),
5455 TOK2LOC(parser, opening),
5456 TOK2LOC(parser, closing),
5457 parameters,
5458 body
5459 );
5460}
5461
5465static pm_local_variable_and_write_node_t *
5466pm_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) {
5467 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));
5468 assert(operator->type == PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
5469
5470 return pm_local_variable_and_write_node_new(
5471 parser->arena,
5472 ++parser->node_id,
5473 0,
5474 PM_LOCATION_INIT_NODES(target, value),
5475 target->location,
5476 TOK2LOC(parser, operator),
5477 value,
5478 name,
5479 depth
5480 );
5481}
5482
5486static pm_local_variable_operator_write_node_t *
5487pm_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) {
5488 return pm_local_variable_operator_write_node_new(
5489 parser->arena,
5490 ++parser->node_id,
5491 0,
5492 PM_LOCATION_INIT_NODES(target, value),
5493 target->location,
5494 TOK2LOC(parser, operator),
5495 value,
5496 name,
5497 pm_parser_constant_id_raw(parser, operator->start, operator->end - 1),
5498 depth
5499 );
5500}
5501
5505static pm_local_variable_or_write_node_t *
5506pm_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) {
5507 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));
5508 assert(operator->type == PM_TOKEN_PIPE_PIPE_EQUAL);
5509
5510 return pm_local_variable_or_write_node_new(
5511 parser->arena,
5512 ++parser->node_id,
5513 0,
5514 PM_LOCATION_INIT_NODES(target, value),
5515 target->location,
5516 TOK2LOC(parser, operator),
5517 value,
5518 name,
5519 depth
5520 );
5521}
5522
5526static pm_local_variable_read_node_t *
5527pm_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) {
5528 if (!missing) pm_locals_read(&pm_parser_scope_find(parser, depth)->locals, name_id);
5529
5530 return pm_local_variable_read_node_new(
5531 parser->arena,
5532 ++parser->node_id,
5533 0,
5534 PM_LOCATION_INIT_TOKEN(parser, name),
5535 name_id,
5536 depth
5537 );
5538}
5539
5543static pm_local_variable_read_node_t *
5544pm_local_variable_read_node_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5545 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5546 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, false);
5547}
5548
5553static pm_local_variable_read_node_t *
5554pm_local_variable_read_node_missing_create(pm_parser_t *parser, const pm_token_t *name, uint32_t depth) {
5555 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, name);
5556 return pm_local_variable_read_node_create_constant_id(parser, name, name_id, depth, true);
5557}
5558
5562static pm_local_variable_write_node_t *
5563pm_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) {
5564 return pm_local_variable_write_node_new(
5565 parser->arena,
5566 ++parser->node_id,
5567 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5568 ((pm_location_t) { .start = name_loc->start, .length = PM_NODE_END(value) - name_loc->start }),
5569 name,
5570 depth,
5571 *name_loc,
5572 value,
5573 TOK2LOC(parser, operator)
5574 );
5575}
5576
5580static PRISM_INLINE bool
5581pm_token_is_it(const uint8_t *start, const uint8_t *end) {
5582 return (end - start == 2) && (start[0] == 'i') && (start[1] == 't');
5583}
5584
5589static PRISM_INLINE bool
5590pm_token_is_numbered_parameter(const pm_parser_t *parser, uint32_t start, uint32_t length) {
5591 return (
5592 (length == 2) &&
5593 (parser->start[start] == '_') &&
5594 (parser->start[start + 1] != '0') &&
5595 pm_char_is_decimal_digit(parser->start[start + 1])
5596 );
5597}
5598
5603static PRISM_INLINE void
5604pm_refute_numbered_parameter(pm_parser_t *parser, uint32_t start, uint32_t length) {
5605 if (pm_token_is_numbered_parameter(parser, start, length)) {
5606 PM_PARSER_ERR_FORMAT(parser, start, length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + start);
5607 }
5608}
5609
5614static pm_local_variable_target_node_t *
5615pm_local_variable_target_node_create(pm_parser_t *parser, const pm_location_t *location, pm_constant_id_t name, uint32_t depth) {
5616 pm_refute_numbered_parameter(parser, location->start, location->length);
5617
5618 return pm_local_variable_target_node_new(
5619 parser->arena,
5620 ++parser->node_id,
5621 0,
5622 ((pm_location_t) { .start = location->start, .length = location->length }),
5623 name,
5624 depth
5625 );
5626}
5627
5631static pm_match_predicate_node_t *
5632pm_match_predicate_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5633 pm_assert_value_expression(parser, value);
5634
5635 return pm_match_predicate_node_new(
5636 parser->arena,
5637 ++parser->node_id,
5638 0,
5639 PM_LOCATION_INIT_NODES(value, pattern),
5640 value,
5641 pattern,
5642 TOK2LOC(parser, operator)
5643 );
5644}
5645
5649static pm_match_required_node_t *
5650pm_match_required_node_create(pm_parser_t *parser, pm_node_t *value, pm_node_t *pattern, const pm_token_t *operator) {
5651 pm_assert_value_expression(parser, value);
5652
5653 return pm_match_required_node_new(
5654 parser->arena,
5655 ++parser->node_id,
5656 0,
5657 PM_LOCATION_INIT_NODES(value, pattern),
5658 value,
5659 pattern,
5660 TOK2LOC(parser, operator)
5661 );
5662}
5663
5667static pm_match_write_node_t *
5668pm_match_write_node_create(pm_parser_t *parser, pm_call_node_t *call) {
5669 return pm_match_write_node_new(
5670 parser->arena,
5671 ++parser->node_id,
5672 0,
5673 PM_LOCATION_INIT_NODE(call),
5674 call,
5675 ((pm_node_list_t) { 0 })
5676 );
5677}
5678
5682static pm_module_node_t *
5683pm_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) {
5684 pm_constant_id_list_t module_locals = { .ids = NULL, .size = 0, .capacity = 0 };
5685 if (locals != NULL) module_locals = *locals;
5686
5687 return pm_module_node_new(
5688 parser->arena,
5689 ++parser->node_id,
5690 0,
5691 PM_LOCATION_INIT_TOKENS(parser, module_keyword, end_keyword),
5692 module_locals,
5693 TOK2LOC(parser, module_keyword),
5694 constant_path,
5695 body,
5696 TOK2LOC(parser, end_keyword),
5697 pm_parser_constant_id_token(parser, name)
5698 );
5699}
5700
5704static pm_multi_target_node_t *
5705pm_multi_target_node_create(pm_parser_t *parser) {
5706 return pm_multi_target_node_new(
5707 parser->arena,
5708 ++parser->node_id,
5709 0,
5710 PM_LOCATION_INIT_UNSET,
5711 ((pm_node_list_t) { 0 }),
5712 NULL,
5713 ((pm_node_list_t) { 0 }),
5714 ((pm_location_t) { 0 }),
5715 ((pm_location_t) { 0 })
5716 );
5717}
5718
5722static void
5723pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t *node, pm_node_t *target) {
5724 if (PM_NODE_TYPE_P(target, PM_SPLAT_NODE)) {
5725 if (node->rest == NULL) {
5726 node->rest = target;
5727 } else {
5728 pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
5729 pm_node_list_append(parser->arena, &node->rights, target);
5730 }
5731 } else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
5732 if (node->rest == NULL) {
5733 node->rest = target;
5734 } else {
5735 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
5736 pm_node_list_append(parser->arena, &node->rights, target);
5737 }
5738 } else if (node->rest == NULL) {
5739 pm_node_list_append(parser->arena, &node->lefts, target);
5740 } else {
5741 pm_node_list_append(parser->arena, &node->rights, target);
5742 }
5743
5744 if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_START(node) > PM_NODE_START(target))) {
5745 PM_NODE_START_SET_NODE(node, target);
5746 }
5747
5748 if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_END(node) < PM_NODE_END(target))) {
5749 PM_NODE_LENGTH_SET_NODE(node, target);
5750 }
5751}
5752
5756static void
5757pm_multi_target_node_opening_set(const pm_parser_t *parser, pm_multi_target_node_t *node, const pm_token_t *lparen) {
5758 PM_NODE_START_SET_TOKEN(parser, node, lparen);
5759 PM_NODE_LENGTH_SET_TOKEN(parser, node, lparen);
5760 node->lparen_loc = TOK2LOC(parser, lparen);
5761}
5762
5766static void
5767pm_multi_target_node_closing_set(const pm_parser_t *parser, pm_multi_target_node_t *node, const pm_token_t *rparen) {
5768 PM_NODE_LENGTH_SET_TOKEN(parser, node, rparen);
5769 node->rparen_loc = TOK2LOC(parser, rparen);
5770}
5771
5775static pm_multi_write_node_t *
5776pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, const pm_token_t *operator, pm_node_t *value) {
5777 /* The target is no longer necessary because we have reused its children. It
5778 * is arena-allocated so no explicit free is needed. */
5779 return pm_multi_write_node_new(
5780 parser->arena,
5781 ++parser->node_id,
5782 pm_implicit_array_write_flags(value, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY),
5783 PM_LOCATION_INIT_NODES(target, value),
5784 target->lefts,
5785 target->rest,
5786 target->rights,
5787 target->lparen_loc,
5788 target->rparen_loc,
5789 TOK2LOC(parser, operator),
5790 value
5791 );
5792}
5793
5797static pm_next_node_t *
5798pm_next_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
5799 assert(keyword->type == PM_TOKEN_KEYWORD_NEXT);
5800
5801 return pm_next_node_new(
5802 parser->arena,
5803 ++parser->node_id,
5804 0,
5805 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
5806 arguments,
5807 TOK2LOC(parser, keyword)
5808 );
5809}
5810
5814static pm_nil_node_t *
5815pm_nil_node_create(pm_parser_t *parser, const pm_token_t *token) {
5816 assert(token->type == PM_TOKEN_KEYWORD_NIL);
5817
5818 return pm_nil_node_new(
5819 parser->arena,
5820 ++parser->node_id,
5821 PM_NODE_FLAG_STATIC_LITERAL,
5822 PM_LOCATION_INIT_TOKEN(parser, token)
5823 );
5824}
5825
5829static pm_no_block_parameter_node_t *
5830pm_no_block_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
5831 assert(operator->type == PM_TOKEN_AMPERSAND || operator->type == PM_TOKEN_UAMPERSAND);
5832 assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
5833
5834 return pm_no_block_parameter_node_new(
5835 parser->arena,
5836 ++parser->node_id,
5837 0,
5838 PM_LOCATION_INIT_TOKENS(parser, operator, keyword),
5839 TOK2LOC(parser, operator),
5840 TOK2LOC(parser, keyword)
5841 );
5842}
5843
5847static pm_no_keywords_parameter_node_t *
5848pm_no_keywords_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *keyword) {
5849 assert(operator->type == PM_TOKEN_USTAR_STAR || operator->type == PM_TOKEN_STAR_STAR);
5850 assert(keyword->type == PM_TOKEN_KEYWORD_NIL);
5851
5852 return pm_no_keywords_parameter_node_new(
5853 parser->arena,
5854 ++parser->node_id,
5855 0,
5856 PM_LOCATION_INIT_TOKENS(parser, operator, keyword),
5857 TOK2LOC(parser, operator),
5858 TOK2LOC(parser, keyword)
5859 );
5860}
5861
5865static pm_numbered_parameters_node_t *
5866pm_numbered_parameters_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *closing, uint8_t maximum) {
5867 return pm_numbered_parameters_node_new(
5868 parser->arena,
5869 ++parser->node_id,
5870 0,
5871 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
5872 maximum
5873 );
5874}
5875
5880#define NTH_REF_MAX ((uint32_t) (INT_MAX >> 1))
5881
5888static uint32_t
5889pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *token) {
5890 const uint8_t *start = token->start + 1;
5891 const uint8_t *end = token->end;
5892
5893 ptrdiff_t diff = end - start;
5894 assert(diff > 0);
5895#if PTRDIFF_MAX > SIZE_MAX
5896 assert(diff < (ptrdiff_t) SIZE_MAX);
5897#endif
5898 size_t length = (size_t) diff;
5899
5900 char *digits = xcalloc(length + 1, sizeof(char));
5901 memcpy(digits, start, length);
5902 digits[length] = '\0';
5903
5904 char *endptr;
5905 errno = 0;
5906 unsigned long value = strtoul(digits, &endptr, 10);
5907
5908 if ((digits == endptr) || (*endptr != '\0')) {
5909 pm_parser_err(parser, U32(start - parser->start), U32(length), PM_ERR_INVALID_NUMBER_DECIMAL);
5910 value = 0;
5911 }
5912
5913 xfree_sized(digits, sizeof(char) * (length + 1));
5914
5915 if ((errno == ERANGE) || (value > NTH_REF_MAX)) {
5916 PM_PARSER_WARN_FORMAT(parser, U32(start - parser->start), U32(length), PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start);
5917 value = 0;
5918 }
5919
5920 return (uint32_t) value;
5921}
5922
5923#undef NTH_REF_MAX
5924
5928static pm_numbered_reference_read_node_t *
5929pm_numbered_reference_read_node_create(pm_parser_t *parser, const pm_token_t *name) {
5930 assert(name->type == PM_TOKEN_NUMBERED_REFERENCE);
5931
5932 return pm_numbered_reference_read_node_new(
5933 parser->arena,
5934 ++parser->node_id,
5935 0,
5936 PM_LOCATION_INIT_TOKEN(parser, name),
5937 pm_numbered_reference_read_node_number(parser, name)
5938 );
5939}
5940
5944static pm_optional_parameter_node_t *
5945pm_optional_parameter_node_create(pm_parser_t *parser, const pm_token_t *name, const pm_token_t *operator, pm_node_t *value) {
5946 return pm_optional_parameter_node_new(
5947 parser->arena,
5948 ++parser->node_id,
5949 0,
5950 PM_LOCATION_INIT_TOKEN_NODE(parser, name, value),
5951 pm_parser_constant_id_token(parser, name),
5952 TOK2LOC(parser, name),
5953 TOK2LOC(parser, operator),
5954 value
5955 );
5956}
5957
5961static pm_or_node_t *
5962pm_or_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
5963 pm_assert_value_expression(parser, left);
5964
5965 return pm_or_node_new(
5966 parser->arena,
5967 ++parser->node_id,
5968 0,
5969 PM_LOCATION_INIT_NODES(left, right),
5970 left,
5971 right,
5972 TOK2LOC(parser, operator)
5973 );
5974}
5975
5979static pm_parameters_node_t *
5980pm_parameters_node_create(pm_parser_t *parser) {
5981 return pm_parameters_node_new(
5982 parser->arena,
5983 ++parser->node_id,
5984 0,
5985 PM_LOCATION_INIT_UNSET,
5986 ((pm_node_list_t) { 0 }),
5987 ((pm_node_list_t) { 0 }),
5988 NULL,
5989 ((pm_node_list_t) { 0 }),
5990 ((pm_node_list_t) { 0 }),
5991 NULL,
5992 NULL
5993 );
5994}
5995
5999static void
6000pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param) {
6001 if ((params->base.location.length == 0) || PM_NODE_START(params) > PM_NODE_START(param)) {
6002 PM_NODE_START_SET_NODE(params, param);
6003 }
6004
6005 if ((params->base.location.length == 0) || (PM_NODE_END(params) < PM_NODE_END(param))) {
6006 PM_NODE_LENGTH_SET_NODE(params, param);
6007 }
6008}
6009
6013static void
6014pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6015 pm_parameters_node_location_set(params, param);
6016 pm_node_list_append(arena, &params->requireds, param);
6017}
6018
6022static void
6023pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_optional_parameter_node_t *param) {
6024 pm_parameters_node_location_set(params, UP(param));
6025 pm_node_list_append(arena, &params->optionals, UP(param));
6026}
6027
6031static void
6032pm_parameters_node_posts_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6033 pm_parameters_node_location_set(params, param);
6034 pm_node_list_append(arena, &params->posts, param);
6035}
6036
6040static void
6041pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
6042 pm_parameters_node_location_set(params, param);
6043 params->rest = param;
6044}
6045
6049static void
6050pm_parameters_node_keywords_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6051 pm_parameters_node_location_set(params, param);
6052 pm_node_list_append(arena, &params->keywords, param);
6053}
6054
6058static void
6059pm_parameters_node_keyword_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
6060 assert(params->keyword_rest == NULL);
6061 pm_parameters_node_location_set(params, param);
6062 params->keyword_rest = param;
6063}
6064
6068static void
6069pm_parameters_node_block_set(pm_parameters_node_t *params, pm_node_t *param) {
6070 assert(params->block == NULL);
6071 pm_parameters_node_location_set(params, param);
6072 params->block = param;
6073}
6074
6078static pm_program_node_t *
6079pm_program_node_create(pm_parser_t *parser, pm_constant_id_list_t *locals, pm_statements_node_t *statements) {
6080 return pm_program_node_new(
6081 parser->arena,
6082 ++parser->node_id,
6083 0,
6084 PM_LOCATION_INIT_NODE(statements),
6085 *locals,
6086 statements
6087 );
6088}
6089
6093static pm_parentheses_node_t *
6094pm_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) {
6095 return pm_parentheses_node_new(
6096 parser->arena,
6097 ++parser->node_id,
6098 flags,
6099 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6100 body,
6101 TOK2LOC(parser, opening),
6102 TOK2LOC(parser, closing)
6103 );
6104}
6105
6109static pm_pinned_expression_node_t *
6110pm_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) {
6111 return pm_pinned_expression_node_new(
6112 parser->arena,
6113 ++parser->node_id,
6114 0,
6115 PM_LOCATION_INIT_TOKENS(parser, operator, rparen),
6116 expression,
6117 TOK2LOC(parser, operator),
6118 TOK2LOC(parser, lparen),
6119 TOK2LOC(parser, rparen)
6120 );
6121}
6122
6126static pm_pinned_variable_node_t *
6127pm_pinned_variable_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *variable) {
6128 return pm_pinned_variable_node_new(
6129 parser->arena,
6130 ++parser->node_id,
6131 0,
6132 PM_LOCATION_INIT_TOKEN_NODE(parser, operator, variable),
6133 variable,
6134 TOK2LOC(parser, operator)
6135 );
6136}
6137
6141static pm_post_execution_node_t *
6142pm_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) {
6143 return pm_post_execution_node_new(
6144 parser->arena,
6145 ++parser->node_id,
6146 0,
6147 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
6148 statements,
6149 TOK2LOC(parser, keyword),
6150 TOK2LOC(parser, opening),
6151 TOK2LOC(parser, closing)
6152 );
6153}
6154
6158static pm_pre_execution_node_t *
6159pm_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) {
6160 return pm_pre_execution_node_new(
6161 parser->arena,
6162 ++parser->node_id,
6163 0,
6164 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
6165 statements,
6166 TOK2LOC(parser, keyword),
6167 TOK2LOC(parser, opening),
6168 TOK2LOC(parser, closing)
6169 );
6170}
6171
6175static pm_range_node_t *
6176pm_range_node_create(pm_parser_t *parser, pm_node_t *left, const pm_token_t *operator, pm_node_t *right) {
6177 pm_assert_value_expression(parser, left);
6178 pm_assert_value_expression(parser, right);
6179 pm_node_flags_t flags = 0;
6180
6181 // Indicate that this node is an exclusive range if the operator is `...`.
6182 if (operator->type == PM_TOKEN_DOT_DOT_DOT || operator->type == PM_TOKEN_UDOT_DOT_DOT) {
6183 flags |= PM_RANGE_FLAGS_EXCLUDE_END;
6184 }
6185
6186 // Indicate that this node is a static literal (i.e., can be compiled with
6187 // a putobject in CRuby) if the left and right are implicit nil, explicit
6188 // nil, or integers.
6189 if (
6190 (left == NULL || PM_NODE_TYPE_P(left, PM_NIL_NODE) || PM_NODE_TYPE_P(left, PM_INTEGER_NODE)) &&
6191 (right == NULL || PM_NODE_TYPE_P(right, PM_NIL_NODE) || PM_NODE_TYPE_P(right, PM_INTEGER_NODE))
6192 ) {
6193 flags |= PM_NODE_FLAG_STATIC_LITERAL;
6194 }
6195
6196 uint32_t start = left == NULL ? PM_TOKEN_START(parser, operator) : PM_NODE_START(left);
6197 uint32_t end = right == NULL ? PM_TOKEN_END(parser, operator) : PM_NODE_END(right);
6198
6199 return pm_range_node_new(
6200 parser->arena,
6201 ++parser->node_id,
6202 flags,
6203 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6204 left,
6205 right,
6206 TOK2LOC(parser, operator)
6207 );
6208}
6209
6213static pm_redo_node_t *
6214pm_redo_node_create(pm_parser_t *parser, const pm_token_t *token) {
6215 assert(token->type == PM_TOKEN_KEYWORD_REDO);
6216
6217 return pm_redo_node_new(
6218 parser->arena,
6219 ++parser->node_id,
6220 0,
6221 PM_LOCATION_INIT_TOKEN(parser, token)
6222 );
6223}
6224
6229static pm_regular_expression_node_t *
6230pm_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) {
6231 return pm_regular_expression_node_new(
6232 parser->arena,
6233 ++parser->node_id,
6234 pm_regular_expression_flags_create(parser, closing) | PM_NODE_FLAG_STATIC_LITERAL,
6235 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6236 TOK2LOC(parser, opening),
6237 TOK2LOC(parser, content),
6238 TOK2LOC(parser, closing),
6239 *unescaped
6240 );
6241}
6242
6246static PRISM_INLINE pm_regular_expression_node_t *
6247pm_regular_expression_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6248 return pm_regular_expression_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6249}
6250
6254static pm_required_parameter_node_t *
6255pm_required_parameter_node_create(pm_parser_t *parser, const pm_token_t *token) {
6256 return pm_required_parameter_node_new(
6257 parser->arena,
6258 ++parser->node_id,
6259 0,
6260 PM_LOCATION_INIT_TOKEN(parser, token),
6261 pm_parser_constant_id_token(parser, token)
6262 );
6263}
6264
6268static pm_rescue_modifier_node_t *
6269pm_rescue_modifier_node_create(pm_parser_t *parser, pm_node_t *expression, const pm_token_t *keyword, pm_node_t *rescue_expression) {
6270 return pm_rescue_modifier_node_new(
6271 parser->arena,
6272 ++parser->node_id,
6273 0,
6274 PM_LOCATION_INIT_NODES(expression, rescue_expression),
6275 expression,
6276 TOK2LOC(parser, keyword),
6277 rescue_expression
6278 );
6279}
6280
6284static pm_rescue_node_t *
6285pm_rescue_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
6286 return pm_rescue_node_new(
6287 parser->arena,
6288 ++parser->node_id,
6289 0,
6290 PM_LOCATION_INIT_TOKEN(parser, keyword),
6291 TOK2LOC(parser, keyword),
6292 ((pm_node_list_t) { 0 }),
6293 ((pm_location_t) { 0 }),
6294 NULL,
6295 ((pm_location_t) { 0 }),
6296 NULL,
6297 NULL
6298 );
6299}
6300
6301static PRISM_INLINE void
6302pm_rescue_node_operator_set(const pm_parser_t *parser, pm_rescue_node_t *node, const pm_token_t *operator) {
6303 node->operator_loc = TOK2LOC(parser, operator);
6304}
6305
6309static void
6310pm_rescue_node_reference_set(pm_rescue_node_t *node, pm_node_t *reference) {
6311 node->reference = reference;
6312 PM_NODE_LENGTH_SET_NODE(node, reference);
6313}
6314
6318static void
6319pm_rescue_node_statements_set(pm_rescue_node_t *node, pm_statements_node_t *statements) {
6320 node->statements = statements;
6321 if (pm_statements_node_body_length(statements) > 0) {
6322 PM_NODE_LENGTH_SET_NODE(node, statements);
6323 }
6324}
6325
6329static void
6330pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subsequent) {
6331 node->subsequent = subsequent;
6332 PM_NODE_LENGTH_SET_NODE(node, subsequent);
6333}
6334
6338static void
6339pm_rescue_node_exceptions_append(pm_arena_t *arena, pm_rescue_node_t *node, pm_node_t *exception) {
6340 pm_node_list_append(arena, &node->exceptions, exception);
6341 PM_NODE_LENGTH_SET_NODE(node, exception);
6342}
6343
6347static pm_rest_parameter_node_t *
6348pm_rest_parameter_node_create(pm_parser_t *parser, const pm_token_t *operator, const pm_token_t *name) {
6349 return pm_rest_parameter_node_new(
6350 parser->arena,
6351 ++parser->node_id,
6352 0,
6353 (name == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKENS(parser, operator, name),
6354 name == NULL ? 0 : pm_parser_constant_id_token(parser, name),
6355 NTOK2LOC(parser, name),
6356 TOK2LOC(parser, operator)
6357 );
6358}
6359
6363static pm_retry_node_t *
6364pm_retry_node_create(pm_parser_t *parser, const pm_token_t *token) {
6365 assert(token->type == PM_TOKEN_KEYWORD_RETRY);
6366
6367 return pm_retry_node_new(
6368 parser->arena,
6369 ++parser->node_id,
6370 0,
6371 PM_LOCATION_INIT_TOKEN(parser, token)
6372 );
6373}
6374
6378static pm_return_node_t *
6379pm_return_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_node_t *arguments) {
6380 return pm_return_node_new(
6381 parser->arena,
6382 ++parser->node_id,
6383 0,
6384 (arguments == NULL) ? PM_LOCATION_INIT_TOKEN(parser, keyword) : PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, arguments),
6385 TOK2LOC(parser, keyword),
6386 arguments
6387 );
6388}
6389
6393static pm_self_node_t *
6394pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) {
6395 assert(token->type == PM_TOKEN_KEYWORD_SELF);
6396
6397 return pm_self_node_new(
6398 parser->arena,
6399 ++parser->node_id,
6400 0,
6401 PM_LOCATION_INIT_TOKEN(parser, token)
6402 );
6403}
6404
6408static pm_shareable_constant_node_t *
6409pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shareable_constant_value_t value) {
6410 return pm_shareable_constant_node_new(
6411 parser->arena,
6412 ++parser->node_id,
6413 (pm_node_flags_t) value,
6414 PM_LOCATION_INIT_NODE(write),
6415 write
6416 );
6417}
6418
6422static pm_singleton_class_node_t *
6423pm_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) {
6424 return pm_singleton_class_node_new(
6425 parser->arena,
6426 ++parser->node_id,
6427 0,
6428 PM_LOCATION_INIT_TOKENS(parser, class_keyword, end_keyword),
6429 *locals,
6430 TOK2LOC(parser, class_keyword),
6431 TOK2LOC(parser, operator),
6432 expression,
6433 body,
6434 TOK2LOC(parser, end_keyword)
6435 );
6436}
6437
6441static pm_source_encoding_node_t *
6442pm_source_encoding_node_create(pm_parser_t *parser, const pm_token_t *token) {
6443 assert(token->type == PM_TOKEN_KEYWORD___ENCODING__);
6444
6445 return pm_source_encoding_node_new(
6446 parser->arena,
6447 ++parser->node_id,
6448 PM_NODE_FLAG_STATIC_LITERAL,
6449 PM_LOCATION_INIT_TOKEN(parser, token)
6450 );
6451}
6452
6456static pm_source_file_node_t*
6457pm_source_file_node_create(pm_parser_t *parser, const pm_token_t *file_keyword) {
6458 assert(file_keyword->type == PM_TOKEN_KEYWORD___FILE__);
6459
6460 pm_node_flags_t flags = 0;
6461
6462 switch (parser->frozen_string_literal) {
6463 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6464 flags |= PM_STRING_FLAGS_MUTABLE;
6465 break;
6466 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6467 flags |= PM_STRING_FLAGS_FROZEN;
6468 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
6469 flags |= PM_NODE_FLAG_STATIC_LITERAL;
6470 }
6471 break;
6472 }
6473
6474 return pm_source_file_node_new(
6475 parser->arena,
6476 ++parser->node_id,
6477 flags,
6478 PM_LOCATION_INIT_TOKEN(parser, file_keyword),
6479 parser->filepath
6480 );
6481}
6482
6486static pm_source_line_node_t *
6487pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) {
6488 assert(token->type == PM_TOKEN_KEYWORD___LINE__);
6489
6490 return pm_source_line_node_new(
6491 parser->arena,
6492 ++parser->node_id,
6493 PM_NODE_FLAG_STATIC_LITERAL,
6494 PM_LOCATION_INIT_TOKEN(parser, token)
6495 );
6496}
6497
6501static pm_splat_node_t *
6502pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
6503 return pm_splat_node_new(
6504 parser->arena,
6505 ++parser->node_id,
6506 0,
6507 (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression),
6508 TOK2LOC(parser, operator),
6509 expression
6510 );
6511}
6512
6516static pm_statements_node_t *
6517pm_statements_node_create(pm_parser_t *parser) {
6518 return pm_statements_node_new(
6519 parser->arena,
6520 ++parser->node_id,
6521 0,
6522 PM_LOCATION_INIT_UNSET,
6523 ((pm_node_list_t) { 0 })
6524 );
6525}
6526
6530static size_t
6531pm_statements_node_body_length(pm_statements_node_t *node) {
6532 return node && node->body.size;
6533}
6534
6539static PRISM_INLINE void
6540pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement) {
6541 if (pm_statements_node_body_length(node) == 0 || PM_NODE_START(statement) < PM_NODE_START(node)) {
6542 PM_NODE_START_SET_NODE(node, statement);
6543 }
6544
6545 if (PM_NODE_END(statement) > PM_NODE_END(node)) {
6546 PM_NODE_LENGTH_SET_NODE(node, statement);
6547 }
6548}
6549
6553static void
6554pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline) {
6555 pm_statements_node_body_update(node, statement);
6556
6557 if (node->body.size > 0) {
6558 const pm_node_t *previous = node->body.nodes[node->body.size - 1];
6559
6560 switch (PM_NODE_TYPE(previous)) {
6561 case PM_BREAK_NODE:
6562 case PM_NEXT_NODE:
6563 case PM_REDO_NODE:
6564 case PM_RETRY_NODE:
6565 case PM_RETURN_NODE:
6566 pm_parser_warn_node(parser, statement, PM_WARN_UNREACHABLE_STATEMENT);
6567 break;
6568 default:
6569 break;
6570 }
6571 }
6572
6573 pm_node_list_append(parser->arena, &node->body, statement);
6574 if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6575}
6576
6580static void
6581pm_statements_node_body_prepend(pm_arena_t *arena, pm_statements_node_t *node, pm_node_t *statement) {
6582 pm_statements_node_body_update(node, statement);
6583 pm_node_list_prepend(arena, &node->body, statement);
6584 pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6585}
6586
6590static PRISM_INLINE pm_string_node_t *
6591pm_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) {
6592 pm_node_flags_t flags = 0;
6593
6594 switch (parser->frozen_string_literal) {
6595 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6596 flags = PM_STRING_FLAGS_MUTABLE;
6597 break;
6598 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6599 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6600 break;
6601 }
6602
6603 uint32_t start = PM_TOKEN_START(parser, opening == NULL ? content : opening);
6604 uint32_t end = PM_TOKEN_END(parser, closing == NULL ? content : closing);
6605
6606 return pm_string_node_new(
6607 parser->arena,
6608 ++parser->node_id,
6609 flags,
6610 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6611 NTOK2LOC(parser, opening),
6612 TOK2LOC(parser, content),
6613 NTOK2LOC(parser, closing),
6614 *string
6615 );
6616}
6617
6621static pm_string_node_t *
6622pm_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6623 return pm_string_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6624}
6625
6630static pm_string_node_t *
6631pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6632 pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, &parser->current_string);
6633 parser->current_string = PM_STRING_EMPTY;
6634 return node;
6635}
6636
6640static pm_super_node_t *
6641pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_t *arguments) {
6642 assert(keyword->type == PM_TOKEN_KEYWORD_SUPER);
6643
6644 const pm_location_t *end = pm_arguments_end(arguments);
6645 assert(end != NULL && "unreachable");
6646
6647 return pm_super_node_new(
6648 parser->arena,
6649 ++parser->node_id,
6650 0,
6651 ((pm_location_t) { .start = PM_TOKEN_START(parser, keyword), .length = PM_LOCATION_END(end) - PM_TOKEN_START(parser, keyword) }),
6652 TOK2LOC(parser, keyword),
6653 arguments->opening_loc,
6654 arguments->arguments,
6655 arguments->closing_loc,
6656 arguments->block
6657 );
6658}
6659
6664static bool
6665pm_ascii_only_p(const pm_string_t *contents) {
6666 const size_t length = pm_string_length(contents);
6667 const uint8_t *source = pm_string_source(contents);
6668
6669 for (size_t index = 0; index < length; index++) {
6670 if (source[index] & 0x80) return false;
6671 }
6672
6673 return true;
6674}
6675
6679static void
6680parse_symbol_encoding_validate_utf8(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6681 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6682 size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor);
6683
6684 if (width == 0) {
6685 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6686 break;
6687 }
6688
6689 cursor += width;
6690 }
6691}
6692
6697static void
6698parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6699 const pm_encoding_t *encoding = parser->encoding;
6700
6701 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6702 size_t width = encoding->char_width(cursor, end - cursor);
6703
6704 if (width == 0) {
6705 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6706 break;
6707 }
6708
6709 cursor += width;
6710 }
6711}
6712
6722static PRISM_INLINE pm_node_flags_t
6723parse_symbol_encoding(pm_parser_t *parser, const pm_encoding_t *explicit_encoding, const pm_token_t *location, const pm_string_t *contents, bool validate) {
6724 if (explicit_encoding != NULL) {
6725 // A Symbol may optionally have its encoding explicitly set. This will
6726 // happen if an escape sequence results in a non-ASCII code point.
6727 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
6728 if (validate) parse_symbol_encoding_validate_utf8(parser, location, contents);
6729 return PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
6730 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
6731 return PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING;
6732 } else if (validate) {
6733 parse_symbol_encoding_validate_other(parser, location, contents);
6734 }
6735 } else if (pm_ascii_only_p(contents)) {
6736 // Ruby stipulates that all source files must use an ASCII-compatible
6737 // encoding. Thus, all symbols appearing in source are eligible for
6738 // "downgrading" to US-ASCII.
6739 return PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
6740 } else if (validate) {
6741 parse_symbol_encoding_validate_other(parser, location, contents);
6742 }
6743
6744 return 0;
6745}
6746
6751static pm_symbol_node_t *
6752pm_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) {
6753 uint32_t start = opening == NULL ? PM_TOKEN_START(parser, value) : PM_TOKEN_START(parser, opening);
6754 uint32_t end = closing == NULL ? PM_TOKEN_END(parser, value) : PM_TOKEN_END(parser, closing);
6755
6756 return pm_symbol_node_new(
6757 parser->arena,
6758 ++parser->node_id,
6759 PM_NODE_FLAG_STATIC_LITERAL | flags,
6760 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6761 NTOK2LOC(parser, opening),
6762 NTOK2LOC(parser, value),
6763 NTOK2LOC(parser, closing),
6764 *unescaped
6765 );
6766}
6767
6771static PRISM_INLINE pm_symbol_node_t *
6772pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6773 return pm_symbol_node_create_unescaped(parser, opening, value, closing, &PM_STRING_EMPTY, 0);
6774}
6775
6779static pm_symbol_node_t *
6780pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6781 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));
6782 parser->current_string = PM_STRING_EMPTY;
6783 return node;
6784}
6785
6789static pm_symbol_node_t *
6790pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) {
6791 assert(token->type == PM_TOKEN_LABEL);
6792
6793 pm_token_t closing = { .type = PM_TOKEN_LABEL_END, .start = token->end - 1, .end = token->end };
6794 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end - 1 };
6795 pm_symbol_node_t *node = pm_symbol_node_create(parser, NULL, &label, &closing);
6796
6797 assert((label.end - label.start) >= 0);
6798 pm_string_shared_init(&node->unescaped, label.start, label.end);
6799 pm_node_flag_set(UP(node), parse_symbol_encoding(parser, parser->explicit_encoding, &label, &node->unescaped, false));
6800
6801 return node;
6802}
6803
6807static pm_symbol_node_t *
6808pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
6809 pm_symbol_node_t *node = pm_symbol_node_new(
6810 parser->arena,
6811 ++parser->node_id,
6812 PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING,
6813 PM_LOCATION_INIT_UNSET,
6814 ((pm_location_t) { 0 }),
6815 ((pm_location_t) { 0 }),
6816 ((pm_location_t) { 0 }),
6817 ((pm_string_t) { 0 })
6818 );
6819
6820 pm_string_constant_init(&node->unescaped, content, strlen(content));
6821 return node;
6822}
6823
6827static bool
6828pm_symbol_node_label_p(const pm_parser_t *parser, const pm_node_t *node) {
6829 const pm_location_t *location = NULL;
6830
6831 switch (PM_NODE_TYPE(node)) {
6832 case PM_SYMBOL_NODE: {
6833 const pm_symbol_node_t *cast = (pm_symbol_node_t *) node;
6834 if (cast->closing_loc.length > 0) {
6835 location = &cast->closing_loc;
6836 }
6837 break;
6838 }
6839 case PM_INTERPOLATED_SYMBOL_NODE: {
6840 const pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node;
6841 if (cast->closing_loc.length > 0) {
6842 location = &cast->closing_loc;
6843 }
6844 break;
6845 }
6846 default:
6847 return false;
6848 }
6849
6850 return (location != NULL) && (parser->start[PM_LOCATION_END(location) - 1] == ':');
6851}
6852
6856static pm_symbol_node_t *
6857pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t *opening, const pm_token_t *closing) {
6858 pm_symbol_node_t *new_node = pm_symbol_node_new(
6859 parser->arena,
6860 ++parser->node_id,
6861 PM_NODE_FLAG_STATIC_LITERAL,
6862 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6863 TOK2LOC(parser, opening),
6864 node->content_loc,
6865 TOK2LOC(parser, closing),
6866 node->unescaped
6867 );
6868
6869 pm_token_t content = {
6870 .type = PM_TOKEN_IDENTIFIER,
6871 .start = parser->start + node->content_loc.start,
6872 .end = parser->start + node->content_loc.start + node->content_loc.length
6873 };
6874
6875 pm_node_flag_set(UP(new_node), parse_symbol_encoding(parser, parser->explicit_encoding, &content, &node->unescaped, true));
6876
6877 /* The old node is arena-allocated so no explicit free is needed. */
6878 return new_node;
6879}
6880
6884static pm_string_node_t *
6885pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) {
6886 pm_node_flags_t flags = 0;
6887
6888 switch (parser->frozen_string_literal) {
6889 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6890 flags = PM_STRING_FLAGS_MUTABLE;
6891 break;
6892 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6893 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6894 break;
6895 }
6896
6897 pm_string_node_t *new_node = pm_string_node_new(
6898 parser->arena,
6899 ++parser->node_id,
6900 flags,
6901 PM_LOCATION_INIT_NODE(node),
6902 node->opening_loc,
6903 node->value_loc,
6904 node->closing_loc,
6905 node->unescaped
6906 );
6907
6908 /* The old node is arena-allocated so no explicit free is needed. */
6909 return new_node;
6910}
6911
6915static pm_true_node_t *
6916pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) {
6917 assert(token->type == PM_TOKEN_KEYWORD_TRUE);
6918
6919 return pm_true_node_new(
6920 parser->arena,
6921 ++parser->node_id,
6922 PM_NODE_FLAG_STATIC_LITERAL,
6923 PM_LOCATION_INIT_TOKEN(parser, token)
6924 );
6925}
6926
6930static pm_true_node_t *
6931pm_true_node_synthesized_create(pm_parser_t *parser) {
6932 return pm_true_node_new(
6933 parser->arena,
6934 ++parser->node_id,
6935 PM_NODE_FLAG_STATIC_LITERAL,
6936 PM_LOCATION_INIT_UNSET
6937 );
6938}
6939
6943static pm_undef_node_t *
6944pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
6945 assert(token->type == PM_TOKEN_KEYWORD_UNDEF);
6946
6947 return pm_undef_node_new(
6948 parser->arena,
6949 ++parser->node_id,
6950 0,
6951 PM_LOCATION_INIT_TOKEN(parser, token),
6952 ((pm_node_list_t) { 0 }),
6953 TOK2LOC(parser, token)
6954 );
6955}
6956
6960static void
6961pm_undef_node_append(pm_arena_t *arena, pm_undef_node_t *node, pm_node_t *name) {
6962 PM_NODE_LENGTH_SET_NODE(node, name);
6963 pm_node_list_append(arena, &node->names, name);
6964}
6965
6969static pm_unless_node_t *
6970pm_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) {
6971 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6972 pm_node_t *end = statements == NULL ? predicate : UP(statements);
6973
6974 return pm_unless_node_new(
6975 parser->arena,
6976 ++parser->node_id,
6977 PM_NODE_FLAG_NEWLINE,
6978 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, end),
6979 TOK2LOC(parser, keyword),
6980 predicate,
6981 NTOK2LOC(parser, then_keyword),
6982 statements,
6983 NULL,
6984 ((pm_location_t) { 0 })
6985 );
6986}
6987
6991static pm_unless_node_t *
6992pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) {
6993 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6994
6995 pm_statements_node_t *statements = pm_statements_node_create(parser);
6996 pm_statements_node_body_append(parser, statements, statement, true);
6997
6998 return pm_unless_node_new(
6999 parser->arena,
7000 ++parser->node_id,
7001 PM_NODE_FLAG_NEWLINE,
7002 PM_LOCATION_INIT_NODES(statement, predicate),
7003 TOK2LOC(parser, unless_keyword),
7004 predicate,
7005 ((pm_location_t) { 0 }),
7006 statements,
7007 NULL,
7008 ((pm_location_t) { 0 })
7009 );
7010}
7011
7012static PRISM_INLINE void
7013pm_unless_node_end_keyword_loc_set(const pm_parser_t *parser, pm_unless_node_t *node, const pm_token_t *end_keyword) {
7014 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
7015 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
7016}
7017
7023static void
7024pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statements) {
7025 assert(parser->current_block_exits != NULL);
7026
7027 // All of the block exits that we want to remove should be within the
7028 // statements, and since we are modifying the statements, we shouldn't have
7029 // to check the end location.
7030 uint32_t start = statements->base.location.start;
7031
7032 for (size_t index = parser->current_block_exits->size; index > 0; index--) {
7033 pm_node_t *block_exit = parser->current_block_exits->nodes[index - 1];
7034 if (block_exit->location.start < start) break;
7035
7036 // Implicitly remove from the list by lowering the size.
7037 parser->current_block_exits->size--;
7038 }
7039}
7040
7044static pm_until_node_t *
7045pm_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) {
7046 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7047
7048 return pm_until_node_new(
7049 parser->arena,
7050 ++parser->node_id,
7051 flags,
7052 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7053 TOK2LOC(parser, keyword),
7054 NTOK2LOC(parser, do_keyword),
7055 TOK2LOC(parser, closing),
7056 predicate,
7057 statements
7058 );
7059}
7060
7064static pm_until_node_t *
7065pm_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) {
7066 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7067 pm_loop_modifier_block_exits(parser, statements);
7068
7069 return pm_until_node_new(
7070 parser->arena,
7071 ++parser->node_id,
7072 flags,
7073 PM_LOCATION_INIT_NODES(statements, predicate),
7074 TOK2LOC(parser, keyword),
7075 ((pm_location_t) { 0 }),
7076 ((pm_location_t) { 0 }),
7077 predicate,
7078 statements
7079 );
7080}
7081
7085static pm_when_node_t *
7086pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
7087 return pm_when_node_new(
7088 parser->arena,
7089 ++parser->node_id,
7090 0,
7091 PM_LOCATION_INIT_TOKEN(parser, keyword),
7092 TOK2LOC(parser, keyword),
7093 ((pm_node_list_t) { 0 }),
7094 ((pm_location_t) { 0 }),
7095 NULL
7096 );
7097}
7098
7102static void
7103pm_when_node_conditions_append(pm_arena_t *arena, pm_when_node_t *node, pm_node_t *condition) {
7104 PM_NODE_LENGTH_SET_NODE(node, condition);
7105 pm_node_list_append(arena, &node->conditions, condition);
7106}
7107
7111static PRISM_INLINE void
7112pm_when_node_then_keyword_loc_set(const pm_parser_t *parser, pm_when_node_t *node, const pm_token_t *then_keyword) {
7113 PM_NODE_LENGTH_SET_TOKEN(parser, node, then_keyword);
7114 node->then_keyword_loc = TOK2LOC(parser, then_keyword);
7115}
7116
7120static void
7121pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statements) {
7122 if (PM_NODE_END(statements) > PM_NODE_END(node)) {
7123 PM_NODE_LENGTH_SET_NODE(node, statements);
7124 }
7125
7126 node->statements = statements;
7127}
7128
7132static pm_while_node_t *
7133pm_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) {
7134 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7135
7136 return pm_while_node_new(
7137 parser->arena,
7138 ++parser->node_id,
7139 flags,
7140 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7141 TOK2LOC(parser, keyword),
7142 NTOK2LOC(parser, do_keyword),
7143 TOK2LOC(parser, closing),
7144 predicate,
7145 statements
7146 );
7147}
7148
7152static pm_while_node_t *
7153pm_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) {
7154 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7155 pm_loop_modifier_block_exits(parser, statements);
7156
7157 return pm_while_node_new(
7158 parser->arena,
7159 ++parser->node_id,
7160 flags,
7161 PM_LOCATION_INIT_NODES(statements, predicate),
7162 TOK2LOC(parser, keyword),
7163 ((pm_location_t) { 0 }),
7164 ((pm_location_t) { 0 }),
7165 predicate,
7166 statements
7167 );
7168}
7169
7173static pm_while_node_t *
7174pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_statements_node_t *statements) {
7175 return pm_while_node_new(
7176 parser->arena,
7177 ++parser->node_id,
7178 0,
7179 PM_LOCATION_INIT_UNSET,
7180 ((pm_location_t) { 0 }),
7181 ((pm_location_t) { 0 }),
7182 ((pm_location_t) { 0 }),
7183 predicate,
7184 statements
7185 );
7186}
7187
7192static pm_x_string_node_t *
7193pm_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) {
7194 return pm_x_string_node_new(
7195 parser->arena,
7196 ++parser->node_id,
7197 PM_STRING_FLAGS_FROZEN,
7198 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
7199 TOK2LOC(parser, opening),
7200 TOK2LOC(parser, content),
7201 TOK2LOC(parser, closing),
7202 *unescaped
7203 );
7204}
7205
7209static PRISM_INLINE pm_x_string_node_t *
7210pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
7211 return pm_xstring_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
7212}
7213
7217static pm_yield_node_t *
7218pm_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) {
7219 uint32_t start = PM_TOKEN_START(parser, keyword);
7220 uint32_t end;
7221
7222 if (rparen_loc->length > 0) {
7223 end = PM_LOCATION_END(rparen_loc);
7224 } else if (arguments != NULL) {
7225 end = PM_NODE_END(arguments);
7226 } else if (lparen_loc->length > 0) {
7227 end = PM_LOCATION_END(lparen_loc);
7228 } else {
7229 end = PM_TOKEN_END(parser, keyword);
7230 }
7231
7232 return pm_yield_node_new(
7233 parser->arena,
7234 ++parser->node_id,
7235 0,
7236 ((pm_location_t) { .start = start, .length = U32(end - start) }),
7237 TOK2LOC(parser, keyword),
7238 *lparen_loc,
7239 arguments,
7240 *rparen_loc
7241 );
7242}
7243
7248static int
7249pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant_id) {
7250 pm_scope_t *scope = parser->current_scope;
7251 int depth = 0;
7252
7253 while (scope != NULL) {
7254 if (pm_locals_find(&scope->locals, constant_id) != UINT32_MAX) return depth;
7255 if (scope->closed) break;
7256
7257 scope = scope->previous;
7258 depth++;
7259 }
7260
7261 return -1;
7262}
7263
7269static PRISM_INLINE int
7270pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
7271 return pm_parser_local_depth_constant_id(parser, pm_parser_constant_id_token(parser, token));
7272}
7273
7277static PRISM_INLINE void
7278pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7279 pm_locals_write(&parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
7280}
7281
7285static pm_constant_id_t
7286pm_parser_local_add_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7287 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
7288 if (constant_id != 0) pm_parser_local_add(parser, constant_id, start, end, reads);
7289 return constant_id;
7290}
7291
7295static PRISM_INLINE pm_constant_id_t
7296pm_parser_local_add_location(pm_parser_t *parser, pm_location_t *location, uint32_t reads) {
7297 return pm_parser_local_add_raw(parser, parser->start + location->start, parser->start + location->start + location->length, reads);
7298}
7299
7303static PRISM_INLINE pm_constant_id_t
7304pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token, uint32_t reads) {
7305 return pm_parser_local_add_raw(parser, token->start, token->end, reads);
7306}
7307
7311static pm_constant_id_t
7312pm_parser_local_add_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
7313 pm_constant_id_t constant_id = pm_parser_constant_id_owned(parser, start, length);
7314 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7315 return constant_id;
7316}
7317
7321static pm_constant_id_t
7322pm_parser_local_add_constant(pm_parser_t *parser, const char *start, size_t length) {
7323 pm_constant_id_t constant_id = pm_parser_constant_id_constant(parser, start, length);
7324 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7325 return constant_id;
7326}
7327
7335static bool
7336pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) {
7337 // We want to check whether the parameter name is a numbered parameter or
7338 // not.
7339 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, name), PM_TOKEN_LENGTH(name));
7340
7341 // Otherwise we'll fetch the constant id for the parameter name and check
7342 // whether it's already in the current scope.
7343 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name);
7344
7345 if (pm_locals_find(&parser->current_scope->locals, constant_id) != UINT32_MAX) {
7346 // Add an error if the parameter doesn't start with _ and has been seen before
7347 if ((name->start < name->end) && (*name->start != '_')) {
7348 pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_DUPLICATED);
7349 }
7350 return true;
7351 }
7352 return false;
7353}
7354
7358static void
7359pm_parser_scope_pop(pm_parser_t *parser) {
7360 pm_scope_t *scope = parser->current_scope;
7361 parser->current_scope = scope->previous;
7362 pm_locals_free(&scope->locals);
7363 xfree_sized(scope, sizeof(pm_scope_t));
7364}
7365
7366/******************************************************************************/
7367/* Stack helpers */
7368/******************************************************************************/
7369
7373static PRISM_INLINE void
7374pm_state_stack_push(pm_state_stack_t *stack, bool value) {
7375 *stack = (*stack << 1) | (value & 1);
7376}
7377
7381static PRISM_INLINE void
7382pm_state_stack_pop(pm_state_stack_t *stack) {
7383 *stack >>= 1;
7384}
7385
7389static PRISM_INLINE bool
7390pm_state_stack_p(const pm_state_stack_t *stack) {
7391 return *stack & 1;
7392}
7393
7394static PRISM_INLINE void
7395pm_accepts_block_stack_push(pm_parser_t *parser, bool value) {
7396 // Use the negation of the value to prevent stack overflow.
7397 pm_state_stack_push(&parser->accepts_block_stack, !value);
7398}
7399
7400static PRISM_INLINE void
7401pm_accepts_block_stack_pop(pm_parser_t *parser) {
7402 pm_state_stack_pop(&parser->accepts_block_stack);
7403}
7404
7405static PRISM_INLINE bool
7406pm_accepts_block_stack_p(pm_parser_t *parser) {
7407 return !pm_state_stack_p(&parser->accepts_block_stack);
7408}
7409
7410static PRISM_INLINE void
7411pm_do_loop_stack_push(pm_parser_t *parser, bool value) {
7412 pm_state_stack_push(&parser->do_loop_stack, value);
7413}
7414
7415static PRISM_INLINE void
7416pm_do_loop_stack_pop(pm_parser_t *parser) {
7417 pm_state_stack_pop(&parser->do_loop_stack);
7418}
7419
7420static PRISM_INLINE bool
7421pm_do_loop_stack_p(pm_parser_t *parser) {
7422 return pm_state_stack_p(&parser->do_loop_stack);
7423}
7424
7449static PRISM_INLINE void
7450pm_enclosure_frame_push(pm_parser_t *parser) {
7451 pm_do_loop_stack_push(parser, false);
7452 pm_accepts_block_stack_push(parser, true);
7453}
7454
7455static PRISM_INLINE void
7456pm_enclosure_frame_pop(pm_parser_t *parser) {
7457 pm_do_loop_stack_pop(parser);
7458 pm_accepts_block_stack_pop(parser);
7459}
7460
7461/******************************************************************************/
7462/* Lexer check helpers */
7463/******************************************************************************/
7464
7469static PRISM_INLINE uint8_t
7470peek_at(const pm_parser_t *parser, const uint8_t *cursor) {
7471 if (cursor < parser->end) {
7472 return *cursor;
7473 } else {
7474 return '\0';
7475 }
7476}
7477
7483static PRISM_INLINE uint8_t
7484peek_offset(pm_parser_t *parser, ptrdiff_t offset) {
7485 return peek_at(parser, parser->current.end + offset);
7486}
7487
7492static PRISM_INLINE uint8_t
7493peek(const pm_parser_t *parser) {
7494 return peek_at(parser, parser->current.end);
7495}
7496
7501static PRISM_INLINE bool
7502match(pm_parser_t *parser, uint8_t value) {
7503 if (peek(parser) == value) {
7504 parser->current.end++;
7505 return true;
7506 }
7507 return false;
7508}
7509
7514static PRISM_INLINE size_t
7515match_eol_at(pm_parser_t *parser, const uint8_t *cursor) {
7516 if (peek_at(parser, cursor) == '\n') {
7517 return 1;
7518 }
7519 if (peek_at(parser, cursor) == '\r' && peek_at(parser, cursor + 1) == '\n') {
7520 return 2;
7521 }
7522 return 0;
7523}
7524
7530static PRISM_INLINE size_t
7531match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) {
7532 return match_eol_at(parser, parser->current.end + offset);
7533}
7534
7540static PRISM_INLINE size_t
7541match_eol(pm_parser_t *parser) {
7542 return match_eol_at(parser, parser->current.end);
7543}
7544
7548static PRISM_INLINE const uint8_t *
7549next_newline(const uint8_t *cursor, ptrdiff_t length) {
7550 assert(length >= 0);
7551
7552 // Note that it's okay for us to use memchr here to look for \n because none
7553 // of the encodings that we support have \n as a component of a multi-byte
7554 // character.
7555 return memchr(cursor, '\n', (size_t) length);
7556}
7557
7561static PRISM_INLINE bool
7562ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) {
7563 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));
7564}
7565
7570static bool
7571parser_lex_magic_comment_encoding_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
7572 const pm_encoding_t *encoding = pm_encoding_find(start, end);
7573
7574 if (encoding != NULL) {
7575 if (parser->encoding != encoding) {
7576 parser->encoding = encoding;
7577 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
7578 }
7579
7580 parser->encoding_changed = (encoding != PM_ENCODING_UTF_8_ENTRY);
7581 return true;
7582 }
7583
7584 return false;
7585}
7586
7591static void
7592parser_lex_magic_comment_encoding(pm_parser_t *parser) {
7593 const uint8_t *cursor = parser->current.start + 1;
7594 const uint8_t *end = parser->current.end;
7595
7596 bool separator = false;
7597 while (true) {
7598 if (end - cursor <= 6) return;
7599 switch (cursor[6]) {
7600 case 'C': case 'c': cursor += 6; continue;
7601 case 'O': case 'o': cursor += 5; continue;
7602 case 'D': case 'd': cursor += 4; continue;
7603 case 'I': case 'i': cursor += 3; continue;
7604 case 'N': case 'n': cursor += 2; continue;
7605 case 'G': case 'g': cursor += 1; continue;
7606 case '=': case ':':
7607 separator = true;
7608 cursor += 6;
7609 break;
7610 default:
7611 cursor += 6;
7612 if (pm_char_is_whitespace(*cursor)) break;
7613 continue;
7614 }
7615 if (pm_strncasecmp(cursor - 6, (const uint8_t *) "coding", 6) == 0) break;
7616 separator = false;
7617 }
7618
7619 while (true) {
7620 do {
7621 if (++cursor >= end) return;
7622 } while (pm_char_is_whitespace(*cursor));
7623
7624 if (separator) break;
7625 if (*cursor != '=' && *cursor != ':') return;
7626
7627 separator = true;
7628 cursor++;
7629 }
7630
7631 const uint8_t *value_start = cursor;
7632 while ((*cursor == '-' || *cursor == '_' || parser->encoding->alnum_char(cursor, 1)) && ++cursor < end);
7633
7634 if (!parser_lex_magic_comment_encoding_value(parser, value_start, cursor)) {
7635 // If we were unable to parse the encoding value, then we've got an
7636 // issue because we didn't understand the encoding that the user was
7637 // trying to use. In this case we'll keep using the default encoding but
7638 // add an error to the parser to indicate an unsuccessful parse.
7639 pm_parser_err(parser, U32(value_start - parser->start), U32(cursor - value_start), PM_ERR_INVALID_ENCODING_MAGIC_COMMENT);
7640 }
7641}
7642
7643typedef enum {
7644 PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE,
7645 PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE,
7646 PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID
7647} pm_magic_comment_boolean_value_t;
7648
7653static pm_magic_comment_boolean_value_t
7654parser_lex_magic_comment_boolean_value(const uint8_t *value_start, uint32_t value_length) {
7655 if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "true", 4) == 0) {
7656 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE;
7657 } else if (value_length == 5 && pm_strncasecmp(value_start, (const uint8_t *) "false", 5) == 0) {
7658 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE;
7659 } else {
7660 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID;
7661 }
7662}
7663
7664static PRISM_INLINE bool
7665pm_char_is_magic_comment_key_delimiter(const uint8_t b) {
7666 return b == '\'' || b == '"' || b == ':' || b == ';';
7667}
7668
7674static PRISM_INLINE const uint8_t *
7675parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor, const uint8_t *end) {
7676 // Scan for '*' as the middle character, since it is rarer than '-' in
7677 // typical comments and avoids repeated memchr calls for '-' that hit
7678 // dashes in words like "foo-bar".
7679 while ((cursor + 3 <= end) && (cursor = pm_memchr(cursor + 1, '*', (size_t) (end - cursor - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7680 if (cursor[-1] == '-' && cursor + 1 < end && cursor[1] == '-') {
7681 return cursor - 1;
7682 }
7683 }
7684 return NULL;
7685}
7686
7697static PRISM_INLINE bool
7698parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
7699 bool result = true;
7700
7701 const uint8_t *start = parser->current.start + 1;
7702 const uint8_t *end = parser->current.end;
7703 if (end - start <= 7) return false;
7704
7705 const uint8_t *cursor;
7706 bool indicator = false;
7707
7708 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7709 start = cursor + 3;
7710
7711 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7712 end = cursor;
7713 indicator = true;
7714 } else {
7715 // If we have a start marker but not an end marker, then we cannot
7716 // have a magic comment.
7717 return false;
7718 }
7719 } else {
7720 // Non-emacs magic comments must contain a colon for `key: value`.
7721 // Reject early if there is no colon to avoid scanning the entire
7722 // comment character-by-character.
7723 if (pm_memchr(start, ':', (size_t) (end - start), parser->encoding_changed, parser->encoding) == NULL) {
7724 return false;
7725 }
7726
7727 // Advance start past leading whitespace so the main loop begins
7728 // directly at the key, avoiding a redundant whitespace scan.
7729 start += pm_strspn_whitespace(start, end - start);
7730 }
7731
7732 cursor = start;
7733 while (cursor < end) {
7734 if (indicator) {
7735 while (cursor < end && (pm_char_is_magic_comment_key_delimiter(*cursor) || pm_char_is_whitespace(*cursor))) cursor++;
7736 }
7737
7738 const uint8_t *key_start = cursor;
7739 while (cursor < end && (!pm_char_is_magic_comment_key_delimiter(*cursor) && !pm_char_is_whitespace(*cursor))) cursor++;
7740
7741 const uint8_t *key_end = cursor;
7742 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7743 if (cursor == end) break;
7744
7745 if (*cursor == ':') {
7746 cursor++;
7747 } else {
7748 if (!indicator) return false;
7749 continue;
7750 }
7751
7752 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7753 if (cursor == end) break;
7754
7755 const uint8_t *value_start;
7756 const uint8_t *value_end;
7757
7758 if (*cursor == '"') {
7759 value_start = ++cursor;
7760 for (; cursor < end && *cursor != '"'; cursor++) {
7761 if (*cursor == '\\' && (cursor + 1 < end)) cursor++;
7762 }
7763 value_end = cursor;
7764 if (cursor < end && *cursor == '"') cursor++;
7765 } else {
7766 value_start = cursor;
7767 while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++;
7768 value_end = cursor;
7769 }
7770
7771 if (indicator) {
7772 while (cursor < end && (*cursor == ';' || pm_char_is_whitespace(*cursor))) cursor++;
7773 } else {
7774 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7775 if (cursor != end) return false;
7776 }
7777
7778 // Here, we need to do some processing on the key to swap out dashes for
7779 // underscores. We only need to do this if there _is_ a dash in the key.
7780 pm_string_t key;
7781 const size_t key_length = (size_t) (key_end - key_start);
7782 const uint8_t *dash = pm_memchr(key_start, '-', key_length, parser->encoding_changed, parser->encoding);
7783
7784 if (dash == NULL) {
7785 pm_string_shared_init(&key, key_start, key_end);
7786 } else {
7787 uint8_t *buffer = xmalloc(key_length);
7788 if (buffer == NULL) break;
7789
7790 memcpy(buffer, key_start, key_length);
7791 buffer[dash - key_start] = '_';
7792
7793 while ((dash = pm_memchr(dash + 1, '-', (size_t) (key_end - dash - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7794 buffer[dash - key_start] = '_';
7795 }
7796
7797 pm_string_owned_init(&key, buffer, key_length);
7798 }
7799
7800 // Finally, we can start checking the key against the list of known
7801 // magic comment keys, and potentially change state based on that.
7802 const uint8_t *key_source = pm_string_source(&key);
7803 uint32_t value_length = (uint32_t) (value_end - value_start);
7804
7805 // We only want to attempt to compare against encoding comments if it's
7806 // the first line in the file (or the second in the case of a shebang).
7807 if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7808 if (
7809 (key_length == 8 && pm_strncasecmp(key_source, (const uint8_t *) "encoding", 8) == 0) ||
7810 (key_length == 6 && pm_strncasecmp(key_source, (const uint8_t *) "coding", 6) == 0)
7811 ) {
7812 result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7813 }
7814 }
7815
7816 if (key_length == 11) {
7817 if (pm_strncasecmp(key_source, (const uint8_t *) "warn_indent", 11) == 0) {
7818 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7819 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7820 PM_PARSER_WARN_TOKEN_FORMAT(
7821 parser,
7822 &parser->current,
7823 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7824 (int) key_length,
7825 (const char *) key_source,
7826 (int) value_length,
7827 (const char *) value_start
7828 );
7829 break;
7830 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7831 parser->warn_mismatched_indentation = false;
7832 break;
7833 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7834 parser->warn_mismatched_indentation = true;
7835 break;
7836 }
7837 }
7838 } else if (key_length == 21) {
7839 if (pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) {
7840 // We only want to handle frozen string literal comments if it's
7841 // before any semantic tokens have been seen.
7842 if (semantic_token_seen) {
7843 pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
7844 } else {
7845 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7846 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7847 PM_PARSER_WARN_TOKEN_FORMAT(
7848 parser,
7849 &parser->current,
7850 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7851 (int) key_length,
7852 (const char *) key_source,
7853 (int) value_length,
7854 (const char *) value_start
7855 );
7856 break;
7857 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7858 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
7859 break;
7860 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7861 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
7862 break;
7863 }
7864 }
7865 }
7866 } else if (key_length == 24) {
7867 if (pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) {
7868 const uint8_t *cursor = parser->current.start;
7869 while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
7870
7871 if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
7872 pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
7873 } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
7874 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
7875 } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
7876 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
7877 } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
7878 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
7879 } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
7880 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
7881 } else {
7882 PM_PARSER_WARN_TOKEN_FORMAT(
7883 parser,
7884 &parser->current,
7885 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7886 (int) key_length,
7887 (const char *) key_source,
7888 (int) value_length,
7889 (const char *) value_start
7890 );
7891 }
7892 }
7893 }
7894
7895 // When we're done, we want to free the string in case we had to
7896 // allocate memory for it.
7897 pm_string_cleanup(&key);
7898
7899 // Allocate a new magic comment node to append to the parser's list.
7900 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));
7901 magic_comment->node.next = NULL;
7902 magic_comment->key = (pm_location_t) { .start = U32(key_start - parser->start), .length = U32(key_length) };
7903 magic_comment->value = (pm_location_t) { .start = U32(value_start - parser->start), .length = value_length };
7904 pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment);
7905 }
7906
7907 return result;
7908}
7909
7910/******************************************************************************/
7911/* Context manipulations */
7912/******************************************************************************/
7913
7914static const uint32_t context_terminators[] = {
7915 [PM_CONTEXT_NONE] = 0,
7916 [PM_CONTEXT_BEGIN] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7917 [PM_CONTEXT_BEGIN_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7918 [PM_CONTEXT_BEGIN_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7919 [PM_CONTEXT_BEGIN_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7920 [PM_CONTEXT_BLOCK_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7921 [PM_CONTEXT_BLOCK_KEYWORDS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7922 [PM_CONTEXT_BLOCK_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7923 [PM_CONTEXT_BLOCK_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7924 [PM_CONTEXT_BLOCK_PARAMETERS] = (1U << PM_TOKEN_PIPE),
7925 [PM_CONTEXT_BLOCK_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7926 [PM_CONTEXT_CASE_WHEN] = (1U << PM_TOKEN_KEYWORD_WHEN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7927 [PM_CONTEXT_CASE_IN] = (1U << PM_TOKEN_KEYWORD_IN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7928 [PM_CONTEXT_CLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7929 [PM_CONTEXT_CLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7930 [PM_CONTEXT_CLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7931 [PM_CONTEXT_CLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7932 [PM_CONTEXT_DEF] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7933 [PM_CONTEXT_DEF_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7934 [PM_CONTEXT_DEF_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7935 [PM_CONTEXT_DEF_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7936 [PM_CONTEXT_DEF_PARAMS] = (1U << PM_TOKEN_EOF),
7937 [PM_CONTEXT_DEFINED] = (1U << PM_TOKEN_EOF),
7938 [PM_CONTEXT_DEFAULT_PARAMS] = (1U << PM_TOKEN_COMMA) | (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7939 [PM_CONTEXT_ELSE] = (1U << PM_TOKEN_KEYWORD_END),
7940 [PM_CONTEXT_ELSIF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7941 [PM_CONTEXT_EMBEXPR] = (1U << PM_TOKEN_EMBEXPR_END),
7942 [PM_CONTEXT_FOR] = (1U << PM_TOKEN_KEYWORD_END),
7943 [PM_CONTEXT_FOR_INDEX] = (1U << PM_TOKEN_KEYWORD_IN),
7944 [PM_CONTEXT_IF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7945 [PM_CONTEXT_LAMBDA_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7946 [PM_CONTEXT_LAMBDA_DO_END] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7947 [PM_CONTEXT_LAMBDA_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7948 [PM_CONTEXT_LAMBDA_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7949 [PM_CONTEXT_LAMBDA_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7950 [PM_CONTEXT_LOOP_PREDICATE] = (1U << PM_TOKEN_KEYWORD_DO) | (1U << PM_TOKEN_KEYWORD_THEN),
7951 [PM_CONTEXT_MAIN] = (1U << PM_TOKEN_EOF),
7952 [PM_CONTEXT_MODULE] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7953 [PM_CONTEXT_MODULE_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7954 [PM_CONTEXT_MODULE_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7955 [PM_CONTEXT_MODULE_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7956 [PM_CONTEXT_MULTI_TARGET] = (1U << PM_TOKEN_EOF),
7957 [PM_CONTEXT_PARENS] = (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7958 [PM_CONTEXT_POSTEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7959 [PM_CONTEXT_PREDICATE] = (1U << PM_TOKEN_KEYWORD_THEN) | (1U << PM_TOKEN_NEWLINE) | (1U << PM_TOKEN_SEMICOLON),
7960 [PM_CONTEXT_PREEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7961 [PM_CONTEXT_RESCUE_MODIFIER] = (1U << PM_TOKEN_EOF),
7962 [PM_CONTEXT_SCLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7963 [PM_CONTEXT_SCLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7964 [PM_CONTEXT_SCLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7965 [PM_CONTEXT_SCLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7966 [PM_CONTEXT_TERNARY] = (1U << PM_TOKEN_EOF),
7967 [PM_CONTEXT_UNLESS] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7968 [PM_CONTEXT_UNTIL] = (1U << PM_TOKEN_KEYWORD_END),
7969 [PM_CONTEXT_WHILE] = (1U << PM_TOKEN_KEYWORD_END),
7970};
7971
7972static PRISM_INLINE bool
7973context_terminator(pm_context_t context, pm_token_t *token) {
7974 return token->type < 32 && (context_terminators[context] & (1U << token->type));
7975}
7976
7981static pm_context_t
7982context_recoverable(const pm_parser_t *parser, pm_token_t *token) {
7983 pm_context_node_t *context_node = parser->current_context;
7984
7985 while (context_node != NULL) {
7986 if (context_terminator(context_node->context, token)) return context_node->context;
7987 context_node = context_node->prev;
7988 }
7989
7990 return PM_CONTEXT_NONE;
7991}
7992
7993static bool
7994context_push(pm_parser_t *parser, pm_context_t context) {
7995 pm_context_node_t *context_node = (pm_context_node_t *) xmalloc(sizeof(pm_context_node_t));
7996 if (context_node == NULL) return false;
7997
7998 *context_node = (pm_context_node_t) { .context = context, .prev = NULL };
7999
8000 if (parser->current_context == NULL) {
8001 parser->current_context = context_node;
8002 } else {
8003 context_node->prev = parser->current_context;
8004 parser->current_context = context_node;
8005 }
8006
8007 return true;
8008}
8009
8010static void
8011context_pop(pm_parser_t *parser) {
8012 pm_context_node_t *prev = parser->current_context->prev;
8013 xfree_sized(parser->current_context, sizeof(pm_context_node_t));
8014 parser->current_context = prev;
8015}
8016
8017static bool
8018context_p(const pm_parser_t *parser, pm_context_t context) {
8019 pm_context_node_t *context_node = parser->current_context;
8020
8021 while (context_node != NULL) {
8022 if (context_node->context == context) return true;
8023 context_node = context_node->prev;
8024 }
8025
8026 return false;
8027}
8028
8029static bool
8030context_def_p(const pm_parser_t *parser) {
8031 pm_context_node_t *context_node = parser->current_context;
8032
8033 while (context_node != NULL) {
8034 switch (context_node->context) {
8035 case PM_CONTEXT_DEF:
8036 case PM_CONTEXT_DEF_PARAMS:
8037 case PM_CONTEXT_DEF_ENSURE:
8038 case PM_CONTEXT_DEF_RESCUE:
8039 case PM_CONTEXT_DEF_ELSE:
8040 return true;
8041 case PM_CONTEXT_CLASS:
8042 case PM_CONTEXT_CLASS_ENSURE:
8043 case PM_CONTEXT_CLASS_RESCUE:
8044 case PM_CONTEXT_CLASS_ELSE:
8045 case PM_CONTEXT_MODULE:
8046 case PM_CONTEXT_MODULE_ENSURE:
8047 case PM_CONTEXT_MODULE_RESCUE:
8048 case PM_CONTEXT_MODULE_ELSE:
8049 case PM_CONTEXT_SCLASS:
8050 case PM_CONTEXT_SCLASS_ENSURE:
8051 case PM_CONTEXT_SCLASS_RESCUE:
8052 case PM_CONTEXT_SCLASS_ELSE:
8053 return false;
8054 default:
8055 context_node = context_node->prev;
8056 }
8057 }
8058
8059 return false;
8060}
8061
8066static const char *
8067context_human(pm_context_t context) {
8068 switch (context) {
8069 case PM_CONTEXT_NONE:
8070 assert(false && "unreachable");
8071 return "";
8072 case PM_CONTEXT_BEGIN: return "begin statement";
8073 case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block";
8074 case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block";
8075 case PM_CONTEXT_BLOCK_PARAMETERS: return "'|'..'|' block parameter";
8076 case PM_CONTEXT_CASE_WHEN: return "'when' clause";
8077 case PM_CONTEXT_CASE_IN: return "'in' clause";
8078 case PM_CONTEXT_CLASS: return "class definition";
8079 case PM_CONTEXT_DEF: return "method definition";
8080 case PM_CONTEXT_DEF_PARAMS: return "method parameters";
8081 case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value";
8082 case PM_CONTEXT_DEFINED: return "'defined?' expression";
8083 case PM_CONTEXT_ELSE:
8084 case PM_CONTEXT_BEGIN_ELSE:
8085 case PM_CONTEXT_BLOCK_ELSE:
8086 case PM_CONTEXT_CLASS_ELSE:
8087 case PM_CONTEXT_DEF_ELSE:
8088 case PM_CONTEXT_LAMBDA_ELSE:
8089 case PM_CONTEXT_MODULE_ELSE:
8090 case PM_CONTEXT_SCLASS_ELSE: return "'else' clause";
8091 case PM_CONTEXT_ELSIF: return "'elsif' clause";
8092 case PM_CONTEXT_EMBEXPR: return "embedded expression";
8093 case PM_CONTEXT_BEGIN_ENSURE:
8094 case PM_CONTEXT_BLOCK_ENSURE:
8095 case PM_CONTEXT_CLASS_ENSURE:
8096 case PM_CONTEXT_DEF_ENSURE:
8097 case PM_CONTEXT_LAMBDA_ENSURE:
8098 case PM_CONTEXT_MODULE_ENSURE:
8099 case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause";
8100 case PM_CONTEXT_FOR: return "for loop";
8101 case PM_CONTEXT_FOR_INDEX: return "for loop index";
8102 case PM_CONTEXT_IF: return "if statement";
8103 case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block";
8104 case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block";
8105 case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate";
8106 case PM_CONTEXT_MAIN: return "top level context";
8107 case PM_CONTEXT_MODULE: return "module definition";
8108 case PM_CONTEXT_MULTI_TARGET: return "multiple targets";
8109 case PM_CONTEXT_PARENS: return "parentheses";
8110 case PM_CONTEXT_POSTEXE: return "'END' block";
8111 case PM_CONTEXT_PREDICATE: return "predicate";
8112 case PM_CONTEXT_PREEXE: return "'BEGIN' block";
8113 case PM_CONTEXT_BEGIN_RESCUE:
8114 case PM_CONTEXT_BLOCK_RESCUE:
8115 case PM_CONTEXT_CLASS_RESCUE:
8116 case PM_CONTEXT_DEF_RESCUE:
8117 case PM_CONTEXT_LAMBDA_RESCUE:
8118 case PM_CONTEXT_MODULE_RESCUE:
8119 case PM_CONTEXT_RESCUE_MODIFIER:
8120 case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause";
8121 case PM_CONTEXT_SCLASS: return "singleton class definition";
8122 case PM_CONTEXT_TERNARY: return "ternary expression";
8123 case PM_CONTEXT_UNLESS: return "unless statement";
8124 case PM_CONTEXT_UNTIL: return "until statement";
8125 case PM_CONTEXT_WHILE: return "while statement";
8126 }
8127
8128 assert(false && "unreachable");
8129 return "";
8130}
8131
8132/******************************************************************************/
8133/* Specific token lexers */
8134/******************************************************************************/
8135
8136static PRISM_INLINE void
8137pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *string, size_t length, const uint8_t *invalid) {
8138 if (invalid != NULL) {
8139 pm_diagnostic_id_t diag_id = (invalid == (string + length - 1)) ? PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING : PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER;
8140 pm_parser_err(parser, U32(invalid - parser->start), 1, diag_id);
8141 }
8142}
8143
8144static size_t
8145pm_strspn_binary_number_validate(pm_parser_t *parser, const uint8_t *string) {
8146 const uint8_t *invalid = NULL;
8147 size_t length = pm_strspn_binary_number(string, parser->end - string, &invalid);
8148 pm_strspn_number_validate(parser, string, length, invalid);
8149 return length;
8150}
8151
8152static size_t
8153pm_strspn_octal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8154 const uint8_t *invalid = NULL;
8155 size_t length = pm_strspn_octal_number(string, parser->end - string, &invalid);
8156 pm_strspn_number_validate(parser, string, length, invalid);
8157 return length;
8158}
8159
8160static size_t
8161pm_strspn_decimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8162 const uint8_t *invalid = NULL;
8163 size_t length = pm_strspn_decimal_number(string, parser->end - string, &invalid);
8164 pm_strspn_number_validate(parser, string, length, invalid);
8165 return length;
8166}
8167
8168static size_t
8169pm_strspn_hexadecimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8170 const uint8_t *invalid = NULL;
8171 size_t length = pm_strspn_hexadecimal_number(string, parser->end - string, &invalid);
8172 pm_strspn_number_validate(parser, string, length, invalid);
8173 return length;
8174}
8175
8176static pm_token_type_t
8177lex_optional_float_suffix(pm_parser_t *parser, bool* seen_e) {
8178 pm_token_type_t type = PM_TOKEN_INTEGER;
8179
8180 // Here we're going to attempt to parse the optional decimal portion of a
8181 // float. If it's not there, then it's okay and we'll just continue on.
8182 if (peek(parser) == '.') {
8183 if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8184 parser->current.end += 2;
8185 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8186 type = PM_TOKEN_FLOAT;
8187 } else {
8188 // If we had a . and then something else, then it's not a float
8189 // suffix on a number it's a method call or something else.
8190 return type;
8191 }
8192 }
8193
8194 // Here we're going to attempt to parse the optional exponent portion of a
8195 // float. If it's not there, it's okay and we'll just continue on.
8196 if ((peek(parser) == 'e') || (peek(parser) == 'E')) {
8197 if ((peek_offset(parser, 1) == '+') || (peek_offset(parser, 1) == '-')) {
8198 parser->current.end += 2;
8199
8200 if (pm_char_is_decimal_digit(peek(parser))) {
8201 parser->current.end++;
8202 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8203 } else {
8204 pm_parser_err_current(parser, PM_ERR_INVALID_FLOAT_EXPONENT);
8205 }
8206 } else if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8207 parser->current.end++;
8208 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8209 } else {
8210 return type;
8211 }
8212
8213 *seen_e = true;
8214 type = PM_TOKEN_FLOAT;
8215 }
8216
8217 return type;
8218}
8219
8220static pm_token_type_t
8221lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) {
8222 pm_token_type_t type = PM_TOKEN_INTEGER;
8223 *seen_e = false;
8224
8225 if (peek_offset(parser, -1) == '0') {
8226 switch (*parser->current.end) {
8227 // 0d1111 is a decimal number
8228 case 'd':
8229 case 'D':
8230 parser->current.end++;
8231 if (pm_char_is_decimal_digit(peek(parser))) {
8232 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8233 } else {
8234 match(parser, '_');
8235 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_DECIMAL);
8236 }
8237
8238 break;
8239
8240 // 0b1111 is a binary number
8241 case 'b':
8242 case 'B':
8243 parser->current.end++;
8244 if (pm_char_is_binary_digit(peek(parser))) {
8245 parser->current.end += pm_strspn_binary_number_validate(parser, parser->current.end);
8246 } else {
8247 match(parser, '_');
8248 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY);
8249 }
8250
8251 parser->integer.base = PM_INTEGER_BASE_FLAGS_BINARY;
8252 break;
8253
8254 // 0o1111 is an octal number
8255 case 'o':
8256 case 'O':
8257 parser->current.end++;
8258 if (pm_char_is_octal_digit(peek(parser))) {
8259 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8260 } else {
8261 match(parser, '_');
8262 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL);
8263 }
8264
8265 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8266 break;
8267
8268 // 01111 is an octal number
8269 case '_':
8270 case '0':
8271 case '1':
8272 case '2':
8273 case '3':
8274 case '4':
8275 case '5':
8276 case '6':
8277 case '7':
8278 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8279 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8280 break;
8281
8282 // 0x1111 is a hexadecimal number
8283 case 'x':
8284 case 'X':
8285 parser->current.end++;
8286 if (pm_char_is_hexadecimal_digit(peek(parser))) {
8287 parser->current.end += pm_strspn_hexadecimal_number_validate(parser, parser->current.end);
8288 } else {
8289 match(parser, '_');
8290 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL);
8291 }
8292
8293 parser->integer.base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL;
8294 break;
8295
8296 // 0.xxx is a float
8297 case '.': {
8298 type = lex_optional_float_suffix(parser, seen_e);
8299 break;
8300 }
8301
8302 // 0exxx is a float
8303 case 'e':
8304 case 'E': {
8305 type = lex_optional_float_suffix(parser, seen_e);
8306 break;
8307 }
8308 }
8309 } else {
8310 // If it didn't start with a 0, then we'll lex as far as we can into a
8311 // decimal number. We compute the integer value inline to avoid
8312 // re-scanning the digits later in pm_integer_parse.
8313 {
8314 const uint8_t *cursor = parser->current.end;
8315 const uint8_t *end = parser->end;
8316 uint64_t value = (uint64_t) (cursor[-1] - '0');
8317
8318 bool has_underscore = false;
8319 bool prev_underscore = false;
8320 const uint8_t *invalid = NULL;
8321
8322 while (cursor < end) {
8323 uint8_t c = *cursor;
8324 if (c >= '0' && c <= '9') {
8325 if (value <= UINT32_MAX) value = value * 10 + (uint64_t) (c - '0');
8326 prev_underscore = false;
8327 cursor++;
8328 } else if (c == '_') {
8329 has_underscore = true;
8330 if (prev_underscore && invalid == NULL) invalid = cursor;
8331 prev_underscore = true;
8332 cursor++;
8333 } else {
8334 break;
8335 }
8336 }
8337
8338 if (has_underscore) {
8339 if (prev_underscore && invalid == NULL) invalid = cursor - 1;
8340 pm_strspn_number_validate(parser, parser->current.end, (size_t) (cursor - parser->current.end), invalid);
8341 }
8342
8343 if (value <= UINT32_MAX) {
8344 parser->integer.value = (uint32_t) value;
8345 parser->integer.lexed = true;
8346 }
8347
8348 parser->current.end = cursor;
8349 }
8350
8351 // Afterward, we'll lex as far as we can into an optional float suffix.
8352 // Guard the function call: the vast majority of decimal numbers are
8353 // plain integers, so avoid the call when the next byte cannot start a
8354 // float suffix.
8355 {
8356 uint8_t next = peek(parser);
8357 if (next == '.' || next == 'e' || next == 'E') {
8358 type = lex_optional_float_suffix(parser, seen_e);
8359
8360 // If it turned out to be a float, the cached integer value is
8361 // invalid.
8362 if (type != PM_TOKEN_INTEGER) {
8363 parser->integer.lexed = false;
8364 }
8365 }
8366 }
8367 }
8368
8369 // At this point we have a completed number, but we want to provide the user
8370 // with a good experience if they put an additional .xxx fractional
8371 // component on the end, so we'll check for that here.
8372 if (peek_offset(parser, 0) == '.' && pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8373 const uint8_t *fraction_start = parser->current.end;
8374 const uint8_t *fraction_end = parser->current.end + 2;
8375 fraction_end += pm_strspn_decimal_digit(fraction_end, parser->end - fraction_end);
8376 pm_parser_err(parser, U32(fraction_start - parser->start), U32(fraction_end - fraction_start), PM_ERR_INVALID_NUMBER_FRACTION);
8377 }
8378
8379 return type;
8380}
8381
8382static pm_token_type_t
8383lex_numeric(pm_parser_t *parser) {
8384 pm_token_type_t type = PM_TOKEN_INTEGER;
8385 parser->integer.base = PM_INTEGER_BASE_FLAGS_DECIMAL;
8386 parser->integer.lexed = false;
8387
8388 if (parser->current.end < parser->end) {
8389 bool seen_e = false;
8390 type = lex_numeric_prefix(parser, &seen_e);
8391
8392 const uint8_t *end = parser->current.end;
8393 pm_token_type_t suffix_type = type;
8394
8395 if (type == PM_TOKEN_INTEGER) {
8396 if (match(parser, 'r')) {
8397 suffix_type = PM_TOKEN_INTEGER_RATIONAL;
8398
8399 if (match(parser, 'i')) {
8400 suffix_type = PM_TOKEN_INTEGER_RATIONAL_IMAGINARY;
8401 }
8402 } else if (match(parser, 'i')) {
8403 suffix_type = PM_TOKEN_INTEGER_IMAGINARY;
8404 }
8405 } else {
8406 if (!seen_e && match(parser, 'r')) {
8407 suffix_type = PM_TOKEN_FLOAT_RATIONAL;
8408
8409 if (match(parser, 'i')) {
8410 suffix_type = PM_TOKEN_FLOAT_RATIONAL_IMAGINARY;
8411 }
8412 } else if (match(parser, 'i')) {
8413 suffix_type = PM_TOKEN_FLOAT_IMAGINARY;
8414 }
8415 }
8416
8417 const uint8_t b = peek(parser);
8418 if (b != '\0' && (b >= 0x80 || ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) || b == '_')) {
8419 parser->current.end = end;
8420 } else {
8421 type = suffix_type;
8422 }
8423 }
8424
8425 return type;
8426}
8427
8428static pm_token_type_t
8429lex_global_variable(pm_parser_t *parser) {
8430 if (parser->current.end >= parser->end) {
8431 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8432 return PM_TOKEN_GLOBAL_VARIABLE;
8433 }
8434
8435 // True if multiple characters are allowed after the declaration of the
8436 // global variable. Not true when it starts with "$-".
8437 bool allow_multiple = true;
8438
8439 switch (*parser->current.end) {
8440 case '~': // $~: match-data
8441 case '*': // $*: argv
8442 case '$': // $$: pid
8443 case '?': // $?: last status
8444 case '!': // $!: error string
8445 case '@': // $@: error position
8446 case '/': // $/: input record separator
8447 case '\\': // $\: output record separator
8448 case ';': // $;: field separator
8449 case ',': // $,: output field separator
8450 case '.': // $.: last read line number
8451 case '=': // $=: ignorecase
8452 case ':': // $:: load path
8453 case '<': // $<: reading filename
8454 case '>': // $>: default output handle
8455 case '\"': // $": already loaded files
8456 parser->current.end++;
8457 return PM_TOKEN_GLOBAL_VARIABLE;
8458
8459 case '&': // $&: last match
8460 case '`': // $`: string before last match
8461 case '\'': // $': string after last match
8462 case '+': // $+: string matches last paren.
8463 parser->current.end++;
8464 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_BACK_REFERENCE;
8465
8466 case '0': {
8467 parser->current.end++;
8468 size_t width;
8469
8470 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8471 do {
8472 parser->current.end += width;
8473 } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8474
8475 // $0 isn't allowed to be followed by anything.
8476 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;
8477 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, diag_id);
8478 }
8479
8480 return PM_TOKEN_GLOBAL_VARIABLE;
8481 }
8482
8483 case '1':
8484 case '2':
8485 case '3':
8486 case '4':
8487 case '5':
8488 case '6':
8489 case '7':
8490 case '8':
8491 case '9':
8492 parser->current.end += pm_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end);
8493 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_NUMBERED_REFERENCE;
8494
8495 case '-':
8496 parser->current.end++;
8497 allow_multiple = false;
8499 default: {
8500 size_t width;
8501
8502 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8503 do {
8504 parser->current.end += width;
8505 } while (allow_multiple && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8506 } else if (pm_char_is_whitespace(peek(parser))) {
8507 // If we get here, then we have a $ followed by whitespace,
8508 // which is not allowed.
8509 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8510 } else {
8511 // If we get here, then we have a $ followed by something that
8512 // isn't recognized as a global variable.
8513 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;
8514 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
8515 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);
8516 }
8517
8518 return PM_TOKEN_GLOBAL_VARIABLE;
8519 }
8520 }
8521}
8522
8535static PRISM_INLINE pm_token_type_t
8536lex_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) {
8537 if (memcmp(current_start, value, vlen) == 0) {
8538 pm_lex_state_t last_state = parser->lex_state;
8539
8540 if (parser->lex_state & PM_LEX_STATE_FNAME) {
8541 lex_state_set(parser, PM_LEX_STATE_ENDFN);
8542 } else {
8543 lex_state_set(parser, state);
8544 if (state == PM_LEX_STATE_BEG) {
8545 parser->command_start = true;
8546 }
8547
8548 if ((modifier_type != PM_TOKEN_EOF) && !(last_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
8549 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
8550 return modifier_type;
8551 }
8552 }
8553
8554 return type;
8555 }
8556
8557 return PM_TOKEN_EOF;
8558}
8559
8560static pm_token_type_t
8561lex_identifier(pm_parser_t *parser, bool previous_command_start) {
8562 // Lex as far as we can into the current identifier.
8563 size_t width;
8564 const uint8_t *end = parser->end;
8565 const uint8_t *current_start = parser->current.start;
8566 const uint8_t *current_end = parser->current.end;
8567 bool encoding_changed = parser->encoding_changed;
8568
8569 if (encoding_changed) {
8570 while ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) {
8571 current_end += width;
8572 }
8573 } else {
8574 // Fast path: scan ASCII identifier bytes using wide operations.
8575 current_end += scan_identifier_ascii(current_end, end);
8576
8577 // Byte-at-a-time fallback for the tail and any UTF-8 sequences.
8578 while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) {
8579 current_end += width;
8580 }
8581 }
8582 parser->current.end = current_end;
8583
8584 // Now cache the length of the identifier so that we can quickly compare it
8585 // against known keywords.
8586 width = (size_t) (current_end - current_start);
8587
8588 if (current_end < end) {
8589 if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
8590 // First we'll attempt to extend the identifier by a ! or ?. Then we'll
8591 // check if we're returning the defined? keyword or just an identifier.
8592 width++;
8593
8594 if (
8595 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8596 (peek(parser) == ':') && (peek_offset(parser, 1) != ':')
8597 ) {
8598 // If we're in a position where we can accept a : at the end of an
8599 // identifier, then we'll optionally accept it.
8600 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8601 (void) match(parser, ':');
8602
8603 /* A label is a symbol lexed inline rather than through a lex
8604 * mode, so it clears the encoding here. */
8605 parser->explicit_encoding = NULL;
8606 return PM_TOKEN_LABEL;
8607 }
8608
8609 if (parser->lex_state != PM_LEX_STATE_DOT) {
8610 if (width == 8 && (lex_keyword(parser, current_start, "defined?", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF) != PM_TOKEN_EOF)) {
8611 return PM_TOKEN_KEYWORD_DEFINED;
8612 }
8613 }
8614
8615 return PM_TOKEN_METHOD_NAME;
8616 }
8617
8618 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, '=')) {
8619 // If we're in a position where we can accept a = at the end of an
8620 // identifier, then we'll optionally accept it.
8621 return PM_TOKEN_IDENTIFIER;
8622 }
8623
8624 if (
8625 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8626 peek(parser) == ':' && peek_offset(parser, 1) != ':'
8627 ) {
8628 // If we're in a position where we can accept a : at the end of an
8629 // identifier, then we'll optionally accept it.
8630 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8631 (void) match(parser, ':');
8632
8633 /* A label is a symbol lexed inline rather than through a lex
8634 * mode, so it clears the encoding here. */
8635 parser->explicit_encoding = NULL;
8636 return PM_TOKEN_LABEL;
8637 }
8638 }
8639
8640 if (parser->lex_state != PM_LEX_STATE_DOT) {
8641 pm_token_type_t type;
8642
8643 /* The lex state from before lex_keyword transitions it, mirroring the
8644 * `state = p->lex.state` capture in parse.y's keyword handling. */
8645 pm_lex_state_t previous_lex_state = parser->lex_state;
8646
8647 switch (width) {
8648 case 2:
8649 if (lex_keyword(parser, current_start, "do", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF) != PM_TOKEN_EOF) {
8650 /* In FNAME position (a symbol like `:do` or a method name
8651 * like `def do`), `do` is a plain name rather than a
8652 * block, loop, or lambda opener, so none of the
8653 * discrimination below applies. This mirrors parse.y,
8654 * whose EXPR_FNAME early-return precedes all of the
8655 * keyword_do special-casing (and never touches
8656 * lpar_beg). */
8657 if (previous_lex_state & PM_LEX_STATE_FNAME) {
8658 return PM_TOKEN_KEYWORD_DO;
8659 }
8660 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
8661 // At the bare nesting level of a lambda literal (no
8662 // delimiter opened since `->`), a `do` opens the lambda
8663 // body. This is a distinct token so that a command in a
8664 // parameter default cannot consume it as its own block
8665 // (`-> a = foo do end` is `->(a = foo) do end`). It
8666 // mirrors CRuby's keyword_do_LAMBDA.
8667 //
8668 // Clear the nesting so that no token within the
8669 // `do`/`end` body is considered to be at the beginning
8670 // of a lambda; the parser restores the enclosing value
8671 // once the lambda has been fully parsed. This mirrors
8672 // parse.y setting `p->lex.lpar_beg = -1` when lexing
8673 // keyword_do_LAMBDA.
8674 parser->lambda_enclosure_nesting = -1;
8675 return PM_TOKEN_KEYWORD_DO_LAMBDA;
8676 }
8677 if (pm_do_loop_stack_p(parser)) {
8678 return PM_TOKEN_KEYWORD_DO_LOOP;
8679 }
8680 if (!pm_accepts_block_stack_p(parser)) {
8681 return PM_TOKEN_KEYWORD_DO_BLOCK;
8682 }
8683 return PM_TOKEN_KEYWORD_DO;
8684 }
8685
8686 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;
8687 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;
8688 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;
8689 break;
8690 case 3:
8691 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;
8692 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;
8693 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;
8694 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;
8695 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;
8696 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;
8697 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;
8698 break;
8699 case 4:
8700 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;
8701 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;
8702 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;
8703 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;
8704 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;
8705 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;
8706 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;
8707 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;
8708 break;
8709 case 5:
8710 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;
8711 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;
8712 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;
8713 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;
8714 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;
8715 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;
8716 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;
8717 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;
8718 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;
8719 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;
8720 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;
8721 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;
8722 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;
8723 break;
8724 case 6:
8725 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;
8726 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;
8727 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;
8728 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;
8729 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;
8730 break;
8731 case 8:
8732 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;
8733 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;
8734 break;
8735 case 12:
8736 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;
8737 break;
8738 }
8739 }
8740
8741 if (encoding_changed) {
8742 return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8743 }
8744
8745 /* Identifiers usually start with an ASCII byte, for which the uppercase
8746 * check is a simple range comparison. This avoids the call into the
8747 * encoding module for every identifier. */
8748 if (*current_start < 0x80) {
8749 return (*current_start >= 'A' && *current_start <= 'Z') ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8750 }
8751 return pm_encoding_utf_8_isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8752}
8753
8758static bool
8759current_token_starts_line(pm_parser_t *parser) {
8760 return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n');
8761}
8762
8777static pm_token_type_t
8778lex_interpolation(pm_parser_t *parser, const uint8_t *pound) {
8779 // If there is no content following this #, then we're at the end of
8780 // the string and we can safely return string content.
8781 if (pound + 1 >= parser->end) {
8782 parser->current.end = pound + 1;
8783 return PM_TOKEN_STRING_CONTENT;
8784 }
8785
8786 // Now we'll check against the character that follows the #. If it
8787 // constitutes valid interplation, we'll handle that, otherwise we'll return
8788 // 0.
8789 switch (pound[1]) {
8790 case '@': {
8791 // In this case we may have hit an embedded instance or class variable.
8792 if (pound + 2 >= parser->end) {
8793 parser->current.end = pound + 1;
8794 return PM_TOKEN_STRING_CONTENT;
8795 }
8796
8797 // If we're looking at a @ and there's another @, then we'll skip past the
8798 // second @.
8799 const uint8_t *variable = pound + 2;
8800 if (*variable == '@' && pound + 3 < parser->end) variable++;
8801
8802 if (char_is_identifier_start(parser, variable, parser->end - variable)) {
8803 // At this point we're sure that we've either hit an embedded instance
8804 // or class variable. In this case we'll first need to check if we've
8805 // already consumed content.
8806 if (pound > parser->current.start) {
8807 parser->current.end = pound;
8808 return PM_TOKEN_STRING_CONTENT;
8809 }
8810
8811 // Otherwise we need to return the embedded variable token
8812 // and then switch to the embedded variable lex mode.
8813 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8814 parser->current.end = pound + 1;
8815 return PM_TOKEN_EMBVAR;
8816 }
8817
8818 // If we didn't get a valid interpolation, then this is just regular
8819 // string content. This is like if we get "#@-". In this case the caller
8820 // should keep lexing.
8821 parser->current.end = pound + 1;
8822 return 0;
8823 }
8824 case '$':
8825 // In this case we may have hit an embedded global variable. If there's
8826 // not enough room, then we'll just return string content.
8827 if (pound + 2 >= parser->end) {
8828 parser->current.end = pound + 1;
8829 return PM_TOKEN_STRING_CONTENT;
8830 }
8831
8832 // This is the character that we're going to check to see if it is the
8833 // start of an identifier that would indicate that this is a global
8834 // variable.
8835 const uint8_t *check = pound + 2;
8836
8837 if (pound[2] == '-') {
8838 if (pound + 3 >= parser->end) {
8839 parser->current.end = pound + 2;
8840 return PM_TOKEN_STRING_CONTENT;
8841 }
8842
8843 check++;
8844 }
8845
8846 // If the character that we're going to check is the start of an
8847 // identifier, or we don't have a - and the character is a decimal number
8848 // or a global name punctuation character, then we've hit an embedded
8849 // global variable.
8850 if (
8851 char_is_identifier_start(parser, check, parser->end - check) ||
8852 (pound[2] != '-' && (pm_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2])))
8853 ) {
8854 // In this case we've hit an embedded global variable. First check to
8855 // see if we've already consumed content. If we have, then we need to
8856 // return that content as string content first.
8857 if (pound > parser->current.start) {
8858 parser->current.end = pound;
8859 return PM_TOKEN_STRING_CONTENT;
8860 }
8861
8862 // Otherwise, we need to return the embedded variable token and switch
8863 // to the embedded variable lex mode.
8864 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8865 parser->current.end = pound + 1;
8866 return PM_TOKEN_EMBVAR;
8867 }
8868
8869 // In this case we've hit a #$ that does not indicate a global variable.
8870 // In this case we'll continue lexing past it.
8871 parser->current.end = pound + 1;
8872 return 0;
8873 case '{':
8874 // In this case it's the start of an embedded expression. If we have
8875 // already consumed content, then we need to return that content as string
8876 // content first.
8877 if (pound > parser->current.start) {
8878 parser->current.end = pound;
8879 return PM_TOKEN_STRING_CONTENT;
8880 }
8881
8882 parser->enclosure_nesting++;
8883
8884 // Otherwise we'll skip past the #{ and begin lexing the embedded
8885 // expression.
8886 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBEXPR });
8887 parser->current.end = pound + 2;
8888 parser->command_start = true;
8889 pm_enclosure_frame_push(parser);
8890 return PM_TOKEN_EMBEXPR_BEGIN;
8891 default:
8892 // In this case we've hit a # that doesn't constitute interpolation. We'll
8893 // mark that by returning the not provided token type. This tells the
8894 // consumer to keep lexing forward.
8895 parser->current.end = pound + 1;
8896 return 0;
8897 }
8898}
8899
8900static const uint8_t PM_ESCAPE_FLAG_NONE = 0x0;
8901static const uint8_t PM_ESCAPE_FLAG_CONTROL = 0x1;
8902static const uint8_t PM_ESCAPE_FLAG_META = 0x2;
8903static const uint8_t PM_ESCAPE_FLAG_SINGLE = 0x4;
8904static const uint8_t PM_ESCAPE_FLAG_REGEXP = 0x8;
8905
8909static const bool ascii_printable_chars[] = {
8910 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
8911 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8912 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8913 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8914 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8915 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
8916 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8917 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
8918};
8919
8920static PRISM_INLINE bool
8921char_is_ascii_printable(const uint8_t b) {
8922 return (b < 0x80) && ascii_printable_chars[b];
8923}
8924
8929static PRISM_INLINE uint8_t
8930escape_hexadecimal_digit(const uint8_t value) {
8931 return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9);
8932}
8933
8939static PRISM_INLINE uint32_t
8940escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location, const uint8_t flags) {
8941 uint32_t value = 0;
8942 for (size_t index = 0; index < length; index++) {
8943 if (index != 0) value <<= 4;
8944 value |= escape_hexadecimal_digit(string[index]);
8945 }
8946
8947 // Here we're going to verify that the value is actually a valid Unicode
8948 // codepoint and not a surrogate pair.
8949 if (value >= 0xD800 && value <= 0xDFFF) {
8950 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8951 // In regexp context, defer the error to regexp encoding
8952 // validation where we can produce a regexp-specific message.
8953 } else if (error_location != NULL) {
8954 pm_parser_err(parser, error_location->start, error_location->length, PM_ERR_ESCAPE_INVALID_UNICODE);
8955 } else {
8956 pm_parser_err(parser, U32(string - parser->start), U32(length), PM_ERR_ESCAPE_INVALID_UNICODE);
8957 }
8958 return 0xFFFD;
8959 }
8960
8961 return value;
8962}
8963
8967static PRISM_INLINE uint8_t
8968escape_byte(uint8_t value, const uint8_t flags) {
8969 if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x9f;
8970 if (flags & PM_ESCAPE_FLAG_META) value |= 0x80;
8971 return value;
8972}
8973
8977static PRISM_INLINE void
8978escape_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) {
8979 // \u escape sequences in string-like structures implicitly change the
8980 // encoding to UTF-8 if they are >= 0x80 or if they are used in a character
8981 // literal.
8982 if (value >= 0x80 || flags & PM_ESCAPE_FLAG_SINGLE) {
8983 if (parser->explicit_encoding != NULL && parser->explicit_encoding != PM_ENCODING_UTF_8_ENTRY) {
8984 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8985 // In regexp context, suppress this error — the regexp encoding
8986 // validation will produce a more specific error message.
8987 } else {
8988 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(end - start), PM_ERR_MIXED_ENCODING, parser->explicit_encoding->name);
8989 }
8990 }
8991
8992 parser->explicit_encoding = PM_ENCODING_UTF_8_ENTRY;
8993 }
8994
8995 if (!pm_buffer_append_unicode_codepoint(buffer, value)) {
8996 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8997 // In regexp context, defer the error to the regexp encoding
8998 // validation which produces a regexp-specific message.
8999 } else {
9000 pm_parser_err(parser, U32(start - parser->start), U32(end - start), PM_ERR_ESCAPE_INVALID_UNICODE);
9001 }
9002
9003 pm_buffer_append_byte(buffer, 0xEF);
9004 pm_buffer_append_byte(buffer, 0xBF);
9005 pm_buffer_append_byte(buffer, 0xBD);
9006 }
9007}
9008
9013static PRISM_INLINE void
9014escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t flags, uint8_t byte) {
9015 if (byte >= 0x80) {
9016 if (parser->explicit_encoding != NULL && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
9017 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9018 // In regexp context, suppress this error — the regexp encoding
9019 // validation will produce a more specific error message.
9020 } else {
9021 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_MIXED_ENCODING, parser->encoding->name);
9022 }
9023 }
9024
9025 parser->explicit_encoding = parser->encoding;
9026 }
9027
9028 pm_buffer_append_byte(buffer, byte);
9029}
9030
9046static PRISM_INLINE void
9047escape_write_byte(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags, uint8_t byte) {
9048 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9049 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", byte);
9050 }
9051
9052 escape_write_byte_encoded(parser, buffer, flags, byte);
9053}
9054
9058static PRISM_INLINE void
9059escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9060 size_t width;
9061 if (parser->encoding_changed) {
9062 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9063 } else {
9064 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9065 }
9066
9067 if (width == 1) {
9068 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);
9069 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(*parser->current.end++, flags));
9070 } else if (width > 1) {
9071 // Valid multibyte character. Just ignore escape.
9072 pm_buffer_t *b = (flags & PM_ESCAPE_FLAG_REGEXP) ? regular_expression_buffer : buffer;
9073 pm_buffer_append_bytes(b, parser->current.end, width);
9074 parser->current.end += width;
9075 } else {
9076 // Assume the next character wasn't meant to be part of this escape
9077 // sequence since it is invalid. Add an error and move on.
9078 parser->current.end++;
9079 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9080 }
9081}
9082
9088static void
9089escape_read_warn(pm_parser_t *parser, uint8_t flags, uint8_t flag, const char *type) {
9090#define FLAG(value) ((value & PM_ESCAPE_FLAG_CONTROL) ? "\\C-" : (value & PM_ESCAPE_FLAG_META) ? "\\M-" : "")
9091
9092 PM_PARSER_WARN_TOKEN_FORMAT(
9093 parser,
9094 &parser->current,
9095 PM_WARN_INVALID_CHARACTER,
9096 FLAG(flags),
9097 FLAG(flag),
9098 type
9099 );
9100
9101#undef FLAG
9102}
9103
9107static void
9108escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9109 uint8_t peeked = peek(parser);
9110 switch (peeked) {
9111 case '\\': {
9112 parser->current.end++;
9113 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\\', flags));
9114 return;
9115 }
9116 case '\'': {
9117 parser->current.end++;
9118 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\'', flags));
9119 return;
9120 }
9121 case 'a': {
9122 parser->current.end++;
9123 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\a', flags));
9124 return;
9125 }
9126 case 'b': {
9127 parser->current.end++;
9128 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\b', flags));
9129 return;
9130 }
9131 case 'e': {
9132 parser->current.end++;
9133 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\033', flags));
9134 return;
9135 }
9136 case 'f': {
9137 parser->current.end++;
9138 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\f', flags));
9139 return;
9140 }
9141 case 'n': {
9142 parser->current.end++;
9143 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\n', flags));
9144 return;
9145 }
9146 case 'r': {
9147 parser->current.end++;
9148 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\r', flags));
9149 return;
9150 }
9151 case 's': {
9152 parser->current.end++;
9153 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(' ', flags));
9154 return;
9155 }
9156 case 't': {
9157 parser->current.end++;
9158 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\t', flags));
9159 return;
9160 }
9161 case 'v': {
9162 parser->current.end++;
9163 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\v', flags));
9164 return;
9165 }
9166 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': {
9167 uint8_t value = (uint8_t) (*parser->current.end - '0');
9168 parser->current.end++;
9169
9170 if (pm_char_is_octal_digit(peek(parser))) {
9171 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9172 parser->current.end++;
9173
9174 if (pm_char_is_octal_digit(peek(parser))) {
9175 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9176 parser->current.end++;
9177 }
9178 }
9179
9180 value = escape_byte(value, flags);
9181 escape_write_byte(parser, buffer, regular_expression_buffer, flags, value);
9182 return;
9183 }
9184 case 'x': {
9185 const uint8_t *start = parser->current.end - 1;
9186
9187 parser->current.end++;
9188 uint8_t byte = peek(parser);
9189
9190 if (pm_char_is_hexadecimal_digit(byte)) {
9191 uint8_t value = escape_hexadecimal_digit(byte);
9192 parser->current.end++;
9193
9194 byte = peek(parser);
9195 if (pm_char_is_hexadecimal_digit(byte)) {
9196 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(byte));
9197 parser->current.end++;
9198 }
9199
9200 value = escape_byte(value, flags);
9201 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9202 if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) {
9203 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", value);
9204 } else {
9205 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9206 }
9207 }
9208
9209 escape_write_byte_encoded(parser, buffer, flags, value);
9210 } else {
9211 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL);
9212 }
9213
9214 return;
9215 }
9216 case 'u': {
9217 const uint8_t *start = parser->current.end - 1;
9218 parser->current.end++;
9219
9220 if (parser->current.end == parser->end) {
9221 const uint8_t *start = parser->current.end - 2;
9222 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9223 } else if (peek(parser) == '{') {
9224 const uint8_t *unicode_codepoints_start = parser->current.end - 2;
9225 parser->current.end++;
9226
9227 size_t whitespace;
9228 while (true) {
9229 if ((whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end)) > 0) {
9230 parser->current.end += whitespace;
9231 } else if (peek(parser) == '\\' && peek_offset(parser, 1) == 'n') {
9232 // This is super hacky, but it gets us nicer error
9233 // messages because we can still pass it off to the
9234 // regular expression engine even if we hit an
9235 // unterminated regular expression.
9236 parser->current.end += 2;
9237 } else {
9238 break;
9239 }
9240 }
9241
9242 const uint8_t *extra_codepoints_start = NULL;
9243 int codepoints_count = 0;
9244
9245 while ((parser->current.end < parser->end) && (*parser->current.end != '}')) {
9246 const uint8_t *unicode_start = parser->current.end;
9247 size_t hexadecimal_length = pm_strspn_hexadecimal_digit(parser->current.end, parser->end - parser->current.end);
9248
9249 if (hexadecimal_length > 6) {
9250 // \u{nnnn} character literal allows only 1-6 hexadecimal digits
9251 pm_parser_err(parser, U32(unicode_start - parser->start), U32(hexadecimal_length), PM_ERR_ESCAPE_INVALID_UNICODE_LONG);
9252 } else if (hexadecimal_length == 0) {
9253 // there are not hexadecimal characters
9254
9255 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9256 // If this is a regular expression, we are going to
9257 // let the regular expression engine handle this
9258 // error instead of us because we don't know at this
9259 // point if we're inside a comment in /x mode.
9260 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9261 } else {
9262 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE);
9263 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9264 }
9265
9266 return;
9267 }
9268
9269 parser->current.end += hexadecimal_length;
9270 codepoints_count++;
9271 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count == 2) {
9272 extra_codepoints_start = unicode_start;
9273 }
9274
9275 uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL, flags);
9276 escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value);
9277
9278 parser->current.end += pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
9279 }
9280
9281 // ?\u{nnnn} character literal should contain only one codepoint
9282 // and cannot be like ?\u{nnnn mmmm}.
9283 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count > 1) {
9284 pm_parser_err(parser, U32(extra_codepoints_start - parser->start), U32(parser->current.end - 1 - extra_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL);
9285 }
9286
9287 if (parser->current.end == parser->end) {
9288 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);
9289 } else if (peek(parser) == '}') {
9290 parser->current.end++;
9291 } else {
9292 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9293 // If this is a regular expression, we are going to let
9294 // the regular expression engine handle this error
9295 // instead of us because we don't know at this point if
9296 // we're inside a comment in /x mode.
9297 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9298 } else {
9299 pm_parser_err(parser, U32(unicode_codepoints_start - parser->start), U32(parser->current.end - unicode_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9300 }
9301 }
9302
9303 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9304 pm_buffer_append_bytes(regular_expression_buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start));
9305 }
9306 } else {
9307 size_t length = pm_strspn_hexadecimal_digit(parser->current.end, MIN(parser->end - parser->current.end, 4));
9308
9309 if (length == 0) {
9310 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9311 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9312 } else {
9313 const uint8_t *start = parser->current.end - 2;
9314 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9315 }
9316 } else if (length == 4) {
9317 uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL, flags);
9318
9319 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9320 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start));
9321 }
9322
9323 escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value);
9324 parser->current.end += 4;
9325 } else {
9326 parser->current.end += length;
9327
9328 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9329 // If this is a regular expression, we are going to let
9330 // the regular expression engine handle this error
9331 // instead of us.
9332 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9333 } else {
9334 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE);
9335 }
9336 }
9337 }
9338
9339 return;
9340 }
9341 case 'c': {
9342 parser->current.end++;
9343 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9344 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9345 }
9346
9347 if (parser->current.end == parser->end) {
9348 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9349 return;
9350 }
9351
9352 uint8_t peeked = peek(parser);
9353 switch (peeked) {
9354 case '?': {
9355 parser->current.end++;
9356 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9357 return;
9358 }
9359 case '\\':
9360 parser->current.end++;
9361
9362 if (match(parser, 'u') || match(parser, 'U')) {
9363 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9364 return;
9365 }
9366
9367 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9368 return;
9369 case ' ':
9370 parser->current.end++;
9371 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9372 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9373 return;
9374 case '\t':
9375 parser->current.end++;
9376 escape_read_warn(parser, flags, 0, "\\t");
9377 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9378 return;
9379 default: {
9380 if (!char_is_ascii_printable(peeked)) {
9381 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9382 return;
9383 }
9384
9385 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);
9386 parser->current.end++;
9387 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9388 return;
9389 }
9390 }
9391 }
9392 case 'C': {
9393 parser->current.end++;
9394 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9395 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9396 }
9397
9398 if (peek(parser) != '-') {
9399 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9400 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9401 return;
9402 }
9403
9404 parser->current.end++;
9405 if (parser->current.end == parser->end) {
9406 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9407 return;
9408 }
9409
9410 uint8_t peeked = peek(parser);
9411 switch (peeked) {
9412 case '?': {
9413 parser->current.end++;
9414 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9415 return;
9416 }
9417 case '\\':
9418 parser->current.end++;
9419
9420 if (match(parser, 'u') || match(parser, 'U')) {
9421 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9422 return;
9423 }
9424
9425 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9426 return;
9427 case ' ':
9428 parser->current.end++;
9429 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9430 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9431 return;
9432 case '\t':
9433 parser->current.end++;
9434 escape_read_warn(parser, flags, 0, "\\t");
9435 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9436 return;
9437 default: {
9438 if (!char_is_ascii_printable(peeked)) {
9439 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9440 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9441 return;
9442 }
9443
9444 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);
9445 parser->current.end++;
9446 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9447 return;
9448 }
9449 }
9450 }
9451 case 'M': {
9452 parser->current.end++;
9453 if (flags & PM_ESCAPE_FLAG_META) {
9454 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
9455 }
9456
9457 if (peek(parser) != '-') {
9458 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9459 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9460 return;
9461 }
9462
9463 parser->current.end++;
9464 if (parser->current.end == parser->end) {
9465 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META);
9466 return;
9467 }
9468
9469 uint8_t peeked = peek(parser);
9470 switch (peeked) {
9471 case '\\':
9472 parser->current.end++;
9473
9474 if (match(parser, 'u') || match(parser, 'U')) {
9475 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9476 return;
9477 }
9478
9479 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_META);
9480 return;
9481 case ' ':
9482 parser->current.end++;
9483 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_META, "\\s");
9484 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9485 return;
9486 case '\t':
9487 parser->current.end++;
9488 escape_read_warn(parser, flags & ((uint8_t) ~PM_ESCAPE_FLAG_CONTROL), PM_ESCAPE_FLAG_META, "\\t");
9489 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9490 return;
9491 default:
9492 if (!char_is_ascii_printable(peeked)) {
9493 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9494 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9495 return;
9496 }
9497
9498 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);
9499 parser->current.end++;
9500 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9501 return;
9502 }
9503 }
9504 case '\r': {
9505 if (peek_offset(parser, 1) == '\n') {
9506 if (parser->heredoc_end == NULL) pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 2);
9507 parser->current.end += 2;
9508 escape_write_byte_encoded(parser, buffer, flags, escape_byte('\n', flags));
9509 return;
9510 }
9512 }
9513 default: {
9514 if ((flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) && !char_is_ascii_printable(peeked)) {
9515 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9516 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9517 return;
9518 }
9519 if (parser->current.end < parser->end) {
9520 escape_write_escape_encoded(parser, buffer, regular_expression_buffer, flags);
9521 } else {
9522 pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER);
9523 }
9524 return;
9525 }
9526 }
9527}
9528
9554static pm_token_type_t
9555lex_question_mark(pm_parser_t *parser) {
9556 if (lex_state_end_p(parser)) {
9557 lex_state_set(parser, PM_LEX_STATE_BEG);
9558 return PM_TOKEN_QUESTION_MARK;
9559 }
9560
9561 /*
9562 * A literal takes its encoding from its own contents. Literals that push a
9563 * lex mode clear this in lex_mode_push_*; a character literal is lexed
9564 * inline, so it clears the encoding here.
9565 */
9566 parser->explicit_encoding = NULL;
9567
9568 if (parser->current.end >= parser->end) {
9569 pm_parser_err_current(parser, PM_ERR_INCOMPLETE_QUESTION_MARK);
9570 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9571 return PM_TOKEN_CHARACTER_LITERAL;
9572 }
9573
9574 if (pm_char_is_whitespace(*parser->current.end)) {
9575 lex_state_set(parser, PM_LEX_STATE_BEG);
9576 return PM_TOKEN_QUESTION_MARK;
9577 }
9578
9579 lex_state_set(parser, PM_LEX_STATE_BEG);
9580
9581 if (match(parser, '\\')) {
9582 lex_state_set(parser, PM_LEX_STATE_END);
9583
9584 pm_buffer_t buffer;
9585 pm_buffer_init(&buffer, 3);
9586
9587 escape_read(parser, &buffer, NULL, PM_ESCAPE_FLAG_SINGLE);
9588
9589 // Copy buffer data into the arena and free the heap buffer.
9590 void *arena_data = pm_arena_memdup(parser->arena, buffer.value, buffer.length, PRISM_ALIGNOF(uint8_t));
9591 pm_string_constant_init(&parser->current_string, (const char *) arena_data, buffer.length);
9592 pm_buffer_cleanup(&buffer);
9593
9594 return PM_TOKEN_CHARACTER_LITERAL;
9595 } else {
9596 size_t encoding_width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9597
9598 // Ternary operators can have a ? immediately followed by an identifier
9599 // which starts with an underscore. We check for this case here.
9600 if (
9601 !(parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end) || peek(parser) == '_') ||
9602 (
9603 (parser->current.end + encoding_width >= parser->end) ||
9604 !char_is_identifier(parser, parser->current.end + encoding_width, parser->end - (parser->current.end + encoding_width))
9605 )
9606 ) {
9607 lex_state_set(parser, PM_LEX_STATE_END);
9608 parser->current.end += encoding_width;
9609 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9610 return PM_TOKEN_CHARACTER_LITERAL;
9611 }
9612 }
9613
9614 return PM_TOKEN_QUESTION_MARK;
9615}
9616
9621static pm_token_type_t
9622lex_at_variable(pm_parser_t *parser) {
9623 pm_token_type_t type = match(parser, '@') ? PM_TOKEN_CLASS_VARIABLE : PM_TOKEN_INSTANCE_VARIABLE;
9624 const uint8_t *end = parser->end;
9625
9626 size_t width;
9627 if ((width = char_is_identifier_start(parser, parser->current.end, end - parser->current.end)) > 0) {
9628 parser->current.end += width;
9629
9630 while ((width = char_is_identifier(parser, parser->current.end, end - parser->current.end)) > 0) {
9631 parser->current.end += width;
9632 }
9633 } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) {
9634 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
9635 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) {
9636 diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3;
9637 }
9638
9639 size_t width = parser->encoding->char_width(parser->current.end, end - parser->current.end);
9640 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start);
9641 } else {
9642 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_CLASS_VARIABLE_BARE : PM_ERR_INSTANCE_VARIABLE_BARE;
9643 pm_parser_err_token(parser, &parser->current, diag_id);
9644 }
9645
9646 // If we're lexing an embedded variable, then we need to pop back into the
9647 // parent lex context.
9648 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
9649 lex_mode_pop(parser);
9650 }
9651
9652 return type;
9653}
9654
9658static PRISM_INLINE void
9659parser_lex_callback(pm_parser_t *parser) {
9660 if (parser->lex_callback.callback) {
9661 parser->lex_callback.callback(parser, &parser->current, parser->lex_callback.data);
9662 }
9663}
9664
9669parser_comment(pm_parser_t *parser, pm_comment_type_t type) {
9670 pm_comment_t *comment = (pm_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_comment_t), PRISM_ALIGNOF(pm_comment_t));
9671
9672 *comment = (pm_comment_t) {
9673 .type = type,
9674 .location = TOK2LOC(parser, &parser->current)
9675 };
9676
9677 return comment;
9678}
9679
9685static pm_token_type_t
9686lex_embdoc(pm_parser_t *parser) {
9687 // First, lex out the EMBDOC_BEGIN token.
9688 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9689
9690 if (newline == NULL) {
9691 parser->current.end = parser->end;
9692 } else {
9693 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9694 parser->current.end = newline + 1;
9695 }
9696
9697 parser->current.type = PM_TOKEN_EMBDOC_BEGIN;
9698 parser_lex_callback(parser);
9699
9700 // Now, create a comment that is going to be attached to the parser.
9701 const uint8_t *comment_start = parser->current.start;
9702 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC);
9703
9704 // Now, loop until we find the end of the embedded documentation or the end
9705 // of the file.
9706 while (parser->current.end + 4 <= parser->end) {
9707 parser->current.start = parser->current.end;
9708
9709 // If we've hit the end of the embedded documentation then we'll return
9710 // that token here.
9711 if (
9712 (memcmp(parser->current.end, "=end", 4) == 0) &&
9713 (
9714 (parser->current.end + 4 == parser->end) || // end of file
9715 pm_char_is_whitespace(parser->current.end[4]) || // whitespace
9716 (parser->current.end[4] == '\0') || // NUL or end of script
9717 (parser->current.end[4] == '\004') || // ^D
9718 (parser->current.end[4] == '\032') // ^Z
9719 )
9720 ) {
9721 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9722
9723 if (newline == NULL) {
9724 parser->current.end = parser->end;
9725 } else {
9726 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9727 parser->current.end = newline + 1;
9728 }
9729
9730 parser->current.type = PM_TOKEN_EMBDOC_END;
9731 parser_lex_callback(parser);
9732
9733 comment->location.length = (uint32_t) (parser->current.end - comment_start);
9734 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9735
9736 return PM_TOKEN_EMBDOC_END;
9737 }
9738
9739 // Otherwise, we'll parse until the end of the line and return a line of
9740 // embedded documentation.
9741 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9742
9743 if (newline == NULL) {
9744 parser->current.end = parser->end;
9745 } else {
9746 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9747 parser->current.end = newline + 1;
9748 }
9749
9750 parser->current.type = PM_TOKEN_EMBDOC_LINE;
9751 parser_lex_callback(parser);
9752 }
9753
9754 pm_parser_err_current(parser, PM_ERR_EMBDOC_TERM);
9755
9756 comment->location.length = (uint32_t) (parser->current.end - comment_start);
9757 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9758
9759 return PM_TOKEN_EOF;
9760}
9761
9767static PRISM_INLINE void
9768parser_lex_ignored_newline(pm_parser_t *parser) {
9769 parser->current.type = PM_TOKEN_IGNORED_NEWLINE;
9770 parser_lex_callback(parser);
9771}
9772
9782static PRISM_INLINE void
9783parser_flush_heredoc_end(pm_parser_t *parser) {
9784 assert(parser->heredoc_end <= parser->end);
9785 parser->next_start = parser->heredoc_end;
9786 parser->heredoc_end = NULL;
9787}
9788
9792static bool
9793parser_end_of_line_p(const pm_parser_t *parser) {
9794 const uint8_t *cursor = parser->current.end;
9795
9796 while (cursor < parser->end && *cursor != '\n' && *cursor != '#') {
9797 if (!pm_char_is_inline_whitespace(*cursor++)) return false;
9798 }
9799
9800 return true;
9801}
9802
9821typedef struct {
9827
9832 const uint8_t *cursor;
9834
9854
9858static PRISM_INLINE void
9859pm_token_buffer_push_byte(pm_token_buffer_t *token_buffer, uint8_t byte) {
9860 pm_buffer_append_byte(&token_buffer->buffer, byte);
9861}
9862
9863static PRISM_INLINE void
9864pm_regexp_token_buffer_push_byte(pm_regexp_token_buffer_t *token_buffer, uint8_t byte) {
9865 pm_buffer_append_byte(&token_buffer->regexp_buffer, byte);
9866}
9867
9871static PRISM_INLINE size_t
9872parser_char_width(const pm_parser_t *parser) {
9873 size_t width;
9874 if (parser->encoding_changed) {
9875 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9876 } else {
9877 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9878 }
9879
9880 // TODO: If the character is invalid in the given encoding, then we'll just
9881 // push one byte into the buffer. This should actually be an error.
9882 return (width == 0 ? 1 : width);
9883}
9884
9888static void
9889pm_token_buffer_push_escaped(pm_token_buffer_t *token_buffer, pm_parser_t *parser) {
9890 size_t width = parser_char_width(parser);
9891 pm_buffer_append_bytes(&token_buffer->buffer, parser->current.end, width);
9892 parser->current.end += width;
9893}
9894
9895static void
9896pm_regexp_token_buffer_push_escaped(pm_regexp_token_buffer_t *token_buffer, pm_parser_t *parser) {
9897 size_t width = parser_char_width(parser);
9898 const uint8_t *start = parser->current.end;
9899 pm_buffer_append_bytes(&token_buffer->base.buffer, start, width);
9900 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, width);
9901 parser->current.end += width;
9902}
9903
9910static PRISM_INLINE void
9911pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9912 // Copy buffer data into the arena and free the heap buffer.
9913 size_t len = pm_buffer_length(&token_buffer->buffer);
9914 void *arena_data = pm_arena_memdup(parser->arena, pm_buffer_value(&token_buffer->buffer), len, PRISM_ALIGNOF(uint8_t));
9915 pm_string_constant_init(&parser->current_string, (const char *) arena_data, len);
9916 pm_buffer_cleanup(&token_buffer->buffer);
9917}
9918
9919static PRISM_INLINE void
9920pm_regexp_token_buffer_copy(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9921 pm_token_buffer_copy(parser, &token_buffer->base);
9922 pm_buffer_cleanup(&token_buffer->regexp_buffer);
9923}
9924
9934static void
9935pm_token_buffer_flush(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9936 if (token_buffer->cursor == NULL) {
9937 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9938 } else {
9939 pm_buffer_append_bytes(&token_buffer->buffer, token_buffer->cursor, (size_t) (parser->current.end - token_buffer->cursor));
9940 pm_token_buffer_copy(parser, token_buffer);
9941 }
9942}
9943
9944static void
9945pm_regexp_token_buffer_flush(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9946 if (token_buffer->base.cursor == NULL) {
9947 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9948 } else {
9949 const uint8_t *cursor = token_buffer->base.cursor;
9950 size_t length = (size_t) (parser->current.end - cursor);
9951 pm_buffer_append_bytes(&token_buffer->base.buffer, cursor, length);
9952 pm_buffer_append_bytes(&token_buffer->regexp_buffer, cursor, length);
9953 pm_regexp_token_buffer_copy(parser, token_buffer);
9954 }
9955}
9956
9957#define PM_TOKEN_BUFFER_DEFAULT_SIZE 16
9958
9967static void
9968pm_token_buffer_escape(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9969 const uint8_t *start;
9970 if (token_buffer->cursor == NULL) {
9971 pm_buffer_init(&token_buffer->buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9972 start = parser->current.start;
9973 } else {
9974 start = token_buffer->cursor;
9975 }
9976
9977 const uint8_t *end = parser->current.end - 1;
9978 assert(end >= start);
9979 pm_buffer_append_bytes(&token_buffer->buffer, start, (size_t) (end - start));
9980
9981 token_buffer->cursor = end;
9982}
9983
9984static void
9985pm_regexp_token_buffer_escape(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9986 const uint8_t *start;
9987 if (token_buffer->base.cursor == NULL) {
9988 pm_buffer_init(&token_buffer->base.buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9989 pm_buffer_init(&token_buffer->regexp_buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9990 start = parser->current.start;
9991 } else {
9992 start = token_buffer->base.cursor;
9993 }
9994
9995 const uint8_t *end = parser->current.end - 1;
9996 pm_buffer_append_bytes(&token_buffer->base.buffer, start, (size_t) (end - start));
9997 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, (size_t) (end - start));
9998
9999 token_buffer->base.cursor = end;
10000}
10001
10002#undef PM_TOKEN_BUFFER_DEFAULT_SIZE
10003
10008static PRISM_INLINE size_t
10009pm_heredoc_strspn_inline_whitespace(pm_parser_t *parser, const uint8_t **cursor, pm_heredoc_indent_t indent) {
10010 size_t whitespace = 0;
10011
10012 switch (indent) {
10013 case PM_HEREDOC_INDENT_NONE:
10014 // Do nothing, we can't match a terminator with
10015 // indentation and there's no need to calculate common
10016 // whitespace.
10017 break;
10018 case PM_HEREDOC_INDENT_DASH:
10019 // Skip past inline whitespace.
10020 *cursor += pm_strspn_inline_whitespace(*cursor, parser->end - *cursor);
10021 break;
10022 case PM_HEREDOC_INDENT_TILDE:
10023 // Skip past inline whitespace and calculate common
10024 // whitespace.
10025 while (*cursor < parser->end && pm_char_is_inline_whitespace(**cursor)) {
10026 if (**cursor == '\t') {
10027 whitespace = (whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
10028 } else {
10029 whitespace++;
10030 }
10031 (*cursor)++;
10032 }
10033
10034 break;
10035 }
10036
10037 return whitespace;
10038}
10039
10044static uint8_t
10045pm_lex_percent_delimiter(pm_parser_t *parser) {
10046 size_t eol_length = match_eol(parser);
10047
10048 if (eol_length) {
10049 if (parser->heredoc_end) {
10050 // If we have already lexed a heredoc, then the newline has already
10051 // been added to the list. In this case we want to just flush the
10052 // heredoc end.
10053 parser_flush_heredoc_end(parser);
10054 } else {
10055 // Otherwise, we'll add the newline to the list of newlines.
10056 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + U32(eol_length));
10057 }
10058
10059 uint8_t delimiter = *parser->current.end;
10060
10061 // If our delimiter is \r\n, we want to treat it as if it's \n.
10062 // For example, %\r\nfoo\r\n should be "foo"
10063 if (eol_length == 2) {
10064 delimiter = *(parser->current.end + 1);
10065 }
10066
10067 parser->current.end += eol_length;
10068 return delimiter;
10069 }
10070
10071 return *parser->current.end++;
10072}
10073
10078#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return
10079
10086static void
10087parser_lex(pm_parser_t *parser) {
10088 assert(parser->current.end <= parser->end);
10089 parser->previous = parser->current;
10090
10091 // This value mirrors cmd_state from CRuby.
10092 bool previous_command_start = parser->command_start;
10093 parser->command_start = false;
10094
10095 // This is used to communicate to the newline lexing function that we've
10096 // already seen a comment.
10097 bool lexed_comment = false;
10098
10099 // Here we cache the current value of the semantic token seen flag. This is
10100 // used to reset it in case we find a token that shouldn't flip this flag.
10101 unsigned int semantic_token_seen = parser->semantic_token_seen;
10102 parser->semantic_token_seen = true;
10103
10104 // We'll jump to this label when we are about to encounter an EOF.
10105 // If we still have lex_modes on the stack, we pop them so that cleanup
10106 // can happen. For example, we should still continue parsing after a heredoc
10107 // identifier, even if the heredoc body was syntax invalid.
10108 switch_lex_modes:
10109
10110 switch (parser->lex_modes.current->mode) {
10111 case PM_LEX_DEFAULT:
10112 case PM_LEX_EMBEXPR:
10113 case PM_LEX_EMBVAR:
10114
10115 // We have a specific named label here because we are going to jump back to
10116 // this location in the event that we have lexed a token that should not be
10117 // returned to the parser. This includes comments, ignored newlines, and
10118 // invalid tokens of some form.
10119 lex_next_token: {
10120 // If we have the special next_start pointer set, then we're going to jump
10121 // to that location and start lexing from there.
10122 if (parser->next_start != NULL) {
10123 parser->current.end = parser->next_start;
10124 parser->next_start = NULL;
10125 }
10126
10127 // This value mirrors space_seen from CRuby. It tracks whether or not
10128 // space has been eaten before the start of the next token.
10129 bool space_seen = false;
10130
10131 // First, we're going to skip past any whitespace at the front of the next
10132 // token. Skip runs of inline whitespace in bulk to avoid per-character
10133 // stores back to parser->current.end.
10134 bool chomping = true;
10135 while (chomping) {
10136 /* Skip the run of inline whitespace in bulk, then decide what
10137 * to do based on the first byte after it. Handling both in a
10138 * single pass avoids re-entering the scan when the run was
10139 * non-empty, which is the common case. */
10140 {
10141 static const uint8_t inline_whitespace[256] = {
10142 [' '] = 1, ['\t'] = 1, ['\f'] = 1, ['\v'] = 1
10143 };
10144 const uint8_t *scan = parser->current.end;
10145 while (scan < parser->end && inline_whitespace[*scan]) scan++;
10146 if (scan > parser->current.end) {
10147 parser->current.end = scan;
10148 space_seen = true;
10149 }
10150 if (scan >= parser->end) break;
10151 }
10152
10153 switch (*parser->current.end) {
10154 case '\r':
10155 if (match_eol_offset(parser, 1)) {
10156 chomping = false;
10157 } else {
10158 pm_parser_warn(parser, PM_TOKEN_END(parser, &parser->current), 1, PM_WARN_UNEXPECTED_CARRIAGE_RETURN);
10159 parser->current.end++;
10160 space_seen = true;
10161 }
10162 break;
10163 case '\\': {
10164 size_t eol_length = match_eol_offset(parser, 1);
10165 if (eol_length) {
10166 if (parser->heredoc_end) {
10167 parser->current.end = parser->heredoc_end;
10168 parser->heredoc_end = NULL;
10169 } else {
10170 parser->current.end += eol_length + 1;
10171 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10172 space_seen = true;
10173 }
10174 } else if (pm_char_is_inline_whitespace(*parser->current.end)) {
10175 parser->current.end += 2;
10176 } else {
10177 chomping = false;
10178 }
10179
10180 break;
10181 }
10182 default:
10183 chomping = false;
10184 break;
10185 }
10186 }
10187
10188 // Next, we'll set to start of this token to be the current end.
10189 parser->current.start = parser->current.end;
10190
10191 // We'll check if we're at the end of the file. If we are, then we
10192 // need to return the EOF token.
10193 if (parser->current.end >= parser->end) {
10194 // We may be missing closing tokens. We should pop modes one by one
10195 // to do the appropriate cleanup like moving next_start for heredocs.
10196 // Only when no mode is remaining will we actually emit the EOF token.
10197 if (parser->lex_modes.current->mode != PM_LEX_DEFAULT) {
10198 lex_mode_pop(parser);
10199 goto switch_lex_modes;
10200 }
10201
10202 // If we hit EOF, but the EOF came immediately after a newline,
10203 // set the start of the token to the newline. This way any EOF
10204 // errors will be reported as happening on that line rather than
10205 // a line after. For example "foo(\n" should report an error
10206 // on line 1 even though EOF technically occurs on line 2.
10207 if (parser->current.start > parser->start && (*(parser->current.start - 1) == '\n')) {
10208 parser->current.start -= 1;
10209 }
10210 LEX(PM_TOKEN_EOF);
10211 }
10212
10213 // Finally, we'll check the current character to determine the next
10214 // token.
10215 switch (*parser->current.end++) {
10216 case '\0': // NUL or end of script
10217 case '\004': // ^D
10218 case '\032': // ^Z
10219 parser->current.end--;
10220 LEX(PM_TOKEN_EOF);
10221
10222 case '#': { // comments
10223 const uint8_t *ending = next_newline(parser->current.end, parser->end - parser->current.end);
10224 parser->current.end = ending == NULL ? parser->end : ending;
10225
10226 // If we found a comment while lexing, then we're going to
10227 // add it to the list of comments in the file and keep
10228 // lexing.
10229 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE);
10230 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
10231
10232 parser->current.type = PM_TOKEN_COMMENT;
10233 parser_lex_callback(parser);
10234
10235 // Here, parse the comment to see if it's a magic comment
10236 // and potentially change state on the parser.
10237 if (!parser_lex_magic_comment(parser, semantic_token_seen) && (parser->current.start == parser->encoding_comment_start)) {
10238 ptrdiff_t length = parser->current.end - parser->current.start;
10239
10240 // If we didn't find a magic comment within the first
10241 // pass and we're at the start of the file, then we need
10242 // to do another pass to potentially find other patterns
10243 // for encoding comments.
10244 if (length >= 10 && !parser->encoding_locked) {
10245 parser_lex_magic_comment_encoding(parser);
10246 }
10247 }
10248
10249 /* The comment does not include its terminating newline,
10250 * which lexes through the newline handling below as its
10251 * own token. A comment that ends the file has no newline,
10252 * so the newline handling runs without one to emit. */
10253 if (ending == NULL) {
10254 lexed_comment = true;
10255 } else {
10256 parser->current.start = ending;
10257 parser->current.end = ending + 1;
10258 }
10259 }
10261 case '\r':
10262 case '\n': {
10263 parser->semantic_token_seen = semantic_token_seen & 0x1;
10264 size_t eol_length = match_eol_at(parser, parser->current.end - 1);
10265
10266 if (eol_length) {
10267 // The only way you can have carriage returns in this
10268 // particular loop is if you have a carriage return
10269 // followed by a newline. In that case we'll just skip
10270 // over the carriage return and continue lexing, in
10271 // order to make it so that the newline token
10272 // encapsulates both the carriage return and the
10273 // newline. Note that we need to check that we haven't
10274 // already lexed a comment here because that falls
10275 // through into here as well.
10276 if (!lexed_comment) {
10277 parser->current.end += eol_length - 1; // skip CR
10278 }
10279
10280 if (parser->heredoc_end == NULL) {
10281 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10282 }
10283 }
10284
10285 if (parser->heredoc_end) {
10286 parser_flush_heredoc_end(parser);
10287 }
10288
10289 // If this is an ignored newline, then we can continue lexing after
10290 // calling the callback with the ignored newline token.
10291 switch (lex_state_ignored_p(parser)) {
10292 case PM_IGNORED_NEWLINE_NONE:
10293 break;
10294 case PM_IGNORED_NEWLINE_PATTERN:
10295 if (parser->pattern_matching_newlines || parser->in_keyword_arg) {
10296 if (!lexed_comment) {
10297 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10298 parser_lex_callback(parser);
10299 }
10300
10301 lex_state_set(parser, PM_LEX_STATE_BEG);
10302 parser->command_start = true;
10303 parser->current.type = PM_TOKEN_NEWLINE;
10304 return;
10305 }
10307 case PM_IGNORED_NEWLINE_ALL:
10308 if (!lexed_comment) parser_lex_ignored_newline(parser);
10309 lexed_comment = false;
10310 goto lex_next_token;
10311 }
10312
10313 // Here we need to look ahead and see if there is a call operator
10314 // (either . or &.) that starts the next line. If there is, then this
10315 // is going to become an ignored newline and we're going to instead
10316 // return the call operator.
10317 const uint8_t *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start;
10318 next_content += pm_strspn_inline_whitespace(next_content, parser->end - next_content);
10319
10320 if (next_content < parser->end) {
10321 // If we hit a comment after a newline, then we're going to check
10322 // if it's ignored or if it's followed by a method call ('.').
10323 // If it is, then we're going to call the
10324 // callback with an ignored newline and then continue lexing.
10325 // Otherwise we'll return a regular newline.
10326 if (next_content[0] == '#') {
10327 // Here we look for a "." or "&." following a "\n".
10328 const uint8_t *following = next_newline(next_content, parser->end - next_content);
10329
10330 while (following && (following + 1 < parser->end)) {
10331 following++;
10332 following += pm_strspn_inline_whitespace(following, parser->end - following);
10333
10334 // If this is not followed by a comment, then we can break out
10335 // of this loop.
10336 if (peek_at(parser, following) != '#') break;
10337
10338 // If there is a comment, then we need to find the end of the
10339 // comment and continue searching from there.
10340 following = next_newline(following, parser->end - following);
10341 }
10342
10343 // If the lex state was ignored, we will lex the
10344 // ignored newline.
10345 if (lex_state_ignored_p(parser)) {
10346 if (!lexed_comment) parser_lex_ignored_newline(parser);
10347 lexed_comment = false;
10348 goto lex_next_token;
10349 }
10350
10351 // If we hit a '.' or a '&.' we will lex the ignored
10352 // newline.
10353 if (following && (
10354 (peek_at(parser, following) == '.') ||
10355 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10356 )) {
10357 if (!lexed_comment) parser_lex_ignored_newline(parser);
10358 lexed_comment = false;
10359 goto lex_next_token;
10360 }
10361
10362
10363 // If we are parsing as CRuby 4.0 or later and we
10364 // hit a '&&' or a '||' then we will lex the ignored
10365 // newline.
10366 if (
10367 (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) &&
10368 following && (
10369 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
10370 (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
10371 (
10372 peek_at(parser, following) == 'a' &&
10373 peek_at(parser, following + 1) == 'n' &&
10374 peek_at(parser, following + 2) == 'd' &&
10375 peek_at(parser, next_content + 3) != '!' &&
10376 peek_at(parser, next_content + 3) != '?' &&
10377 !char_is_identifier(parser, following + 3, parser->end - (following + 3))
10378 ) ||
10379 (
10380 peek_at(parser, following) == 'o' &&
10381 peek_at(parser, following + 1) == 'r' &&
10382 peek_at(parser, next_content + 2) != '!' &&
10383 peek_at(parser, next_content + 2) != '?' &&
10384 !char_is_identifier(parser, following + 2, parser->end - (following + 2))
10385 )
10386 )
10387 ) {
10388 if (!lexed_comment) parser_lex_ignored_newline(parser);
10389 lexed_comment = false;
10390 goto lex_next_token;
10391 }
10392 }
10393
10394 // If we hit a . after a newline, then we're in a call chain and
10395 // we need to return the call operator.
10396 if (next_content[0] == '.') {
10397 /* A beginless range on the next line means this
10398 * newline terminates the statement rather than
10399 * continuing a method chain. */
10400 if (peek_at(parser, next_content + 1) == '.') {
10401 if (!lexed_comment) {
10402 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10403 parser_lex_callback(parser);
10404 }
10405
10406 lex_state_set(parser, PM_LEX_STATE_BEG);
10407 parser->command_start = true;
10408 parser->current.type = PM_TOKEN_NEWLINE;
10409 return;
10410 }
10411
10412 if (!lexed_comment) parser_lex_ignored_newline(parser);
10413 lex_state_set(parser, PM_LEX_STATE_DOT);
10414 parser->current.start = next_content;
10415 parser->current.end = next_content + 1;
10416 parser->next_start = NULL;
10417 LEX(PM_TOKEN_DOT);
10418 }
10419
10420 // If we hit a &. after a newline, then we're in a call chain and
10421 // we need to return the call operator.
10422 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '.') {
10423 if (!lexed_comment) parser_lex_ignored_newline(parser);
10424 lex_state_set(parser, PM_LEX_STATE_DOT);
10425 parser->current.start = next_content;
10426 parser->current.end = next_content + 2;
10427 parser->next_start = NULL;
10428 LEX(PM_TOKEN_AMPERSAND_DOT);
10429 }
10430
10431 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
10432 // If we hit an && then we are in a logical chain
10433 // and we need to return the logical operator.
10434 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
10435 if (!lexed_comment) parser_lex_ignored_newline(parser);
10436 lex_state_set(parser, PM_LEX_STATE_BEG);
10437 parser->current.start = next_content;
10438 parser->current.end = next_content + 2;
10439 parser->next_start = NULL;
10440 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10441 }
10442
10443 // If we hit a || then we are in a logical chain and
10444 // we need to return the logical operator.
10445 if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
10446 if (!lexed_comment) parser_lex_ignored_newline(parser);
10447 lex_state_set(parser, PM_LEX_STATE_BEG);
10448 parser->current.start = next_content;
10449 parser->current.end = next_content + 2;
10450 parser->next_start = NULL;
10451 LEX(PM_TOKEN_PIPE_PIPE);
10452 }
10453
10454 // If we hit an 'and' then we are in a logical chain
10455 // and we need to return the logical operator.
10456 if (
10457 peek_at(parser, next_content) == 'a' &&
10458 peek_at(parser, next_content + 1) == 'n' &&
10459 peek_at(parser, next_content + 2) == 'd' &&
10460 peek_at(parser, next_content + 3) != '!' &&
10461 peek_at(parser, next_content + 3) != '?' &&
10462 !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
10463 ) {
10464 if (!lexed_comment) parser_lex_ignored_newline(parser);
10465 lex_state_set(parser, PM_LEX_STATE_BEG);
10466 parser->current.start = next_content;
10467 parser->current.end = next_content + 3;
10468 parser->next_start = NULL;
10469 parser->command_start = true;
10470 LEX(PM_TOKEN_KEYWORD_AND);
10471 }
10472
10473 // If we hit a 'or' then we are in a logical chain
10474 // and we need to return the logical operator.
10475 if (
10476 peek_at(parser, next_content) == 'o' &&
10477 peek_at(parser, next_content + 1) == 'r' &&
10478 peek_at(parser, next_content + 2) != '!' &&
10479 peek_at(parser, next_content + 2) != '?' &&
10480 !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
10481 ) {
10482 if (!lexed_comment) parser_lex_ignored_newline(parser);
10483 lex_state_set(parser, PM_LEX_STATE_BEG);
10484 parser->current.start = next_content;
10485 parser->current.end = next_content + 2;
10486 parser->next_start = NULL;
10487 parser->command_start = true;
10488 LEX(PM_TOKEN_KEYWORD_OR);
10489 }
10490 }
10491 }
10492
10493 // At this point we know this is a regular newline, and we can set the
10494 // necessary state and return the token.
10495 lex_state_set(parser, PM_LEX_STATE_BEG);
10496 parser->command_start = true;
10497 parser->current.type = PM_TOKEN_NEWLINE;
10498 if (!lexed_comment) parser_lex_callback(parser);
10499 return;
10500 }
10501
10502 // ,
10503 case ',':
10504 if ((parser->previous.type == PM_TOKEN_COMMA) && (parser->enclosure_nesting > 0)) {
10505 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
10506 }
10507
10508 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10509 LEX(PM_TOKEN_COMMA);
10510
10511 // (
10512 case '(': {
10513 /* A parenthesis scanned at the beginning of an expression
10514 * groups the expression it wraps, while one scanned in
10515 * argument position with a preceding space wraps a command
10516 * argument. Everything else opens an argument list. */
10517 pm_token_type_t type = PM_TOKEN_PARENTHESIS_LEFT;
10518
10519 if (lex_state_beg_p(parser)) {
10520 type = PM_TOKEN_PARENTHESIS_LEFT_GROUPING;
10521 } else if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (PM_LEX_STATE_END | PM_LEX_STATE_LABEL))) {
10522 type = PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES;
10523 }
10524
10525 parser->enclosure_nesting++;
10526 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10527 pm_enclosure_frame_push(parser);
10528 LEX(type);
10529 }
10530
10531 // )
10532 case ')':
10533 parser->enclosure_nesting--;
10534 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10535 pm_enclosure_frame_pop(parser);
10536 LEX(PM_TOKEN_PARENTHESIS_RIGHT);
10537
10538 // ;
10539 case ';':
10540 lex_state_set(parser, PM_LEX_STATE_BEG);
10541 parser->command_start = true;
10542 LEX(PM_TOKEN_SEMICOLON);
10543
10544 // [ [] []=
10545 case '[':
10546 parser->enclosure_nesting++;
10547 pm_token_type_t type = PM_TOKEN_BRACKET_LEFT;
10548
10549 if (lex_state_operator_p(parser)) {
10550 if (match(parser, ']')) {
10551 parser->enclosure_nesting--;
10552 lex_state_set(parser, PM_LEX_STATE_ARG);
10553 LEX(match(parser, '=') ? PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : PM_TOKEN_BRACKET_LEFT_RIGHT);
10554 }
10555
10556 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABEL);
10557 LEX(type);
10558 }
10559
10560 if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, PM_LEX_STATE_LABELED)))) {
10561 type = PM_TOKEN_BRACKET_LEFT_ARRAY;
10562 }
10563
10564 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10565 pm_enclosure_frame_push(parser);
10566 LEX(type);
10567
10568 // ]
10569 case ']':
10570 parser->enclosure_nesting--;
10571 lex_state_set(parser, PM_LEX_STATE_END);
10572 pm_enclosure_frame_pop(parser);
10573 LEX(PM_TOKEN_BRACKET_RIGHT);
10574
10575 // {
10576 case '{': {
10577 pm_token_type_t type = PM_TOKEN_BRACE_LEFT;
10578
10579 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
10580 /* This { begins a lambda */
10581 parser->command_start = true;
10582 lex_state_set(parser, PM_LEX_STATE_BEG);
10583 type = PM_TOKEN_LAMBDA_BEGIN;
10584 } else if (lex_state_p(parser, PM_LEX_STATE_LABELED)) {
10585 /* This { begins a hash literal */
10586 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10587 type = PM_TOKEN_BRACE_LEFT_HASH;
10588 } else if (lex_state_p(parser, PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_END | PM_LEX_STATE_ENDFN)) {
10589 /* This { begins a block */
10590 parser->command_start = true;
10591 lex_state_set(parser, PM_LEX_STATE_BEG);
10592 } else if (lex_state_p(parser, PM_LEX_STATE_ENDARG)) {
10593 /* This { begins a block following a parenthesized
10594 * command argument */
10595 parser->command_start = true;
10596 lex_state_set(parser, PM_LEX_STATE_BEG);
10597 type = PM_TOKEN_BRACE_LEFT_ARGUMENT;
10598 } else {
10599 /* This { begins a hash literal */
10600 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10601 type = PM_TOKEN_BRACE_LEFT_HASH;
10602 }
10603
10604 parser->enclosure_nesting++;
10605 parser->brace_nesting++;
10606 pm_enclosure_frame_push(parser);
10607
10608 LEX(type);
10609 }
10610
10611 // }
10612 case '}':
10613 parser->enclosure_nesting--;
10614 pm_enclosure_frame_pop(parser);
10615
10616 if ((parser->lex_modes.current->mode == PM_LEX_EMBEXPR) && (parser->brace_nesting == 0)) {
10617 lex_mode_pop(parser);
10618 LEX(PM_TOKEN_EMBEXPR_END);
10619 }
10620
10621 parser->brace_nesting--;
10622 lex_state_set(parser, PM_LEX_STATE_END);
10623 LEX(PM_TOKEN_BRACE_RIGHT);
10624
10625 // * ** **= *=
10626 case '*': {
10627 if (match(parser, '*')) {
10628 if (match(parser, '=')) {
10629 lex_state_set(parser, PM_LEX_STATE_BEG);
10630 LEX(PM_TOKEN_STAR_STAR_EQUAL);
10631 }
10632
10633 pm_token_type_t type = PM_TOKEN_STAR_STAR;
10634
10635 if (lex_state_spcarg_p(parser, space_seen)) {
10636 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR);
10637 type = PM_TOKEN_USTAR_STAR;
10638 } else if (lex_state_beg_p(parser)) {
10639 type = PM_TOKEN_USTAR_STAR;
10640 } else if (ambiguous_operator_p(parser, space_seen)) {
10641 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "**", "argument prefix");
10642 }
10643
10644 if (lex_state_operator_p(parser)) {
10645 lex_state_set(parser, PM_LEX_STATE_ARG);
10646 } else {
10647 lex_state_set(parser, PM_LEX_STATE_BEG);
10648 }
10649
10650 LEX(type);
10651 }
10652
10653 if (match(parser, '=')) {
10654 lex_state_set(parser, PM_LEX_STATE_BEG);
10655 LEX(PM_TOKEN_STAR_EQUAL);
10656 }
10657
10658 pm_token_type_t type = PM_TOKEN_STAR;
10659
10660 if (lex_state_spcarg_p(parser, space_seen)) {
10661 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR);
10662 type = PM_TOKEN_USTAR;
10663 } else if (lex_state_beg_p(parser)) {
10664 type = PM_TOKEN_USTAR;
10665 } else if (ambiguous_operator_p(parser, space_seen)) {
10666 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "*", "argument prefix");
10667 }
10668
10669 if (lex_state_operator_p(parser)) {
10670 lex_state_set(parser, PM_LEX_STATE_ARG);
10671 } else {
10672 lex_state_set(parser, PM_LEX_STATE_BEG);
10673 }
10674
10675 LEX(type);
10676 }
10677
10678 // ! != !~ !@
10679 case '!':
10680 if (lex_state_operator_p(parser)) {
10681 lex_state_set(parser, PM_LEX_STATE_ARG);
10682 if (match(parser, '@')) {
10683 LEX(PM_TOKEN_BANG);
10684 }
10685 } else {
10686 lex_state_set(parser, PM_LEX_STATE_BEG);
10687 }
10688
10689 if (match(parser, '=')) {
10690 LEX(PM_TOKEN_BANG_EQUAL);
10691 }
10692
10693 if (match(parser, '~')) {
10694 LEX(PM_TOKEN_BANG_TILDE);
10695 }
10696
10697 LEX(PM_TOKEN_BANG);
10698
10699 // = => =~ == === =begin
10700 case '=':
10701 if (
10702 current_token_starts_line(parser) &&
10703 (parser->current.end + 5 <= parser->end) &&
10704 memcmp(parser->current.end, "begin", 5) == 0 &&
10705 (pm_char_is_whitespace(peek_offset(parser, 5)) || (peek_offset(parser, 5) == '\0'))
10706 ) {
10707 pm_token_type_t type = lex_embdoc(parser);
10708 if (type == PM_TOKEN_EOF) {
10709 LEX(type);
10710 }
10711
10712 goto lex_next_token;
10713 }
10714
10715 if (lex_state_operator_p(parser)) {
10716 lex_state_set(parser, PM_LEX_STATE_ARG);
10717 } else {
10718 lex_state_set(parser, PM_LEX_STATE_BEG);
10719 }
10720
10721 if (match(parser, '>')) {
10722 LEX(PM_TOKEN_EQUAL_GREATER);
10723 }
10724
10725 if (match(parser, '~')) {
10726 LEX(PM_TOKEN_EQUAL_TILDE);
10727 }
10728
10729 if (match(parser, '=')) {
10730 LEX(match(parser, '=') ? PM_TOKEN_EQUAL_EQUAL_EQUAL : PM_TOKEN_EQUAL_EQUAL);
10731 }
10732
10733 LEX(PM_TOKEN_EQUAL);
10734
10735 // < << <<= <= <=>
10736 case '<':
10737 if (match(parser, '<')) {
10738 if (
10739 !lex_state_p(parser, PM_LEX_STATE_DOT | PM_LEX_STATE_CLASS) &&
10740 !lex_state_end_p(parser) &&
10741 (!lex_state_p(parser, PM_LEX_STATE_ARG_ANY) || lex_state_p(parser, PM_LEX_STATE_LABELED) || space_seen)
10742 ) {
10743 const uint8_t *end = parser->current.end;
10744
10745 pm_heredoc_quote_t quote = PM_HEREDOC_QUOTE_NONE;
10746 pm_heredoc_indent_t indent = PM_HEREDOC_INDENT_NONE;
10747
10748 if (match(parser, '-')) {
10749 indent = PM_HEREDOC_INDENT_DASH;
10750 }
10751 else if (match(parser, '~')) {
10752 indent = PM_HEREDOC_INDENT_TILDE;
10753 }
10754
10755 if (match(parser, '`')) {
10756 quote = PM_HEREDOC_QUOTE_BACKTICK;
10757 }
10758 else if (match(parser, '"')) {
10759 quote = PM_HEREDOC_QUOTE_DOUBLE;
10760 }
10761 else if (match(parser, '\'')) {
10762 quote = PM_HEREDOC_QUOTE_SINGLE;
10763 }
10764
10765 const uint8_t *ident_start = parser->current.end;
10766 size_t width = 0;
10767
10768 if (parser->current.end >= parser->end) {
10769 parser->current.end = end;
10770 } else if (quote == PM_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) == 0) {
10771 parser->current.end = end;
10772 } else {
10773 if (quote == PM_HEREDOC_QUOTE_NONE) {
10774 parser->current.end += width;
10775
10776 while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end))) {
10777 parser->current.end += width;
10778 }
10779 } else {
10780 // If we have quotes, then we're going to go until we find the
10781 // end quote.
10782 while ((parser->current.end < parser->end) && quote != (pm_heredoc_quote_t) (*parser->current.end)) {
10783 if (*parser->current.end == '\r' || *parser->current.end == '\n') break;
10784 parser->current.end++;
10785 }
10786 }
10787
10788 size_t ident_length = (size_t) (parser->current.end - ident_start);
10789 bool ident_error = false;
10790
10791 if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) {
10792 pm_parser_err(parser, U32(ident_start - parser->start), U32(ident_length), PM_ERR_HEREDOC_IDENTIFIER);
10793 ident_error = true;
10794 }
10795
10796 parser->explicit_encoding = NULL;
10797 lex_mode_push(parser, (pm_lex_mode_t) {
10798 .mode = PM_LEX_HEREDOC,
10799 .as.heredoc = {
10800 .base = {
10801 .ident_start = ident_start,
10802 .ident_length = ident_length,
10803 .quote = quote,
10804 .indent = indent
10805 },
10806 .next_start = parser->current.end,
10807 .common_whitespace = NULL,
10808 .line_continuation = false
10809 }
10810 });
10811
10812 if (parser->heredoc_end == NULL) {
10813 const uint8_t *body_start = next_newline(parser->current.end, parser->end - parser->current.end);
10814
10815 if (body_start == NULL) {
10816 // If there is no newline after the heredoc identifier, then
10817 // this is not a valid heredoc declaration. In this case we
10818 // will add an error, but we will still return a heredoc
10819 // start.
10820 if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length);
10821 body_start = parser->end;
10822 } else {
10823 // Otherwise, we want to indicate that the body of the
10824 // heredoc starts on the character after the next newline.
10825 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(body_start - parser->start + 1));
10826 body_start++;
10827 }
10828
10829 parser->next_start = body_start;
10830 } else {
10831 parser->next_start = parser->heredoc_end;
10832 }
10833
10834 LEX(PM_TOKEN_HEREDOC_START);
10835 }
10836 }
10837
10838 if (match(parser, '=')) {
10839 lex_state_set(parser, PM_LEX_STATE_BEG);
10840 LEX(PM_TOKEN_LESS_LESS_EQUAL);
10841 }
10842
10843 if (ambiguous_operator_p(parser, space_seen)) {
10844 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "<<", "here document");
10845 }
10846
10847 if (lex_state_operator_p(parser)) {
10848 lex_state_set(parser, PM_LEX_STATE_ARG);
10849 } else {
10850 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10851 lex_state_set(parser, PM_LEX_STATE_BEG);
10852 }
10853
10854 LEX(PM_TOKEN_LESS_LESS);
10855 }
10856
10857 if (lex_state_operator_p(parser)) {
10858 lex_state_set(parser, PM_LEX_STATE_ARG);
10859 } else {
10860 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10861 lex_state_set(parser, PM_LEX_STATE_BEG);
10862 }
10863
10864 if (match(parser, '=')) {
10865 if (match(parser, '>')) {
10866 LEX(PM_TOKEN_LESS_EQUAL_GREATER);
10867 }
10868
10869 LEX(PM_TOKEN_LESS_EQUAL);
10870 }
10871
10872 LEX(PM_TOKEN_LESS);
10873
10874 // > >> >>= >=
10875 case '>':
10876 if (match(parser, '>')) {
10877 if (lex_state_operator_p(parser)) {
10878 lex_state_set(parser, PM_LEX_STATE_ARG);
10879 } else {
10880 lex_state_set(parser, PM_LEX_STATE_BEG);
10881 }
10882 LEX(match(parser, '=') ? PM_TOKEN_GREATER_GREATER_EQUAL : PM_TOKEN_GREATER_GREATER);
10883 }
10884
10885 if (lex_state_operator_p(parser)) {
10886 lex_state_set(parser, PM_LEX_STATE_ARG);
10887 } else {
10888 lex_state_set(parser, PM_LEX_STATE_BEG);
10889 }
10890
10891 LEX(match(parser, '=') ? PM_TOKEN_GREATER_EQUAL : PM_TOKEN_GREATER);
10892
10893 // double-quoted string literal
10894 case '"': {
10895 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10896 lex_mode_push_string(parser, true, label_allowed, '\0', '"');
10897 LEX(PM_TOKEN_STRING_BEGIN);
10898 }
10899
10900 // xstring literal
10901 case '`': {
10902 if (lex_state_p(parser, PM_LEX_STATE_FNAME)) {
10903 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10904 LEX(PM_TOKEN_BACKTICK);
10905 }
10906
10907 if (lex_state_p(parser, PM_LEX_STATE_DOT)) {
10908 if (previous_command_start) {
10909 lex_state_set(parser, PM_LEX_STATE_CMDARG);
10910 } else {
10911 lex_state_set(parser, PM_LEX_STATE_ARG);
10912 }
10913
10914 LEX(PM_TOKEN_BACKTICK);
10915 }
10916
10917 lex_mode_push_string(parser, true, false, '\0', '`');
10918 LEX(PM_TOKEN_XSTRING_BEGIN);
10919 }
10920
10921 // single-quoted string literal
10922 case '\'': {
10923 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10924 lex_mode_push_string(parser, false, label_allowed, '\0', '\'');
10925 LEX(PM_TOKEN_STRING_BEGIN);
10926 }
10927
10928 // ? character literal
10929 case '?':
10930 LEX(lex_question_mark(parser));
10931
10932 // & && &&= &=
10933 case '&': {
10934 if (match(parser, '&')) {
10935 lex_state_set(parser, PM_LEX_STATE_BEG);
10936
10937 if (match(parser, '=')) {
10938 LEX(PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
10939 }
10940
10941 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10942 }
10943
10944 if (match(parser, '=')) {
10945 lex_state_set(parser, PM_LEX_STATE_BEG);
10946 LEX(PM_TOKEN_AMPERSAND_EQUAL);
10947 }
10948
10949 if (match(parser, '.')) {
10950 lex_state_set(parser, PM_LEX_STATE_DOT);
10951 LEX(PM_TOKEN_AMPERSAND_DOT);
10952 }
10953
10954 pm_token_type_t type = PM_TOKEN_AMPERSAND;
10955 if (lex_state_spcarg_p(parser, space_seen)) {
10956 if ((peek(parser) != ':') || (peek_offset(parser, 1) == '\0')) {
10957 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
10958 } else {
10959 const uint8_t delim = peek_offset(parser, 1);
10960
10961 if ((delim != '\'') && (delim != '"') && !char_is_identifier(parser, parser->current.end + 1, parser->end - (parser->current.end + 1))) {
10962 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
10963 }
10964 }
10965
10966 type = PM_TOKEN_UAMPERSAND;
10967 } else if (lex_state_beg_p(parser)) {
10968 type = PM_TOKEN_UAMPERSAND;
10969 } else if (ambiguous_operator_p(parser, space_seen)) {
10970 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "&", "argument prefix");
10971 }
10972
10973 if (lex_state_operator_p(parser)) {
10974 lex_state_set(parser, PM_LEX_STATE_ARG);
10975 } else {
10976 lex_state_set(parser, PM_LEX_STATE_BEG);
10977 }
10978
10979 LEX(type);
10980 }
10981
10982 // | || ||= |=
10983 case '|':
10984 if (match(parser, '|')) {
10985 if (match(parser, '=')) {
10986 lex_state_set(parser, PM_LEX_STATE_BEG);
10987 LEX(PM_TOKEN_PIPE_PIPE_EQUAL);
10988 }
10989
10990 if (lex_state_p(parser, PM_LEX_STATE_BEG)) {
10991 parser->current.end--;
10992 LEX(PM_TOKEN_PIPE);
10993 }
10994
10995 lex_state_set(parser, PM_LEX_STATE_BEG);
10996 LEX(PM_TOKEN_PIPE_PIPE);
10997 }
10998
10999 if (match(parser, '=')) {
11000 lex_state_set(parser, PM_LEX_STATE_BEG);
11001 LEX(PM_TOKEN_PIPE_EQUAL);
11002 }
11003
11004 if (lex_state_operator_p(parser)) {
11005 lex_state_set(parser, PM_LEX_STATE_ARG);
11006 } else {
11007 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
11008 }
11009
11010 LEX(PM_TOKEN_PIPE);
11011
11012 // + += +@
11013 case '+': {
11014 if (lex_state_operator_p(parser)) {
11015 lex_state_set(parser, PM_LEX_STATE_ARG);
11016
11017 if (match(parser, '@')) {
11018 LEX(PM_TOKEN_UPLUS);
11019 }
11020
11021 LEX(PM_TOKEN_PLUS);
11022 }
11023
11024 if (match(parser, '=')) {
11025 lex_state_set(parser, PM_LEX_STATE_BEG);
11026 LEX(PM_TOKEN_PLUS_EQUAL);
11027 }
11028
11029 if (
11030 lex_state_beg_p(parser) ||
11031 (lex_state_spcarg_p(parser, space_seen) ? (pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS), true) : false)
11032 ) {
11033 lex_state_set(parser, PM_LEX_STATE_BEG);
11034
11035 if (pm_char_is_decimal_digit(peek(parser))) {
11036 parser->current.end++;
11037 pm_token_type_t type = lex_numeric(parser);
11038 lex_state_set(parser, PM_LEX_STATE_END);
11039 LEX(type);
11040 }
11041
11042 LEX(PM_TOKEN_UPLUS);
11043 }
11044
11045 if (ambiguous_operator_p(parser, space_seen)) {
11046 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "+", "unary operator");
11047 }
11048
11049 lex_state_set(parser, PM_LEX_STATE_BEG);
11050 LEX(PM_TOKEN_PLUS);
11051 }
11052
11053 // - -= -@
11054 case '-': {
11055 if (lex_state_operator_p(parser)) {
11056 lex_state_set(parser, PM_LEX_STATE_ARG);
11057
11058 if (match(parser, '@')) {
11059 LEX(PM_TOKEN_UMINUS);
11060 }
11061
11062 LEX(PM_TOKEN_MINUS);
11063 }
11064
11065 if (match(parser, '=')) {
11066 lex_state_set(parser, PM_LEX_STATE_BEG);
11067 LEX(PM_TOKEN_MINUS_EQUAL);
11068 }
11069
11070 if (match(parser, '>')) {
11071 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11072 LEX(PM_TOKEN_MINUS_GREATER);
11073 }
11074
11075 bool spcarg = lex_state_spcarg_p(parser, space_seen);
11076 bool is_beg = lex_state_beg_p(parser);
11077 if (!is_beg && spcarg) {
11078 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS);
11079 }
11080
11081 if (is_beg || spcarg) {
11082 lex_state_set(parser, PM_LEX_STATE_BEG);
11083 LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS);
11084 }
11085
11086 if (ambiguous_operator_p(parser, space_seen)) {
11087 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "-", "unary operator");
11088 }
11089
11090 lex_state_set(parser, PM_LEX_STATE_BEG);
11091 LEX(PM_TOKEN_MINUS);
11092 }
11093
11094 // . .. ...
11095 case '.': {
11096 bool beg_p = lex_state_beg_p(parser);
11097
11098 if (match(parser, '.')) {
11099 if (match(parser, '.')) {
11100 // If we're _not_ inside a range within default parameters
11101 if (!context_p(parser, PM_CONTEXT_DEFAULT_PARAMS) && context_p(parser, PM_CONTEXT_DEF_PARAMS)) {
11102 if (lex_state_p(parser, PM_LEX_STATE_END)) {
11103 lex_state_set(parser, PM_LEX_STATE_BEG);
11104 } else {
11105 lex_state_set(parser, PM_LEX_STATE_ENDARG);
11106 }
11107 LEX(PM_TOKEN_UDOT_DOT_DOT);
11108 }
11109
11110 if (parser->enclosure_nesting == 0 && parser_end_of_line_p(parser)) {
11111 pm_parser_warn_token(parser, &parser->current, PM_WARN_DOT_DOT_DOT_EOL);
11112 }
11113
11114 lex_state_set(parser, PM_LEX_STATE_BEG);
11115 LEX(beg_p ? PM_TOKEN_UDOT_DOT_DOT : PM_TOKEN_DOT_DOT_DOT);
11116 }
11117
11118 lex_state_set(parser, PM_LEX_STATE_BEG);
11119 LEX(beg_p ? PM_TOKEN_UDOT_DOT : PM_TOKEN_DOT_DOT);
11120 }
11121
11122 lex_state_set(parser, PM_LEX_STATE_DOT);
11123 LEX(PM_TOKEN_DOT);
11124 }
11125
11126 // integer
11127 case '0':
11128 case '1':
11129 case '2':
11130 case '3':
11131 case '4':
11132 case '5':
11133 case '6':
11134 case '7':
11135 case '8':
11136 case '9': {
11137 pm_token_type_t type = lex_numeric(parser);
11138 lex_state_set(parser, PM_LEX_STATE_END);
11139 LEX(type);
11140 }
11141
11142 // :: symbol
11143 case ':':
11144 if (match(parser, ':')) {
11145 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)) {
11146 lex_state_set(parser, PM_LEX_STATE_BEG);
11147 LEX(PM_TOKEN_UCOLON_COLON);
11148 }
11149
11150 lex_state_set(parser, PM_LEX_STATE_DOT);
11151 LEX(PM_TOKEN_COLON_COLON);
11152 }
11153
11154 if (lex_state_end_p(parser) || pm_char_is_whitespace(peek(parser)) || peek(parser) == '#') {
11155 lex_state_set(parser, PM_LEX_STATE_BEG);
11156 LEX(PM_TOKEN_COLON);
11157 }
11158
11159 if (peek(parser) == '"' || peek(parser) == '\'') {
11160 lex_mode_push_string(parser, peek(parser) == '"', false, '\0', *parser->current.end);
11161 parser->current.end++;
11162 } else {
11163 /*
11164 * A quoted symbol clears its encoding by pushing a lex
11165 * mode above. A bare symbol is lexed inline, so it
11166 * clears the encoding here.
11167 */
11168 parser->explicit_encoding = NULL;
11169 }
11170
11171 lex_state_set(parser, PM_LEX_STATE_FNAME);
11172 LEX(PM_TOKEN_SYMBOL_BEGIN);
11173
11174 // / /=
11175 case '/':
11176 if (lex_state_beg_p(parser)) {
11177 lex_mode_push_regexp(parser, '\0', '/');
11178 LEX(PM_TOKEN_REGEXP_BEGIN);
11179 }
11180
11181 if (match(parser, '=')) {
11182 lex_state_set(parser, PM_LEX_STATE_BEG);
11183 LEX(PM_TOKEN_SLASH_EQUAL);
11184 }
11185
11186 if (lex_state_spcarg_p(parser, space_seen)) {
11187 // https://bugs.ruby-lang.org/issues/21994
11188 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_4_0) {
11189 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_SLASH);
11190 }
11191 lex_mode_push_regexp(parser, '\0', '/');
11192 LEX(PM_TOKEN_REGEXP_BEGIN);
11193 }
11194
11195 if (ambiguous_operator_p(parser, space_seen)) {
11196 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "/", "regexp literal");
11197 }
11198
11199 if (lex_state_operator_p(parser)) {
11200 lex_state_set(parser, PM_LEX_STATE_ARG);
11201 } else {
11202 lex_state_set(parser, PM_LEX_STATE_BEG);
11203 }
11204
11205 LEX(PM_TOKEN_SLASH);
11206
11207 // ^ ^=
11208 case '^':
11209 if (lex_state_operator_p(parser)) {
11210 lex_state_set(parser, PM_LEX_STATE_ARG);
11211 } else {
11212 lex_state_set(parser, PM_LEX_STATE_BEG);
11213 }
11214 LEX(match(parser, '=') ? PM_TOKEN_CARET_EQUAL : PM_TOKEN_CARET);
11215
11216 // ~ ~@
11217 case '~':
11218 if (lex_state_operator_p(parser)) {
11219 (void) match(parser, '@');
11220 lex_state_set(parser, PM_LEX_STATE_ARG);
11221 } else {
11222 lex_state_set(parser, PM_LEX_STATE_BEG);
11223 }
11224
11225 LEX(PM_TOKEN_TILDE);
11226
11227 // % %= %i %I %q %Q %w %W
11228 case '%': {
11229 // If there is no subsequent character then we have an
11230 // invalid token. We're going to say it's the percent
11231 // operator because we don't want to move into the string
11232 // lex mode unnecessarily.
11233 if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) {
11234 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT_EOF);
11235 LEX(PM_TOKEN_PERCENT);
11236 }
11237
11238 if (!lex_state_beg_p(parser) && match(parser, '=')) {
11239 lex_state_set(parser, PM_LEX_STATE_BEG);
11240 LEX(PM_TOKEN_PERCENT_EQUAL);
11241 } else if (
11242 lex_state_beg_p(parser) ||
11243 (lex_state_p(parser, PM_LEX_STATE_FITEM) && (peek(parser) == 's')) ||
11244 lex_state_spcarg_p(parser, space_seen)
11245 ) {
11246 if (!parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end)) {
11247 if (*parser->current.end >= 0x80) {
11248 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11249 goto lex_next_token;
11250 }
11251
11252 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11253 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11254 LEX(PM_TOKEN_STRING_BEGIN);
11255 }
11256
11257 // Delimiters for %-literals cannot be alphanumeric. We
11258 // validate that here.
11259 uint8_t delimiter = peek_offset(parser, 1);
11260 if (delimiter >= 0x80 || parser->encoding->alnum_char(&delimiter, 1)) {
11261 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11262 goto lex_next_token;
11263 }
11264
11265 switch (peek(parser)) {
11266 case 'i': {
11267 parser->current.end++;
11268
11269 if (parser->current.end < parser->end) {
11270 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11271 } else {
11272 lex_mode_push_list_eof(parser);
11273 }
11274
11275 LEX(PM_TOKEN_PERCENT_LOWER_I);
11276 }
11277 case 'I': {
11278 parser->current.end++;
11279
11280 if (parser->current.end < parser->end) {
11281 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11282 } else {
11283 lex_mode_push_list_eof(parser);
11284 }
11285
11286 LEX(PM_TOKEN_PERCENT_UPPER_I);
11287 }
11288 case 'r': {
11289 parser->current.end++;
11290
11291 if (parser->current.end < parser->end) {
11292 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11293 lex_mode_push_regexp(parser, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11294 } else {
11295 lex_mode_push_regexp(parser, '\0', '\0');
11296 }
11297
11298 LEX(PM_TOKEN_REGEXP_BEGIN);
11299 }
11300 case 'q': {
11301 parser->current.end++;
11302
11303 if (parser->current.end < parser->end) {
11304 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11305 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11306 } else {
11307 lex_mode_push_string_eof(parser);
11308 }
11309
11310 LEX(PM_TOKEN_STRING_BEGIN);
11311 }
11312 case 'Q': {
11313 parser->current.end++;
11314
11315 if (parser->current.end < parser->end) {
11316 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11317 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11318 } else {
11319 lex_mode_push_string_eof(parser);
11320 }
11321
11322 LEX(PM_TOKEN_STRING_BEGIN);
11323 }
11324 case 's': {
11325 parser->current.end++;
11326
11327 if (parser->current.end < parser->end) {
11328 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11329 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11330 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
11331 } else {
11332 lex_mode_push_string_eof(parser);
11333 }
11334
11335 LEX(PM_TOKEN_SYMBOL_BEGIN);
11336 }
11337 case 'w': {
11338 parser->current.end++;
11339
11340 if (parser->current.end < parser->end) {
11341 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11342 } else {
11343 lex_mode_push_list_eof(parser);
11344 }
11345
11346 LEX(PM_TOKEN_PERCENT_LOWER_W);
11347 }
11348 case 'W': {
11349 parser->current.end++;
11350
11351 if (parser->current.end < parser->end) {
11352 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11353 } else {
11354 lex_mode_push_list_eof(parser);
11355 }
11356
11357 LEX(PM_TOKEN_PERCENT_UPPER_W);
11358 }
11359 case 'x': {
11360 parser->current.end++;
11361
11362 if (parser->current.end < parser->end) {
11363 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11364 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11365 } else {
11366 lex_mode_push_string_eof(parser);
11367 }
11368
11369 LEX(PM_TOKEN_PERCENT_LOWER_X);
11370 }
11371 default:
11372 // If we get to this point, then we have a % that is completely
11373 // unparsable. In this case we'll just drop it from the parser
11374 // and skip past it and hope that the next token is something
11375 // that we can parse.
11376 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11377 goto lex_next_token;
11378 }
11379 }
11380
11381 if (ambiguous_operator_p(parser, space_seen)) {
11382 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "%", "string literal");
11383 }
11384
11385 lex_state_set(parser, lex_state_operator_p(parser) ? PM_LEX_STATE_ARG : PM_LEX_STATE_BEG);
11386 LEX(PM_TOKEN_PERCENT);
11387 }
11388
11389 // global variable
11390 case '$': {
11391 pm_token_type_t type = lex_global_variable(parser);
11392
11393 // If we're lexing an embedded variable, then we need to pop back into
11394 // the parent lex context.
11395 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
11396 lex_mode_pop(parser);
11397 }
11398
11399 lex_state_set(parser, PM_LEX_STATE_END);
11400 LEX(type);
11401 }
11402
11403 // instance variable, class variable
11404 case '@':
11405 lex_state_set(parser, parser->lex_state & PM_LEX_STATE_FNAME ? PM_LEX_STATE_ENDFN : PM_LEX_STATE_END);
11406 LEX(lex_at_variable(parser));
11407
11408 default: {
11409 if (*parser->current.start != '_') {
11410 size_t width = char_is_identifier_start(parser, parser->current.start, parser->end - parser->current.start);
11411
11412 // If this isn't the beginning of an identifier, then
11413 // it's an invalid token as we've exhausted all of the
11414 // other options. We'll skip past it and return the next
11415 // token after adding an appropriate error message.
11416 if (!width) {
11417 if (*parser->current.start >= 0x80) {
11418 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *parser->current.start);
11419 } else if (*parser->current.start == '\\') {
11420 switch (peek_at(parser, parser->current.start + 1)) {
11421 case ' ':
11422 parser->current.end++;
11423 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped space");
11424 break;
11425 case '\f':
11426 parser->current.end++;
11427 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped form feed");
11428 break;
11429 case '\t':
11430 parser->current.end++;
11431 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped horizontal tab");
11432 break;
11433 case '\v':
11434 parser->current.end++;
11435 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped vertical tab");
11436 break;
11437 case '\r':
11438 if (peek_at(parser, parser->current.start + 2) != '\n') {
11439 parser->current.end++;
11440 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped carriage return");
11441 break;
11442 }
11444 default:
11445 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "backslash");
11446 break;
11447 }
11448 } else if (char_is_ascii_printable(*parser->current.start)) {
11449 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_PRINTABLE_CHARACTER, *parser->current.start);
11450 } else {
11451 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_CHARACTER, *parser->current.start);
11452 }
11453
11454 goto lex_next_token;
11455 }
11456
11457 parser->current.end = parser->current.start + width;
11458 }
11459
11460 pm_token_type_t type = lex_identifier(parser, previous_command_start);
11461
11462 // If we've hit a __END__ and it was at the start of the
11463 // line or the start of the file and it is followed by
11464 // either a \n or a \r\n, then this is the last token of the
11465 // file.
11466 if (
11467 ((parser->current.end - parser->current.start) == 7) &&
11468 current_token_starts_line(parser) &&
11469 (memcmp(parser->current.start, "__END__", 7) == 0) &&
11470 (parser->current.end == parser->end || match_eol(parser))
11471 ) {
11472 // Since we know we're about to add an __END__ comment,
11473 // we know we need to add all of the newlines to get the
11474 // correct column information for it.
11475 const uint8_t *cursor = parser->current.end;
11476 while ((cursor = next_newline(cursor, parser->end - cursor)) != NULL) {
11477 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(++cursor - parser->start));
11478 }
11479
11480 parser->current.end = parser->end;
11481 parser->current.type = PM_TOKEN___END__;
11482 parser_lex_callback(parser);
11483
11484 parser->data_loc.start = PM_TOKEN_START(parser, &parser->current);
11485 parser->data_loc.length = PM_TOKEN_LENGTH(&parser->current);
11486
11487 LEX(PM_TOKEN_EOF);
11488 }
11489
11490 pm_lex_state_t last_state = parser->lex_state;
11491
11492 if (type == PM_TOKEN_IDENTIFIER || type == PM_TOKEN_CONSTANT || type == PM_TOKEN_METHOD_NAME) {
11493 if (lex_state_p(parser, PM_LEX_STATE_BEG_ANY | PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_DOT)) {
11494 if (previous_command_start) {
11495 lex_state_set(parser, PM_LEX_STATE_CMDARG);
11496 } else {
11497 lex_state_set(parser, PM_LEX_STATE_ARG);
11498 }
11499 } else if (parser->lex_state == PM_LEX_STATE_FNAME) {
11500 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11501 } else {
11502 lex_state_set(parser, PM_LEX_STATE_END);
11503 }
11504 }
11505
11506 if (
11507 !(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) &&
11508 (type == PM_TOKEN_IDENTIFIER) &&
11509 ((pm_parser_local_depth(parser, &parser->current) != -1) ||
11510 pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)))
11511 ) {
11512 lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL);
11513 }
11514
11515 LEX(type);
11516 }
11517 }
11518 }
11519 case PM_LEX_LIST: {
11520 if (parser->next_start != NULL) {
11521 parser->current.end = parser->next_start;
11522 parser->next_start = NULL;
11523 }
11524
11525 // First we'll set the beginning of the token.
11526 parser->current.start = parser->current.end;
11527
11528 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11529
11530 // If there's any whitespace at the start of the list, then we're
11531 // going to trim it off the beginning and create a new token.
11532 size_t whitespace;
11533
11534 if (parser->heredoc_end) {
11535 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11536 if (peek_offset(parser, (ptrdiff_t)whitespace) == '\n') {
11537 whitespace += 1;
11538 }
11539 } else if (lex_mode->as.list.terminator == '\n') {
11540 // When the list delimiter is a newline (e.g. `%w` followed by a
11541 // newline), the newline is the terminator rather than a word
11542 // separator. We only trim inline whitespace here so that the
11543 // terminating newline is left for the terminator handling below.
11544 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11545 } else {
11546 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));
11547 }
11548
11549 if (whitespace > 0) {
11550 parser->current.end += whitespace;
11551 if (peek_offset(parser, -1) == '\n') {
11552 // mutates next_start
11553 parser_flush_heredoc_end(parser);
11554 }
11555 LEX(PM_TOKEN_WORDS_SEP);
11556 }
11557
11558 // We'll check if we're at the end of the file. If we are, then we
11559 // need to return the EOF token.
11560 if (parser->current.end >= parser->end) {
11561 LEX(PM_TOKEN_EOF);
11562 }
11563
11564 // Here we'll get a list of the places where strpbrk should break,
11565 // and then find the first one.
11566 const uint8_t *breakpoints = lex_mode->as.list.breakpoints;
11567 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11568
11569 // If we haven't found an escape yet, then this buffer will be
11570 // unallocated since we can refer directly to the source string.
11571 pm_token_buffer_t token_buffer = { 0 };
11572
11573 while (breakpoint != NULL) {
11574 // If we hit whitespace, then we must have received content by
11575 // now, so we can return an element of the list. A whitespace
11576 // character that is also the terminator (e.g. a newline
11577 // delimiter) is handled by the terminator check below, not here.
11578 if (pm_char_is_whitespace(*breakpoint) && *breakpoint != lex_mode->as.list.terminator) {
11579 parser->current.end = breakpoint;
11580 pm_token_buffer_flush(parser, &token_buffer);
11581 LEX(PM_TOKEN_STRING_CONTENT);
11582 }
11583
11584 // If we hit the terminator, we need to check which token to
11585 // return.
11586 if (*breakpoint == lex_mode->as.list.terminator) {
11587 // If this terminator doesn't actually close the list, then
11588 // we need to continue on past it.
11589 if (lex_mode->as.list.nesting > 0) {
11590 parser->current.end = breakpoint + 1;
11591 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11592 lex_mode->as.list.nesting--;
11593 continue;
11594 }
11595
11596 // If we've hit the terminator and we've already skipped
11597 // past content, then we can return a list node.
11598 if (breakpoint > parser->current.start) {
11599 parser->current.end = breakpoint;
11600 pm_token_buffer_flush(parser, &token_buffer);
11601 LEX(PM_TOKEN_STRING_CONTENT);
11602 }
11603
11604 // Otherwise, switch back to the default state and return
11605 // the end of the list.
11606 parser->current.end = breakpoint + 1;
11607
11608 // If the terminator is a newline (i.e. the list delimiter
11609 // was a newline), then we need to record it so that line
11610 // numbers after the list remain accurate.
11611 if (*breakpoint == '\n') {
11612 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11613 }
11614
11615 lex_mode_pop(parser);
11616 lex_state_set(parser, PM_LEX_STATE_END);
11617 LEX(PM_TOKEN_STRING_END);
11618 }
11619
11620 // If we hit a null byte, skip directly past it.
11621 if (*breakpoint == '\0') {
11622 breakpoint = pm_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1), true);
11623 continue;
11624 }
11625
11626 // If we hit escapes, then we need to treat the next token
11627 // literally. In this case we'll skip past the next character
11628 // and find the next breakpoint.
11629 if (*breakpoint == '\\') {
11630 parser->current.end = breakpoint + 1;
11631
11632 // If we've hit the end of the file, then break out of the
11633 // loop by setting the breakpoint to NULL.
11634 if (parser->current.end == parser->end) {
11635 breakpoint = NULL;
11636 continue;
11637 }
11638
11639 pm_token_buffer_escape(parser, &token_buffer);
11640 uint8_t peeked = peek(parser);
11641
11642 switch (peeked) {
11643 case ' ':
11644 case '\f':
11645 case '\t':
11646 case '\v':
11647 case '\\':
11648 pm_token_buffer_push_byte(&token_buffer, peeked);
11649 parser->current.end++;
11650 break;
11651 case '\r':
11652 parser->current.end++;
11653 if (peek(parser) != '\n') {
11654 pm_token_buffer_push_byte(&token_buffer, '\r');
11655 break;
11656 }
11658 case '\n':
11659 pm_token_buffer_push_byte(&token_buffer, '\n');
11660
11661 if (parser->heredoc_end) {
11662 // ... if we are on the same line as a heredoc,
11663 // flush the heredoc and continue parsing after
11664 // heredoc_end.
11665 parser_flush_heredoc_end(parser);
11666 pm_token_buffer_copy(parser, &token_buffer);
11667 LEX(PM_TOKEN_STRING_CONTENT);
11668 } else {
11669 // ... else track the newline.
11670 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11671 }
11672
11673 parser->current.end++;
11674 break;
11675 default:
11676 if (peeked == lex_mode->as.list.incrementor || peeked == lex_mode->as.list.terminator) {
11677 pm_token_buffer_push_byte(&token_buffer, peeked);
11678 parser->current.end++;
11679 } else if (lex_mode->as.list.interpolation) {
11680 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
11681 } else {
11682 pm_token_buffer_push_byte(&token_buffer, '\\');
11683 pm_token_buffer_push_escaped(&token_buffer, parser);
11684 }
11685
11686 break;
11687 }
11688
11689 token_buffer.cursor = parser->current.end;
11690 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11691 continue;
11692 }
11693
11694 // If we hit a #, then we will attempt to lex interpolation.
11695 if (*breakpoint == '#') {
11696 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11697
11698 if (!type) {
11699 // If we haven't returned at this point then we had something
11700 // that looked like an interpolated class or instance variable
11701 // like "#@" but wasn't actually. In this case we'll just skip
11702 // to the next breakpoint.
11703 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11704 continue;
11705 }
11706
11707 if (type == PM_TOKEN_STRING_CONTENT) {
11708 pm_token_buffer_flush(parser, &token_buffer);
11709 }
11710
11711 LEX(type);
11712 }
11713
11714 // If we've hit the incrementor, then we need to skip past it
11715 // and find the next breakpoint.
11716 assert(*breakpoint == lex_mode->as.list.incrementor);
11717 parser->current.end = breakpoint + 1;
11718 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11719 lex_mode->as.list.nesting++;
11720 continue;
11721 }
11722
11723 if (parser->current.end > parser->current.start) {
11724 pm_token_buffer_flush(parser, &token_buffer);
11725 LEX(PM_TOKEN_STRING_CONTENT);
11726 }
11727
11728 // If we were unable to find a breakpoint, then this token hits the
11729 // end of the file.
11730 parser->current.end = parser->end;
11731 pm_token_buffer_flush(parser, &token_buffer);
11732 LEX(PM_TOKEN_STRING_CONTENT);
11733 }
11734 case PM_LEX_REGEXP: {
11735 // First, we'll set to start of this token to be the current end.
11736 if (parser->next_start == NULL) {
11737 parser->current.start = parser->current.end;
11738 } else {
11739 parser->current.start = parser->next_start;
11740 parser->current.end = parser->next_start;
11741 parser->next_start = NULL;
11742 }
11743
11744 // We'll check if we're at the end of the file. If we are, then we
11745 // need to return the EOF token.
11746 if (parser->current.end >= parser->end) {
11747 LEX(PM_TOKEN_EOF);
11748 }
11749
11750 // Get a reference to the current mode.
11751 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11752
11753 // These are the places where we need to split up the content of the
11754 // regular expression. We'll use strpbrk to find the first of these
11755 // characters.
11756 const uint8_t *breakpoints = lex_mode->as.regexp.breakpoints;
11757 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11758 pm_regexp_token_buffer_t token_buffer = { 0 };
11759
11760 while (breakpoint != NULL) {
11761 uint8_t term = lex_mode->as.regexp.terminator;
11762 bool is_terminator = (*breakpoint == term);
11763
11764 // If the terminator is newline, we need to consider \r\n _also_ a newline
11765 // For example: `%\nfoo\r\n`
11766 // The string should be "foo", not "foo\r"
11767 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
11768 if (term == '\n') {
11769 is_terminator = true;
11770 }
11771
11772 // If the terminator is a CR, but we see a CRLF, we need to
11773 // treat the CRLF as a newline, meaning this is _not_ the
11774 // terminator
11775 if (term == '\r') {
11776 is_terminator = false;
11777 }
11778 }
11779
11780 // If we hit the terminator, we need to determine what kind of
11781 // token to return.
11782 if (is_terminator) {
11783 if (lex_mode->as.regexp.nesting > 0) {
11784 parser->current.end = breakpoint + 1;
11785 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11786 lex_mode->as.regexp.nesting--;
11787 continue;
11788 }
11789
11790 // Here we've hit the terminator. If we have already consumed
11791 // content then we need to return that content as string content
11792 // first.
11793 if (breakpoint > parser->current.start) {
11794 parser->current.end = breakpoint;
11795 pm_regexp_token_buffer_flush(parser, &token_buffer);
11796 LEX(PM_TOKEN_STRING_CONTENT);
11797 }
11798
11799 // Check here if we need to track the newline.
11800 size_t eol_length = match_eol_at(parser, breakpoint);
11801 if (eol_length) {
11802 parser->current.end = breakpoint + eol_length;
11803
11804 // Track the newline if we're not in a heredoc that
11805 // would have already have added the newline to the
11806 // list.
11807 if (parser->heredoc_end == NULL) {
11808 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11809 }
11810 } else {
11811 parser->current.end = breakpoint + 1;
11812 }
11813
11814 // Since we've hit the terminator of the regular expression,
11815 // we now need to parse the options.
11816 parser->current.end += pm_strspn_regexp_option(parser->current.end, parser->end - parser->current.end);
11817
11818 lex_mode_pop(parser);
11819 lex_state_set(parser, PM_LEX_STATE_END);
11820 LEX(PM_TOKEN_REGEXP_END);
11821 }
11822
11823 // If we've hit the incrementor, then we need to skip past it
11824 // and find the next breakpoint.
11825 if (*breakpoint && *breakpoint == lex_mode->as.regexp.incrementor) {
11826 parser->current.end = breakpoint + 1;
11827 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11828 lex_mode->as.regexp.nesting++;
11829 continue;
11830 }
11831
11832 switch (*breakpoint) {
11833 case '\0':
11834 // If we hit a null byte, skip directly past it.
11835 parser->current.end = breakpoint + 1;
11836 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11837 break;
11838 case '\r':
11839 if (peek_at(parser, breakpoint + 1) != '\n') {
11840 parser->current.end = breakpoint + 1;
11841 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11842 break;
11843 }
11844
11845 breakpoint++;
11846 parser->current.end = breakpoint;
11847 pm_regexp_token_buffer_escape(parser, &token_buffer);
11848 token_buffer.base.cursor = breakpoint;
11849
11851 case '\n':
11852 // If we've hit a newline, then we need to track that in
11853 // the list of newlines.
11854 if (parser->heredoc_end == NULL) {
11855 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
11856 parser->current.end = breakpoint + 1;
11857 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11858 break;
11859 }
11860
11861 parser->current.end = breakpoint + 1;
11862 parser_flush_heredoc_end(parser);
11863 pm_regexp_token_buffer_flush(parser, &token_buffer);
11864 LEX(PM_TOKEN_STRING_CONTENT);
11865 case '\\': {
11866 // If we hit escapes, then we need to treat the next
11867 // token literally. In this case we'll skip past the
11868 // next character and find the next breakpoint.
11869 parser->current.end = breakpoint + 1;
11870
11871 // If we've hit the end of the file, then break out of
11872 // the loop by setting the breakpoint to NULL.
11873 if (parser->current.end == parser->end) {
11874 breakpoint = NULL;
11875 break;
11876 }
11877
11878 pm_regexp_token_buffer_escape(parser, &token_buffer);
11879 uint8_t peeked = peek(parser);
11880
11881 switch (peeked) {
11882 case '\r':
11883 parser->current.end++;
11884 if (peek(parser) != '\n') {
11885 if (lex_mode->as.regexp.terminator != '\r') {
11886 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11887 }
11888 pm_regexp_token_buffer_push_byte(&token_buffer, '\r');
11889 pm_token_buffer_push_byte(&token_buffer.base, '\r');
11890 break;
11891 }
11893 case '\n':
11894 if (parser->heredoc_end) {
11895 // ... if we are on the same line as a heredoc,
11896 // flush the heredoc and continue parsing after
11897 // heredoc_end.
11898 parser_flush_heredoc_end(parser);
11899 pm_regexp_token_buffer_copy(parser, &token_buffer);
11900 LEX(PM_TOKEN_STRING_CONTENT);
11901 } else {
11902 // ... else track the newline.
11903 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11904 }
11905
11906 parser->current.end++;
11907 break;
11908 case 'c':
11909 case 'C':
11910 case 'M':
11911 case 'u':
11912 case 'x':
11913 escape_read(parser, &token_buffer.regexp_buffer, &token_buffer.base.buffer, PM_ESCAPE_FLAG_REGEXP);
11914 break;
11915 default:
11916 if (lex_mode->as.regexp.terminator == peeked) {
11917 // Some characters when they are used as the
11918 // terminator also receive an escape. They are
11919 // enumerated here.
11920 switch (peeked) {
11921 case '$': case ')': case '*': case '+':
11922 case '.': case '>': case '?': case ']':
11923 case '^': case '|': case '}':
11924 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11925 break;
11926 default:
11927 break;
11928 }
11929
11930 pm_regexp_token_buffer_push_byte(&token_buffer, peeked);
11931 pm_token_buffer_push_byte(&token_buffer.base, peeked);
11932 parser->current.end++;
11933 break;
11934 }
11935
11936 if (peeked < 0x80) pm_token_buffer_push_byte(&token_buffer.base, '\\');
11937 pm_regexp_token_buffer_push_escaped(&token_buffer, parser);
11938 break;
11939 }
11940
11941 token_buffer.base.cursor = parser->current.end;
11942 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11943 break;
11944 }
11945 case '#': {
11946 // If we hit a #, then we will attempt to lex
11947 // interpolation.
11948 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11949
11950 if (!type) {
11951 // If we haven't returned at this point then we had
11952 // something that looked like an interpolated class or
11953 // instance variable like "#@" but wasn't actually. In
11954 // this case we'll just skip to the next breakpoint.
11955 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11956 break;
11957 }
11958
11959 if (type == PM_TOKEN_STRING_CONTENT) {
11960 pm_regexp_token_buffer_flush(parser, &token_buffer);
11961 }
11962
11963 LEX(type);
11964 }
11965 default:
11966 assert(false && "unreachable");
11967 break;
11968 }
11969 }
11970
11971 if (parser->current.end > parser->current.start) {
11972 pm_regexp_token_buffer_flush(parser, &token_buffer);
11973 LEX(PM_TOKEN_STRING_CONTENT);
11974 }
11975
11976 // If we were unable to find a breakpoint, then this token hits the
11977 // end of the file.
11978 parser->current.end = parser->end;
11979 pm_regexp_token_buffer_flush(parser, &token_buffer);
11980 LEX(PM_TOKEN_STRING_CONTENT);
11981 }
11982 case PM_LEX_STRING: {
11983 // First, we'll set to start of this token to be the current end.
11984 if (parser->next_start == NULL) {
11985 parser->current.start = parser->current.end;
11986 } else {
11987 parser->current.start = parser->next_start;
11988 parser->current.end = parser->next_start;
11989 parser->next_start = NULL;
11990 }
11991
11992 // We'll check if we're at the end of the file. If we are, then we need to
11993 // return the EOF token.
11994 if (parser->current.end >= parser->end) {
11995 LEX(PM_TOKEN_EOF);
11996 }
11997
11998 // These are the places where we need to split up the content of the
11999 // string. We'll use strpbrk to find the first of these characters.
12000 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12001 const uint8_t *breakpoints = lex_mode->as.string.breakpoints;
12002 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12003
12004 // If we haven't found an escape yet, then this buffer will be
12005 // unallocated since we can refer directly to the source string.
12006 pm_token_buffer_t token_buffer = { 0 };
12007
12008 while (breakpoint != NULL) {
12009 // If we hit the incrementor, then we'll increment then nesting and
12010 // continue lexing.
12011 if (lex_mode->as.string.incrementor != '\0' && *breakpoint == lex_mode->as.string.incrementor) {
12012 lex_mode->as.string.nesting++;
12013 parser->current.end = breakpoint + 1;
12014 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12015 continue;
12016 }
12017
12018 uint8_t term = lex_mode->as.string.terminator;
12019 bool is_terminator = (*breakpoint == term);
12020
12021 // If the terminator is newline, we need to consider \r\n _also_ a newline
12022 // For example: `%r\nfoo\r\n`
12023 // The string should be /foo/, not /foo\r/
12024 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
12025 if (term == '\n') {
12026 is_terminator = true;
12027 }
12028
12029 // If the terminator is a CR, but we see a CRLF, we need to
12030 // treat the CRLF as a newline, meaning this is _not_ the
12031 // terminator
12032 if (term == '\r') {
12033 is_terminator = false;
12034 }
12035 }
12036
12037 // Note that we have to check the terminator here first because we could
12038 // potentially be parsing a % string that has a # character as the
12039 // terminator.
12040 if (is_terminator) {
12041 // If this terminator doesn't actually close the string, then we need
12042 // to continue on past it.
12043 if (lex_mode->as.string.nesting > 0) {
12044 parser->current.end = breakpoint + 1;
12045 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12046 lex_mode->as.string.nesting--;
12047 continue;
12048 }
12049
12050 // Here we've hit the terminator. If we have already consumed content
12051 // then we need to return that content as string content first.
12052 if (breakpoint > parser->current.start) {
12053 parser->current.end = breakpoint;
12054 pm_token_buffer_flush(parser, &token_buffer);
12055 LEX(PM_TOKEN_STRING_CONTENT);
12056 }
12057
12058 // Otherwise we need to switch back to the parent lex mode and
12059 // return the end of the string.
12060 size_t eol_length = match_eol_at(parser, breakpoint);
12061 if (eol_length) {
12062 parser->current.end = breakpoint + eol_length;
12063
12064 // Track the newline if we're not in a heredoc that
12065 // would have already have added the newline to the
12066 // list.
12067 if (parser->heredoc_end == NULL) {
12068 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
12069 }
12070 } else {
12071 parser->current.end = breakpoint + 1;
12072 }
12073
12074 if (lex_mode->as.string.label_allowed && (peek(parser) == ':') && (peek_offset(parser, 1) != ':')) {
12075 parser->current.end++;
12076 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
12077 lex_mode_pop(parser);
12078 LEX(PM_TOKEN_LABEL_END);
12079 }
12080
12081 // When the delimiter itself is a newline, we won't
12082 // get a chance to flush heredocs in the usual places since
12083 // the newline is already consumed.
12084 if (term == '\n' && parser->heredoc_end) {
12085 parser_flush_heredoc_end(parser);
12086 }
12087
12088 lex_state_set(parser, PM_LEX_STATE_END);
12089 lex_mode_pop(parser);
12090 LEX(PM_TOKEN_STRING_END);
12091 }
12092
12093 switch (*breakpoint) {
12094 case '\0':
12095 // Skip directly past the null character.
12096 parser->current.end = breakpoint + 1;
12097 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12098 break;
12099 case '\r':
12100 if (peek_at(parser, breakpoint + 1) != '\n') {
12101 parser->current.end = breakpoint + 1;
12102 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12103 break;
12104 }
12105
12106 // If we hit a \r\n sequence, then we need to treat it
12107 // as a newline.
12108 breakpoint++;
12109 parser->current.end = breakpoint;
12110 pm_token_buffer_escape(parser, &token_buffer);
12111 token_buffer.cursor = breakpoint;
12112
12114 case '\n':
12115 // When we hit a newline, we need to flush any potential
12116 // heredocs. Note that this has to happen after we check
12117 // for the terminator in case the terminator is a
12118 // newline character.
12119 if (parser->heredoc_end == NULL) {
12120 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12121 parser->current.end = breakpoint + 1;
12122 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12123 break;
12124 }
12125
12126 parser->current.end = breakpoint + 1;
12127 parser_flush_heredoc_end(parser);
12128 pm_token_buffer_flush(parser, &token_buffer);
12129 LEX(PM_TOKEN_STRING_CONTENT);
12130 case '\\': {
12131 // Here we hit escapes.
12132 parser->current.end = breakpoint + 1;
12133
12134 // If we've hit the end of the file, then break out of
12135 // the loop by setting the breakpoint to NULL.
12136 if (parser->current.end == parser->end) {
12137 breakpoint = NULL;
12138 continue;
12139 }
12140
12141 pm_token_buffer_escape(parser, &token_buffer);
12142 uint8_t peeked = peek(parser);
12143
12144 switch (peeked) {
12145 case '\\':
12146 pm_token_buffer_push_byte(&token_buffer, '\\');
12147 parser->current.end++;
12148 break;
12149 case '\r':
12150 parser->current.end++;
12151 if (peek(parser) != '\n') {
12152 if (!lex_mode->as.string.interpolation) {
12153 pm_token_buffer_push_byte(&token_buffer, '\\');
12154 }
12155 pm_token_buffer_push_byte(&token_buffer, '\r');
12156 break;
12157 }
12159 case '\n':
12160 if (!lex_mode->as.string.interpolation) {
12161 pm_token_buffer_push_byte(&token_buffer, '\\');
12162 pm_token_buffer_push_byte(&token_buffer, '\n');
12163 }
12164
12165 if (parser->heredoc_end) {
12166 // ... if we are on the same line as a heredoc,
12167 // flush the heredoc and continue parsing after
12168 // heredoc_end.
12169 parser_flush_heredoc_end(parser);
12170 pm_token_buffer_copy(parser, &token_buffer);
12171 LEX(PM_TOKEN_STRING_CONTENT);
12172 } else {
12173 // ... else track the newline.
12174 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
12175 }
12176
12177 parser->current.end++;
12178 break;
12179 default:
12180 if (lex_mode->as.string.incrementor != '\0' && peeked == lex_mode->as.string.incrementor) {
12181 pm_token_buffer_push_byte(&token_buffer, peeked);
12182 parser->current.end++;
12183 } else if (lex_mode->as.string.terminator != '\0' && peeked == lex_mode->as.string.terminator) {
12184 pm_token_buffer_push_byte(&token_buffer, peeked);
12185 parser->current.end++;
12186 } else if (lex_mode->as.string.interpolation) {
12187 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12188 } else {
12189 pm_token_buffer_push_byte(&token_buffer, '\\');
12190 pm_token_buffer_push_escaped(&token_buffer, parser);
12191 }
12192
12193 break;
12194 }
12195
12196 token_buffer.cursor = parser->current.end;
12197 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12198 break;
12199 }
12200 case '#': {
12201 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12202
12203 if (!type) {
12204 // If we haven't returned at this point then we had something that
12205 // looked like an interpolated class or instance variable like "#@"
12206 // but wasn't actually. In this case we'll just skip to the next
12207 // breakpoint.
12208 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12209 break;
12210 }
12211
12212 if (type == PM_TOKEN_STRING_CONTENT) {
12213 pm_token_buffer_flush(parser, &token_buffer);
12214 }
12215
12216 LEX(type);
12217 }
12218 default:
12219 assert(false && "unreachable");
12220 }
12221 }
12222
12223 if (parser->current.end > parser->current.start) {
12224 pm_token_buffer_flush(parser, &token_buffer);
12225 LEX(PM_TOKEN_STRING_CONTENT);
12226 }
12227
12228 // If we've hit the end of the string, then this is an unterminated
12229 // string. In that case we'll return a string content token.
12230 parser->current.end = parser->end;
12231 pm_token_buffer_flush(parser, &token_buffer);
12232 LEX(PM_TOKEN_STRING_CONTENT);
12233 }
12234 case PM_LEX_HEREDOC: {
12235 // First, we'll set to start of this token.
12236 if (parser->next_start == NULL) {
12237 parser->current.start = parser->current.end;
12238 } else {
12239 parser->current.start = parser->next_start;
12240 parser->current.end = parser->next_start;
12241 parser->heredoc_end = NULL;
12242 parser->next_start = NULL;
12243 }
12244
12245 // Now let's grab the information about the identifier off of the
12246 // current lex mode.
12247 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12248 pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base;
12249
12250 bool line_continuation = lex_mode->as.heredoc.line_continuation;
12251 lex_mode->as.heredoc.line_continuation = false;
12252
12253 // We'll check if we're at the end of the file. If we are, then we
12254 // will add an error (because we weren't able to find the
12255 // terminator) but still continue parsing so that content after the
12256 // declaration of the heredoc can be parsed.
12257 if (parser->current.end >= parser->end) {
12258 pm_parser_err_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length);
12259 parser->next_start = lex_mode->as.heredoc.next_start;
12260 parser->heredoc_end = parser->current.end;
12261 lex_state_set(parser, PM_LEX_STATE_END);
12262 lex_mode_pop(parser);
12263 LEX(PM_TOKEN_HEREDOC_END);
12264 }
12265
12266 const uint8_t *ident_start = heredoc_lex_mode->ident_start;
12267 size_t ident_length = heredoc_lex_mode->ident_length;
12268
12269 // If we are immediately following a newline and we have hit the
12270 // terminator, then we need to return the ending of the heredoc.
12271 if (current_token_starts_line(parser)) {
12272 const uint8_t *start = parser->current.start;
12273
12274 if (!line_continuation && (start + ident_length <= parser->end)) {
12275 const uint8_t *newline = next_newline(start, parser->end - start);
12276 const uint8_t *ident_end = newline;
12277 const uint8_t *terminator_end = newline;
12278
12279 if (newline == NULL) {
12280 terminator_end = parser->end;
12281 ident_end = parser->end;
12282 } else {
12283 terminator_end++;
12284 if (newline[-1] == '\r') {
12285 ident_end--; // Remove \r
12286 }
12287 }
12288
12289 const uint8_t *terminator_start = ident_end - ident_length;
12290 const uint8_t *cursor = start;
12291
12292 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12293 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12294 cursor++;
12295 }
12296 }
12297
12298 if (
12299 (cursor == terminator_start) &&
12300 (memcmp(terminator_start, ident_start, ident_length) == 0)
12301 ) {
12302 if (newline != NULL) {
12303 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
12304 }
12305
12306 parser->current.end = terminator_end;
12307 if (*lex_mode->as.heredoc.next_start == '\\') {
12308 parser->next_start = NULL;
12309 } else {
12310 parser->next_start = lex_mode->as.heredoc.next_start;
12311 parser->heredoc_end = parser->current.end;
12312 }
12313
12314 lex_state_set(parser, PM_LEX_STATE_END);
12315 lex_mode_pop(parser);
12316 LEX(PM_TOKEN_HEREDOC_END);
12317 }
12318 }
12319
12320 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent);
12321 if (
12322 heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE &&
12323 lex_mode->as.heredoc.common_whitespace != NULL &&
12324 (*lex_mode->as.heredoc.common_whitespace > whitespace) &&
12325 peek_at(parser, start) != '\n'
12326 ) {
12327 *lex_mode->as.heredoc.common_whitespace = whitespace;
12328 }
12329 }
12330
12331 // Otherwise we'll be parsing string content. These are the places
12332 // where we need to split up the content of the heredoc. We'll use
12333 // strpbrk to find the first of these characters.
12334 uint8_t breakpoints[PM_STRPBRK_CACHE_SIZE] = "\r\n\\#";
12335
12336 pm_heredoc_quote_t quote = heredoc_lex_mode->quote;
12337 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12338 breakpoints[3] = '\0';
12339 }
12340
12341 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12342 pm_token_buffer_t token_buffer = { 0 };
12343 bool was_line_continuation = false;
12344
12345 while (breakpoint != NULL) {
12346 switch (*breakpoint) {
12347 case '\0':
12348 // Skip directly past the null character.
12349 parser->current.end = breakpoint + 1;
12350 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12351 break;
12352 case '\r':
12353 parser->current.end = breakpoint + 1;
12354
12355 if (peek_at(parser, breakpoint + 1) != '\n') {
12356 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12357 break;
12358 }
12359
12360 // If we hit a \r\n sequence, then we want to replace it
12361 // with a single \n character in the final string.
12362 breakpoint++;
12363 pm_token_buffer_escape(parser, &token_buffer);
12364 token_buffer.cursor = breakpoint;
12365
12367 case '\n': {
12368 if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) {
12369 parser_flush_heredoc_end(parser);
12370 parser->current.end = breakpoint + 1;
12371 pm_token_buffer_flush(parser, &token_buffer);
12372 LEX(PM_TOKEN_STRING_CONTENT);
12373 }
12374
12375 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12376
12377 // If we have a - or ~ heredoc, then we can match after
12378 // some leading whitespace.
12379 const uint8_t *start = breakpoint + 1;
12380
12381 if (!was_line_continuation && (start + ident_length <= parser->end)) {
12382 // We want to match the terminator starting from the end of the line in case
12383 // there is whitespace in the ident such as <<-' DOC' or <<~' DOC'.
12384 const uint8_t *newline = next_newline(start, parser->end - start);
12385
12386 if (newline == NULL) {
12387 newline = parser->end;
12388 } else if (newline[-1] == '\r') {
12389 newline--; // Remove \r
12390 }
12391
12392 // Start of a possible terminator.
12393 const uint8_t *terminator_start = newline - ident_length;
12394
12395 // Cursor to check for the leading whitespace. We skip the
12396 // leading whitespace if we have a - or ~ heredoc.
12397 const uint8_t *cursor = start;
12398
12399 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12400 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12401 cursor++;
12402 }
12403 }
12404
12405 if (
12406 cursor == terminator_start &&
12407 (memcmp(terminator_start, ident_start, ident_length) == 0)
12408 ) {
12409 parser->current.end = breakpoint + 1;
12410 pm_token_buffer_flush(parser, &token_buffer);
12411 LEX(PM_TOKEN_STRING_CONTENT);
12412 }
12413 }
12414
12415 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.base.indent);
12416
12417 // If we have hit a newline that is followed by a valid
12418 // terminator, then we need to return the content of the
12419 // heredoc here as string content. Then, the next time a
12420 // token is lexed, it will match again and return the
12421 // end of the heredoc.
12422 if (lex_mode->as.heredoc.base.indent == PM_HEREDOC_INDENT_TILDE) {
12423 if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
12424 *lex_mode->as.heredoc.common_whitespace = whitespace;
12425 }
12426
12427 parser->current.end = breakpoint + 1;
12428 pm_token_buffer_flush(parser, &token_buffer);
12429 LEX(PM_TOKEN_STRING_CONTENT);
12430 }
12431
12432 // Otherwise we hit a newline and it wasn't followed by
12433 // a terminator, so we can continue parsing.
12434 parser->current.end = breakpoint + 1;
12435 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12436 break;
12437 }
12438 case '\\': {
12439 // If we hit an escape, then we need to skip past
12440 // however many characters the escape takes up. However
12441 // it's important that if \n or \r\n are escaped, we
12442 // stop looping before the newline and not after the
12443 // newline so that we can still potentially find the
12444 // terminator of the heredoc.
12445 parser->current.end = breakpoint + 1;
12446
12447 // If we've hit the end of the file, then break out of
12448 // the loop by setting the breakpoint to NULL.
12449 if (parser->current.end == parser->end) {
12450 breakpoint = NULL;
12451 continue;
12452 }
12453
12454 pm_token_buffer_escape(parser, &token_buffer);
12455 uint8_t peeked = peek(parser);
12456
12457 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12458 switch (peeked) {
12459 case '\r':
12460 parser->current.end++;
12461 if (peek(parser) != '\n') {
12462 pm_token_buffer_push_byte(&token_buffer, '\\');
12463 pm_token_buffer_push_byte(&token_buffer, '\r');
12464 break;
12465 }
12467 case '\n':
12468 pm_token_buffer_push_byte(&token_buffer, '\\');
12469 pm_token_buffer_push_byte(&token_buffer, '\n');
12470 token_buffer.cursor = parser->current.end + 1;
12471 breakpoint = parser->current.end;
12472 continue;
12473 default:
12474 pm_token_buffer_push_byte(&token_buffer, '\\');
12475 pm_token_buffer_push_escaped(&token_buffer, parser);
12476 break;
12477 }
12478 } else {
12479 switch (peeked) {
12480 case '\r':
12481 parser->current.end++;
12482 if (peek(parser) != '\n') {
12483 pm_token_buffer_push_byte(&token_buffer, '\r');
12484 break;
12485 }
12487 case '\n':
12488 // If we are in a tilde here, we should
12489 // break out of the loop and return the
12490 // string content.
12491 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12492 const uint8_t *end = parser->current.end;
12493
12494 if (parser->heredoc_end == NULL) {
12495 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(end - parser->start + 1));
12496 }
12497
12498 // Here we want the buffer to only
12499 // include up to the backslash.
12500 parser->current.end = breakpoint;
12501 pm_token_buffer_flush(parser, &token_buffer);
12502
12503 // Now we can advance the end of the
12504 // token past the newline.
12505 parser->current.end = end + 1;
12506 lex_mode->as.heredoc.line_continuation = true;
12507 LEX(PM_TOKEN_STRING_CONTENT);
12508 }
12509
12510 was_line_continuation = true;
12511 token_buffer.cursor = parser->current.end + 1;
12512 breakpoint = parser->current.end;
12513 continue;
12514 default:
12515 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12516 break;
12517 }
12518 }
12519
12520 token_buffer.cursor = parser->current.end;
12521 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12522 break;
12523 }
12524 case '#': {
12525 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12526
12527 if (!type) {
12528 // If we haven't returned at this point then we had
12529 // something that looked like an interpolated class
12530 // or instance variable like "#@" but wasn't
12531 // actually. In this case we'll just skip to the
12532 // next breakpoint.
12533 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12534 break;
12535 }
12536
12537 if (type == PM_TOKEN_STRING_CONTENT) {
12538 pm_token_buffer_flush(parser, &token_buffer);
12539 }
12540
12541 LEX(type);
12542 }
12543 default:
12544 assert(false && "unreachable");
12545 }
12546
12547 was_line_continuation = false;
12548 }
12549
12550 if (parser->current.end > parser->current.start) {
12551 parser->current.end = parser->end;
12552 pm_token_buffer_flush(parser, &token_buffer);
12553 LEX(PM_TOKEN_STRING_CONTENT);
12554 }
12555
12556 // If we've hit the end of the string, then this is an unterminated
12557 // heredoc. In that case we'll return a string content token.
12558 parser->current.end = parser->end;
12559 pm_token_buffer_flush(parser, &token_buffer);
12560 LEX(PM_TOKEN_STRING_CONTENT);
12561 }
12562 }
12563
12564 assert(false && "unreachable");
12565}
12566
12567#undef LEX
12568
12569/******************************************************************************/
12570/* Parse functions */
12571/******************************************************************************/
12572
12581typedef enum {
12582 PM_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator
12583 PM_BINDING_POWER_STATEMENT = 2,
12584 PM_BINDING_POWER_MODIFIER_RESCUE = 4, // rescue
12585 PM_BINDING_POWER_MODIFIER = 6, // if unless until while
12586 PM_BINDING_POWER_COMPOSITION = 8, // and or
12587 PM_BINDING_POWER_NOT = 10, // not
12588 PM_BINDING_POWER_MATCH = 12, // => in
12589 PM_BINDING_POWER_DEFINED = 14, // defined?
12590 PM_BINDING_POWER_MULTI_ASSIGNMENT = 16, // =
12591 PM_BINDING_POWER_ASSIGNMENT = 18, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **=
12592 PM_BINDING_POWER_TERNARY = 20, // ?:
12593 PM_BINDING_POWER_RANGE = 22, // .. ...
12594 PM_BINDING_POWER_LOGICAL_OR = 24, // ||
12595 PM_BINDING_POWER_LOGICAL_AND = 26, // &&
12596 PM_BINDING_POWER_EQUALITY = 28, // <=> == === != =~ !~
12597 PM_BINDING_POWER_COMPARISON = 30, // > >= < <=
12598 PM_BINDING_POWER_BITWISE_OR = 32, // | ^
12599 PM_BINDING_POWER_BITWISE_AND = 34, // &
12600 PM_BINDING_POWER_SHIFT = 36, // << >>
12601 PM_BINDING_POWER_TERM = 38, // + -
12602 PM_BINDING_POWER_FACTOR = 40, // * / %
12603 PM_BINDING_POWER_UMINUS = 42, // -@
12604 PM_BINDING_POWER_EXPONENT = 44, // **
12605 PM_BINDING_POWER_UNARY = 46, // ! ~ +@
12606 PM_BINDING_POWER_INDEX = 48, // [] []=
12607 PM_BINDING_POWER_CALL = 50, // :: .
12608 PM_BINDING_POWER_MAX = 52
12609} pm_binding_power_t;
12610
12615typedef struct {
12617 pm_binding_power_t left;
12618
12620 pm_binding_power_t right;
12621
12624
12631
12632#define BINDING_POWER_ASSIGNMENT { PM_BINDING_POWER_UNARY, PM_BINDING_POWER_ASSIGNMENT, true, false }
12633#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, false }
12634#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true, false }
12635#define NON_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, true }
12636#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false }
12637
12638pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
12639 // rescue
12640 [PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = { PM_BINDING_POWER_MODIFIER_RESCUE, PM_BINDING_POWER_COMPOSITION, true, false },
12641
12642 // if unless until while
12643 [PM_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12644 [PM_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12645 [PM_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12646 [PM_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12647
12648 // and or
12649 [PM_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12650 [PM_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12651
12652 // => in
12653 [PM_TOKEN_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12654 [PM_TOKEN_KEYWORD_IN] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12655
12656 // &&= &= ^= = >>= <<= -= %= |= ||= += /= *= **=
12657 [PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12658 [PM_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12659 [PM_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT,
12660 [PM_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT,
12661 [PM_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT,
12662 [PM_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12663 [PM_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12664 [PM_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT,
12665 [PM_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12666 [PM_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12667 [PM_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12668 [PM_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT,
12669 [PM_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12670 [PM_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12671
12672 // ?:
12673 [PM_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_TERNARY),
12674
12675 // .. ...
12676 [PM_TOKEN_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12677 [PM_TOKEN_DOT_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12678 [PM_TOKEN_UDOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12679 [PM_TOKEN_UDOT_DOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12680
12681 // ||
12682 [PM_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_OR),
12683
12684 // &&
12685 [PM_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_AND),
12686
12687 // != !~ == === =~ <=>
12688 [PM_TOKEN_BANG_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12689 [PM_TOKEN_BANG_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12690 [PM_TOKEN_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12691 [PM_TOKEN_EQUAL_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12692 [PM_TOKEN_EQUAL_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12693 [PM_TOKEN_LESS_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12694
12695 // > >= < <=
12696 [PM_TOKEN_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12697 [PM_TOKEN_GREATER_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12698 [PM_TOKEN_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12699 [PM_TOKEN_LESS_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12700
12701 // ^ |
12702 [PM_TOKEN_CARET] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12703 [PM_TOKEN_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12704
12705 // &
12706 [PM_TOKEN_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_AND),
12707
12708 // >> <<
12709 [PM_TOKEN_GREATER_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12710 [PM_TOKEN_LESS_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12711
12712 // - +
12713 [PM_TOKEN_MINUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12714 [PM_TOKEN_PLUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12715
12716 // % / *
12717 [PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12718 [PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12719 [PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12720 [PM_TOKEN_USTAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_FACTOR),
12721
12722 // -@
12723 [PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS),
12724 [PM_TOKEN_UMINUS_NUM] = { PM_BINDING_POWER_UMINUS, PM_BINDING_POWER_MAX, false, false },
12725
12726 // **
12727 [PM_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_EXPONENT),
12728 [PM_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12729
12730 // ! ~ +@
12731 [PM_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12732 [PM_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12733 [PM_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12734
12735 // [
12736 [PM_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_INDEX),
12737
12738 // :: . &.
12739 [PM_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12740 [PM_TOKEN_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12741 [PM_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL)
12742};
12743
12744#undef BINDING_POWER_ASSIGNMENT
12745#undef LEFT_ASSOCIATIVE
12746#undef RIGHT_ASSOCIATIVE
12747#undef RIGHT_ASSOCIATIVE_UNARY
12748
12752static PRISM_INLINE bool
12753match1(const pm_parser_t *parser, pm_token_type_t type) {
12754 return parser->current.type == type;
12755}
12756
12760static PRISM_INLINE bool
12761match2(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12762 return match1(parser, type1) || match1(parser, type2);
12763}
12764
12768static PRISM_INLINE bool
12769match3(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) {
12770 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3);
12771}
12772
12776static PRISM_INLINE bool
12777match4(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) {
12778 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4);
12779}
12780
12784static PRISM_INLINE bool
12785match5(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) {
12786 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5);
12787}
12788
12792static PRISM_INLINE bool
12793match6(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) {
12794 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6);
12795}
12796
12800static PRISM_INLINE bool
12801match8(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) {
12802 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);
12803}
12804
12811static bool
12812accept1(pm_parser_t *parser, pm_token_type_t type) {
12813 if (match1(parser, type)) {
12814 parser_lex(parser);
12815 return true;
12816 }
12817 return false;
12818}
12819
12824static PRISM_INLINE bool
12825accept2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12826 if (match2(parser, type1, type2)) {
12827 parser_lex(parser);
12828 return true;
12829 }
12830 return false;
12831}
12832
12844static void
12845expect1(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id) {
12846 if (accept1(parser, type)) return;
12847
12848 const uint8_t *location = parser->previous.end;
12849 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12850
12851 parser->previous.start = location;
12852 parser->previous.type = 0;
12853}
12854
12859static void
12860expect2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_diagnostic_id_t diag_id) {
12861 if (accept2(parser, type1, type2)) return;
12862
12863 const uint8_t *location = parser->previous.end;
12864 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12865
12866 parser->previous.start = location;
12867 parser->previous.type = 0;
12868}
12869
12874static void
12875expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
12876 if (match1(parser, PM_TOKEN_HEREDOC_END)) {
12877 parser_lex(parser);
12878 } else {
12879 pm_parser_err_heredoc_term(parser, ident_start, ident_length);
12880 parser->previous.start = parser->previous.end;
12881 parser->previous.type = 0;
12882 }
12883}
12884
12891static void
12892expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id, const pm_token_t *opening) {
12893 if (accept1(parser, type)) return;
12894
12895 const uint8_t *start = opening->start;
12896 pm_parser_err(parser, U32(start - parser->start), U32(opening->end - start), diag_id);
12897
12898 parser->previous.start = parser->previous.end;
12899 parser->previous.type = 0;
12900}
12901
12903#define PM_PARSE_ACCEPTS_COMMAND_CALL ((uint8_t) 0x1)
12904#define PM_PARSE_ACCEPTS_LABEL ((uint8_t) 0x2)
12905#define PM_PARSE_ACCEPTS_DO_BLOCK ((uint8_t) 0x4)
12906#define PM_PARSE_IN_ENDLESS_DEF ((uint8_t) 0x8)
12907
12917#define PM_PARSE_ACCEPTS_STATEMENT ((uint8_t) 0x10)
12918
12919static pm_node_t *
12920parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
12921
12926static pm_node_t *
12927parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
12928 pm_node_t *node = parse_expression(parser, binding_power, flags, diag_id, depth);
12929 pm_assert_value_expression(parser, node);
12930 return node;
12931}
12932
12951static PRISM_INLINE bool
12952token_begins_expression_p(pm_token_type_t type) {
12953 switch (type) {
12954 case PM_TOKEN_EQUAL_GREATER:
12955 case PM_TOKEN_KEYWORD_IN:
12956 // We need to special case this because it is a binary operator that
12957 // should not be marked as beginning an expression.
12958 return false;
12959 case PM_TOKEN_BRACE_RIGHT:
12960 case PM_TOKEN_BRACKET_RIGHT:
12961 case PM_TOKEN_COLON:
12962 case PM_TOKEN_COMMA:
12963 case PM_TOKEN_EMBEXPR_END:
12964 case PM_TOKEN_EOF:
12965 case PM_TOKEN_LAMBDA_BEGIN:
12966 case PM_TOKEN_KEYWORD_DO:
12967 case PM_TOKEN_KEYWORD_DO_BLOCK:
12968 case PM_TOKEN_KEYWORD_DO_LAMBDA:
12969 case PM_TOKEN_KEYWORD_DO_LOOP:
12970 case PM_TOKEN_KEYWORD_END:
12971 case PM_TOKEN_KEYWORD_ELSE:
12972 case PM_TOKEN_KEYWORD_ELSIF:
12973 case PM_TOKEN_KEYWORD_ENSURE:
12974 case PM_TOKEN_KEYWORD_THEN:
12975 case PM_TOKEN_KEYWORD_RESCUE:
12976 case PM_TOKEN_KEYWORD_WHEN:
12977 case PM_TOKEN_NEWLINE:
12978 case PM_TOKEN_PARENTHESIS_RIGHT:
12979 case PM_TOKEN_SEMICOLON:
12980 // The reason we need this short-circuit is because we're using the
12981 // binding powers table to tell us if the subsequent token could
12982 // potentially be the start of an expression. If there _is_ a binding
12983 // power for one of these tokens, then we should remove it from this list
12984 // and let it be handled by the default case below.
12985 assert(pm_binding_powers[type].left == PM_BINDING_POWER_UNSET);
12986 return false;
12987 case PM_TOKEN_UAMPERSAND:
12988 // This is a special case because this unary operator cannot appear
12989 // as a general operator, it only appears in certain circumstances.
12990 return false;
12991 case PM_TOKEN_UCOLON_COLON:
12992 case PM_TOKEN_UMINUS:
12993 case PM_TOKEN_UMINUS_NUM:
12994 case PM_TOKEN_UPLUS:
12995 case PM_TOKEN_BANG:
12996 case PM_TOKEN_TILDE:
12997 case PM_TOKEN_UDOT_DOT:
12998 case PM_TOKEN_UDOT_DOT_DOT:
12999 // These unary tokens actually do have binding power associated with them
13000 // so that we can correctly place them into the precedence order. But we
13001 // want them to be marked as beginning an expression, so we need to
13002 // special case them here.
13003 return true;
13004 default:
13005 return pm_binding_powers[type].left == PM_BINDING_POWER_UNSET;
13006 }
13007}
13008
13024static PRISM_INLINE bool
13025token_begins_pattern_p(pm_token_type_t type) {
13026 return (
13027 token_begins_expression_p(type) ||
13028 type == PM_TOKEN_USTAR ||
13029 type == PM_TOKEN_USTAR_STAR ||
13030 type == PM_TOKEN_CARET
13031 );
13032}
13033
13038static pm_node_t *
13039parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
13040 if (accept1(parser, PM_TOKEN_USTAR)) {
13041 pm_token_t operator = parser->previous;
13042 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));
13043 return UP(pm_splat_node_create(parser, &operator, expression));
13044 }
13045
13046 return parse_value_expression(parser, binding_power, flags, diag_id, depth);
13047}
13048
13049static bool
13050pm_node_unreference_each(const pm_node_t *node, void *data) {
13051 switch (PM_NODE_TYPE(node)) {
13052 /* When we are about to destroy a set of nodes that could potentially
13053 * contain block exits for the current scope, we need to check if they
13054 * are contained in the list of block exits and remove them if they are.
13055 */
13056 case PM_BREAK_NODE:
13057 case PM_NEXT_NODE:
13058 case PM_REDO_NODE: {
13059 pm_parser_t *parser = (pm_parser_t *) data;
13060 size_t index = 0;
13061
13062 while (index < parser->current_block_exits->size) {
13063 pm_node_t *block_exit = parser->current_block_exits->nodes[index];
13064
13065 if (block_exit == node) {
13066 if (index + 1 < parser->current_block_exits->size) {
13067 memmove(
13068 &parser->current_block_exits->nodes[index],
13069 &parser->current_block_exits->nodes[index + 1],
13070 (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *)
13071 );
13072 }
13073 parser->current_block_exits->size--;
13074
13075 /* Note returning true here because these nodes could have
13076 * arguments that are themselves block exits. */
13077 return true;
13078 }
13079
13080 index++;
13081 }
13082
13083 return true;
13084 }
13085 /* When an implicit local variable is written to or targeted, it becomes
13086 * a regular, named local variable. This branch removes it from the list
13087 * of implicit parameters when that happens. */
13088 case PM_LOCAL_VARIABLE_READ_NODE:
13089 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13090 pm_parser_t *parser = (pm_parser_t *) data;
13091 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
13092
13093 for (size_t index = 0; index < implicit_parameters->size; index++) {
13094 if (implicit_parameters->nodes[index] == node) {
13095 /* If the node is not the last one in the list, we need to
13096 * shift the remaining nodes down to fill the gap. This is
13097 * extremely unlikely to happen. */
13098 if (index != implicit_parameters->size - 1) {
13099 memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *));
13100 }
13101
13102 implicit_parameters->size--;
13103 break;
13104 }
13105 }
13106
13107 return false;
13108 }
13109 default:
13110 return true;
13111 }
13112}
13113
13119static void
13120pm_node_unreference(pm_parser_t *parser, const pm_node_t *node) {
13121 pm_visit_node(node, pm_node_unreference_each, parser);
13122}
13123
13128static void
13129parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) {
13130 // The method name needs to change. If we previously had
13131 // foo, we now need foo=. In this case we'll allocate a new
13132 // owned string, copy the previous method name in, and
13133 // append an =.
13134 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *name_field);
13135 size_t length = constant->length;
13136 uint8_t *name = (uint8_t *) pm_arena_alloc(parser->arena, length + 1, 1);
13137
13138 memcpy(name, constant->start, length);
13139 name[length] = '=';
13140
13141 *name_field = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, name, length + 1);
13142}
13143
13150static pm_node_t *
13151parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) {
13152 switch (PM_NODE_TYPE(target)) {
13153 case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13154 case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13155 case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13156 case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13157 case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13158 case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13159 case PM_TRUE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13160 default: break;
13161 }
13162
13163 pm_constant_id_t name = pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target));
13164 pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0);
13165
13166 return UP(result);
13167}
13168
13177static pm_node_t *
13178parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_parent) {
13179 switch (PM_NODE_TYPE(target)) {
13180 case PM_ERROR_RECOVERY_NODE:
13181 return target;
13182 case PM_SOURCE_ENCODING_NODE:
13183 case PM_FALSE_NODE:
13184 case PM_SOURCE_FILE_NODE:
13185 case PM_SOURCE_LINE_NODE:
13186 case PM_NIL_NODE:
13187 case PM_SELF_NODE:
13188 case PM_TRUE_NODE: {
13189 // In these special cases, we have specific error messages and we
13190 // will replace them with local variable writes.
13191 return parse_unwriteable_target(parser, target);
13192 }
13193 case PM_CLASS_VARIABLE_READ_NODE:
13195 target->type = PM_CLASS_VARIABLE_TARGET_NODE;
13196 return target;
13197 case PM_CONSTANT_PATH_NODE:
13198 if (context_def_p(parser)) {
13199 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13200 }
13201
13203 target->type = PM_CONSTANT_PATH_TARGET_NODE;
13204
13205 return target;
13206 case PM_CONSTANT_READ_NODE:
13207 if (context_def_p(parser)) {
13208 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13209 }
13210
13211 assert(sizeof(pm_constant_target_node_t) == sizeof(pm_constant_read_node_t));
13212 target->type = PM_CONSTANT_TARGET_NODE;
13213
13214 return target;
13215 case PM_BACK_REFERENCE_READ_NODE:
13216 case PM_NUMBERED_REFERENCE_READ_NODE:
13217 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13218 return UP(pm_error_recovery_node_create_unexpected(parser, target));
13219 case PM_GLOBAL_VARIABLE_READ_NODE:
13221 target->type = PM_GLOBAL_VARIABLE_TARGET_NODE;
13222 return target;
13223 case PM_LOCAL_VARIABLE_READ_NODE: {
13224 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13225 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(target));
13226 pm_node_unreference(parser, target);
13227 }
13228
13229 const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target;
13230 uint32_t name = cast->name;
13231 uint32_t depth = cast->depth;
13232 pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name);
13233
13235 target->type = PM_LOCAL_VARIABLE_TARGET_NODE;
13236
13237 return target;
13238 }
13239 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13240 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13241 pm_node_t *node = UP(pm_local_variable_target_node_create(parser, &target->location, name, 0));
13242
13243 pm_node_unreference(parser, target);
13244
13245 return node;
13246 }
13247 case PM_INSTANCE_VARIABLE_READ_NODE:
13249 target->type = PM_INSTANCE_VARIABLE_TARGET_NODE;
13250 return target;
13251 case PM_MULTI_TARGET_NODE:
13252 if (splat_parent) {
13253 // Multi target is not accepted in all positions. If this is one
13254 // of them, then we need to add an error.
13255 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13256 }
13257
13258 return target;
13259 case PM_SPLAT_NODE: {
13260 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13261
13262 if (splat->expression != NULL) {
13263 splat->expression = parse_target(parser, splat->expression, multiple, true);
13264 }
13265
13266 return UP(splat);
13267 }
13268 case PM_CALL_NODE: {
13269 pm_call_node_t *call = (pm_call_node_t *) target;
13270
13271 // If we have no arguments to the call node and we need this to be a
13272 // target then this is either a method call or a local variable
13273 // write.
13274 if (
13275 (call->message_loc.length > 0) &&
13276 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13277 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13278 (call->opening_loc.length == 0) &&
13279 (call->arguments == NULL) &&
13280 (call->block == NULL)
13281 ) {
13282 if (call->receiver == NULL) {
13283 // When we get here, we have a local variable write, because it
13284 // was previously marked as a method call but now we have an =.
13285 // This looks like:
13286 //
13287 // foo = 1
13288 //
13289 // When it was parsed in the prefix position, foo was seen as a
13290 // method call with no receiver and no arguments. Now we have an
13291 // =, so we know it's a local variable write.
13292 pm_location_t message_loc = call->message_loc;
13293 pm_constant_id_t name = pm_parser_local_add_location(parser, &message_loc, 0);
13294
13295 return UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0));
13296 }
13297
13298 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)) {
13299 if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
13300 pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION);
13301 }
13302
13303 parse_write_name(parser, &call->name);
13304 return UP(pm_call_target_node_create(parser, call));
13305 }
13306 }
13307
13308 // If there is no call operator and the message is "[]" then this is
13309 // an aref expression, and we can transform it into an aset
13310 // expression.
13311 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13312 return UP(pm_index_target_node_create(parser, call));
13313 }
13314 }
13316 default:
13317 // In this case we have a node that we don't know how to convert
13318 // into a target. We need to treat it as an error. For now, we'll
13319 // mark it as an error and just skip right past it.
13320 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13321 return target;
13322 }
13323}
13324
13329static pm_node_t *
13330parse_target_validate(pm_parser_t *parser, pm_node_t *target, bool multiple) {
13331 pm_node_t *result = parse_target(parser, target, multiple, false);
13332
13333 // Ensure that we have one of an =, an 'in' in for indexes, and a ')' in
13334 // parens after the targets.
13335 if (
13336 !match1(parser, PM_TOKEN_EQUAL) &&
13337 !(context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) &&
13338 !(context_p(parser, PM_CONTEXT_PARENS) && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT))
13339 ) {
13340 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13341 }
13342
13343 return result;
13344}
13345
13350static pm_node_t *
13351parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) {
13352 pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser);
13353
13354 if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) {
13355 return UP(pm_shareable_constant_node_create(parser, write, shareable_constant));
13356 }
13357
13358 return write;
13359}
13360
13364static pm_node_t *
13365parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_node_t *value) {
13366 switch (PM_NODE_TYPE(target)) {
13367 case PM_ERROR_RECOVERY_NODE:
13368 return target;
13369 case PM_CLASS_VARIABLE_READ_NODE: {
13370 pm_class_variable_write_node_t *node = pm_class_variable_write_node_create(parser, (pm_class_variable_read_node_t *) target, operator, value);
13371 return UP(node);
13372 }
13373 case PM_CONSTANT_PATH_NODE: {
13374 pm_node_t *node = UP(pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value));
13375
13376 if (context_def_p(parser)) {
13377 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13378 }
13379
13380 return parse_shareable_constant_write(parser, node);
13381 }
13382 case PM_CONSTANT_READ_NODE: {
13383 pm_node_t *node = UP(pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value));
13384
13385 if (context_def_p(parser)) {
13386 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13387 }
13388
13389 return parse_shareable_constant_write(parser, node);
13390 }
13391 case PM_BACK_REFERENCE_READ_NODE:
13392 case PM_NUMBERED_REFERENCE_READ_NODE:
13393 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13395 case PM_GLOBAL_VARIABLE_READ_NODE: {
13396 pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value);
13397 return UP(node);
13398 }
13399 case PM_LOCAL_VARIABLE_READ_NODE: {
13401
13402 pm_location_t location = target->location;
13403 pm_constant_id_t name = local_read->name;
13404 uint32_t depth = local_read->depth;
13405 pm_scope_t *scope = pm_parser_scope_find(parser, depth);
13406
13407 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13408 pm_diagnostic_id_t diag_id = (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED;
13409 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), diag_id, parser->start + PM_NODE_START(target));
13410 pm_node_unreference(parser, target);
13411 }
13412
13413 pm_locals_unread(&scope->locals, name);
13414
13415 return UP(pm_local_variable_write_node_create(parser, name, depth, value, &location, operator));
13416 }
13417 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13418 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13419 pm_node_t *node = UP(pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator));
13420
13421 pm_node_unreference(parser, target);
13422
13423 return node;
13424 }
13425 case PM_INSTANCE_VARIABLE_READ_NODE: {
13426 pm_node_t *write_node = UP(pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value));
13427 return write_node;
13428 }
13429 case PM_MULTI_TARGET_NODE:
13430 return UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value));
13431 case PM_SPLAT_NODE: {
13432 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13433
13434 if (splat->expression != NULL) {
13435 splat->expression = parse_write(parser, splat->expression, operator, value);
13436 }
13437
13438 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
13439 pm_multi_target_node_targets_append(parser, multi_target, UP(splat));
13440
13441 return UP(pm_multi_write_node_create(parser, multi_target, operator, value));
13442 }
13443 case PM_CALL_NODE: {
13444 pm_call_node_t *call = (pm_call_node_t *) target;
13445
13446 // If we have no arguments to the call node and we need this to be a
13447 // target then this is either a method call or a local variable
13448 // write.
13449 if (
13450 (call->message_loc.length > 0) &&
13451 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13452 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13453 (call->opening_loc.length == 0) &&
13454 (call->arguments == NULL) &&
13455 (call->block == NULL)
13456 ) {
13457 if (call->receiver == NULL) {
13458 // When we get here, we have a local variable write, because it
13459 // was previously marked as a method call but now we have an =.
13460 // This looks like:
13461 //
13462 // foo = 1
13463 //
13464 // When it was parsed in the prefix position, foo was seen as a
13465 // method call with no receiver and no arguments. Now we have an
13466 // =, so we know it's a local variable write.
13467 pm_location_t message_loc = call->message_loc;
13468
13469 pm_refute_numbered_parameter(parser, message_loc.start, message_loc.length);
13470 pm_parser_local_add_location(parser, &message_loc, 0);
13471
13472 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));
13473 target = UP(pm_local_variable_write_node_create(parser, constant_id, 0, value, &message_loc, operator));
13474
13475 return target;
13476 }
13477
13478 if (char_is_identifier_start(parser, parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)) {
13479 // When we get here, we have a method call, because it was
13480 // previously marked as a method call but now we have an =. This
13481 // looks like:
13482 //
13483 // foo.bar = 1
13484 //
13485 // When it was parsed in the prefix position, foo.bar was seen as a
13486 // method call with no arguments. Now we have an =, so we know it's
13487 // a method call with an argument. In this case we will create the
13488 // arguments node, parse the argument, and add it to the list.
13489 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
13490 call->arguments = arguments;
13491
13492 pm_arguments_node_arguments_append(parser->arena, arguments, value);
13493 PM_NODE_LENGTH_SET_NODE(call, arguments);
13494 call->equal_loc = TOK2LOC(parser, operator);
13495
13496 parse_write_name(parser, &call->name);
13497 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13498
13499 return UP(call);
13500 }
13501 }
13502
13503 // If there is no call operator and the message is "[]" then this is
13504 // an aref expression, and we can transform it into an aset
13505 // expression.
13506 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13507 if (call->arguments == NULL) {
13508 call->arguments = pm_arguments_node_create(parser);
13509 }
13510
13511 pm_arguments_node_arguments_append(parser->arena, call->arguments, value);
13512 PM_NODE_LENGTH_SET_NODE(target, value);
13513
13514 // Replace the name with "[]=".
13515 call->name = pm_parser_constant_id_constant(parser, "[]=", 3);
13516 call->equal_loc = TOK2LOC(parser, operator);
13517
13518 // Ensure that the arguments for []= don't contain keywords
13519 pm_index_arguments_check(parser, call->arguments, call->block);
13520 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13521
13522 return target;
13523 }
13524
13525 // If there are arguments on the call node, then it can't be a
13526 // method call ending with = or a local variable write, so it must
13527 // be a syntax error. In this case we'll fall through to our default
13528 // handling. We need to free the value that we parsed because there
13529 // is no way for us to attach it to the tree at this point.
13530 //
13531 // Since it is possible for the value to contain an implicit
13532 // parameter somewhere in its subtree, we need to walk it and remove
13533 // any implicit parameters from the list of implicit parameters for
13534 // the current scope.
13535 pm_node_unreference(parser, value);
13536 }
13538 default:
13539 // In this case we have a node that we don't know how to convert into a
13540 // target. We need to treat it as an error. For now, we'll mark it as an
13541 // error and just skip right past it.
13542 pm_parser_err_token(parser, operator, PM_ERR_WRITE_TARGET_UNEXPECTED);
13543 return target;
13544 }
13545}
13546
13553static pm_node_t *
13554parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) {
13555 switch (PM_NODE_TYPE(target)) {
13556 case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13557 case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13558 case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13559 case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13560 case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13561 case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13562 case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13563 default: break;
13564 }
13565
13566 pm_constant_id_t name = pm_parser_local_add_location(parser, &target->location, 1);
13567 pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals);
13568
13569 return UP(result);
13570}
13571
13582static pm_node_t *
13583parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13584 bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE);
13585
13586 pm_multi_target_node_t *result = pm_multi_target_node_create(parser);
13587 pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target, true, false));
13588
13589 while (accept1(parser, PM_TOKEN_COMMA)) {
13590 if (accept1(parser, PM_TOKEN_USTAR)) {
13591 // Here we have a splat operator. It can have a name or be
13592 // anonymous. It can be the final target or be in the middle if
13593 // there haven't been any others yet.
13594 if (has_rest) {
13595 pm_parser_err_previous(parser, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
13596 }
13597
13598 pm_token_t star_operator = parser->previous;
13599 pm_node_t *name = NULL;
13600
13601 if (token_begins_expression_p(parser->current.type)) {
13602 name = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
13603 name = parse_target(parser, name, true, true);
13604 }
13605
13606 pm_node_t *splat = UP(pm_splat_node_create(parser, &star_operator, name));
13607 pm_multi_target_node_targets_append(parser, result, splat);
13608 has_rest = true;
13609 } else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
13610 context_push(parser, PM_CONTEXT_MULTI_TARGET);
13611 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13612 target = parse_target(parser, target, true, false);
13613
13614 pm_multi_target_node_targets_append(parser, result, target);
13615 context_pop(parser);
13616 } else if (token_begins_expression_p(parser->current.type)) {
13617 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13618 target = parse_target(parser, target, true, false);
13619
13620 pm_multi_target_node_targets_append(parser, result, target);
13621 } else if (!match1(parser, PM_TOKEN_EOF)) {
13622 // If we get here, then we have a trailing , in a multi target node.
13623 // We'll add an implicit rest node to represent this.
13624 pm_node_t *rest = UP(pm_implicit_rest_node_create(parser, &parser->previous));
13625 pm_multi_target_node_targets_append(parser, result, rest);
13626 break;
13627 }
13628 }
13629
13630 return UP(result);
13631}
13632
13637static pm_node_t *
13638parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13639 pm_node_t *result = parse_targets(parser, first_target, binding_power, depth);
13640
13641 // If we're inside parentheses, then we allow a newline before the
13642 // closing parenthesis or equals sign. Outside of parentheses, a newline
13643 // is not allowed (e.g., `a, b\n= 1, 2` is not valid).
13644 if (context_p(parser, PM_CONTEXT_PARENS) || context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
13645 accept1(parser, PM_TOKEN_NEWLINE);
13646 }
13647
13648 // Ensure that we have either an = or a ) after the targets.
13649 if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
13650 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13651 }
13652
13653 return result;
13654}
13655
13659static pm_statements_node_t *
13660parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
13661 // First, skip past any optional terminators that might be at the beginning
13662 // of the statements.
13663 while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE));
13664
13665 // If we have a terminator, then we can just return NULL.
13666 if (context_terminator(context, &parser->current)) return NULL;
13667
13668 pm_statements_node_t *statements = pm_statements_node_create(parser);
13669
13670 // At this point we know we have at least one statement, and that it
13671 // immediately follows the current token.
13672 context_push(parser, context);
13673
13674 while (true) {
13675 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));
13676 pm_statements_node_body_append(parser, statements, node, true);
13677
13678 // If we're recovering from a syntax error, then we need to stop parsing
13679 // the statements now.
13680 if (parser->recovering) {
13681 // If this is the level of context where the recovery has happened,
13682 // then we can mark the parser as done recovering.
13683 if (context_terminator(context, &parser->current)) parser->recovering = false;
13684 break;
13685 }
13686
13687 // If we have a terminator, then we will parse all consecutive
13688 // terminators and then continue parsing the statements list.
13689 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
13690 // If we have a terminator, then we will continue parsing the
13691 // statements list.
13692 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13693 if (context_terminator(context, &parser->current)) break;
13694
13695 // Now we can continue parsing the list of statements.
13696 continue;
13697 }
13698
13699 // At this point we have a list of statements that are not terminated by
13700 // a newline or semicolon. At this point we need to check if we're at
13701 // the end of the statements list. If we are, then we should break out
13702 // of the loop.
13703 if (context_terminator(context, &parser->current)) break;
13704
13705 // At this point, we have a syntax error, because the statement was not
13706 // terminated by a newline or semicolon, and we're not at the end of the
13707 // statements list. Ideally we should scan forward to determine if we
13708 // should insert a missing terminator or break out of parsing the
13709 // statements list at this point.
13710 //
13711 // We don't have that yet, so instead we'll do a more naive approach. If
13712 // we were unable to parse an expression, then we will skip past this
13713 // token and continue parsing the statements list. Otherwise we'll add
13714 // an error and continue parsing the statements list.
13715 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) {
13716 parser_lex(parser);
13717
13718 // If we are at the end of the file, then we need to stop parsing
13719 // the statements entirely at this point. Mark the parser as
13720 // recovering, as we know that EOF closes the top-level context, and
13721 // then break out of the loop.
13722 if (match1(parser, PM_TOKEN_EOF)) {
13723 parser->recovering = true;
13724 break;
13725 }
13726
13727 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13728 if (context_terminator(context, &parser->current)) break;
13729 } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) {
13730 // This is an inlined version of accept1 because the error that we
13731 // want to add has varargs. If this happens again, we should
13732 // probably extract a helper function.
13733 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
13734 parser->previous.start = parser->previous.end;
13735 parser->previous.type = 0;
13736 }
13737 }
13738
13739 context_pop(parser);
13740
13741 bool last_value = true;
13742 switch (context) {
13743 case PM_CONTEXT_BEGIN_ENSURE:
13744 case PM_CONTEXT_DEF_ENSURE:
13745 last_value = false;
13746 break;
13747 default:
13748 break;
13749 }
13750 pm_void_statements_check(parser, statements, last_value);
13751
13752 return statements;
13753}
13754
13758static void
13759pm_hash_key_duplicated_warn(pm_parser_t *parser, const pm_node_t *duplicated, const pm_node_t *node) {
13760 pm_buffer_t buffer = { 0 };
13761 pm_static_literal_inspect(&buffer, &parser->line_offsets, parser->start, parser->start_line, parser->encoding, duplicated);
13762
13763 pm_diagnostic_list_append_format(
13764 &parser->metadata_arena,
13765 &parser->warning_list,
13766 duplicated->location.start,
13767 duplicated->location.length,
13768 PM_WARN_DUPLICATED_HASH_KEY,
13769 (int) pm_buffer_length(&buffer),
13770 pm_buffer_value(&buffer),
13771 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line
13772 );
13773
13774 pm_buffer_cleanup(&buffer);
13775}
13776
13781static void
13782pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13783 const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, true);
13784
13785 if (duplicated != NULL) {
13786 pm_hash_key_duplicated_warn(parser, duplicated, node);
13787 }
13788}
13789
13797static void
13798pm_hash_key_static_literals_merge(pm_parser_t *parser, pm_static_literals_t *literals, const pm_hash_node_t *hash, uint32_t boundary) {
13799 const pm_node_list_t *elements = &hash->elements;
13800
13801 for (size_t index = 0; index < elements->size; index++) {
13802 pm_node_t *element = elements->nodes[index];
13803
13804 switch (PM_NODE_TYPE(element)) {
13805 case PM_ASSOC_NODE: {
13806 pm_node_t *key = ((pm_assoc_node_t *) element)->key;
13807 const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, key, true);
13808
13809 if (duplicated != NULL && PM_NODE_START(duplicated) < boundary) {
13810 pm_hash_key_duplicated_warn(parser, duplicated, key);
13811 }
13812
13813 break;
13814 }
13815 case PM_ASSOC_SPLAT_NODE: {
13816 const pm_node_t *value = ((pm_assoc_splat_node_t *) element)->value;
13817
13818 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
13819 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, boundary);
13820 }
13821
13822 break;
13823 }
13824 default:
13825 break;
13826 }
13827 }
13828}
13829
13834static void
13835pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13836 pm_node_t *previous;
13837
13838 if ((previous = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, false)) != NULL) {
13839 pm_diagnostic_list_append_format(
13840 &parser->metadata_arena,
13841 &parser->warning_list,
13842 PM_NODE_START(node),
13843 PM_NODE_LENGTH(node),
13844 PM_WARN_DUPLICATED_WHEN_CLAUSE,
13845 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line,
13846 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(previous), parser->start_line).line
13847 );
13848 }
13849}
13850
13854static bool
13855parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node, uint16_t depth) {
13856 assert(PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE));
13857 bool contains_keyword_splat = false;
13858
13859 while (true) {
13860 pm_node_t *element;
13861
13862 switch (parser->current.type) {
13863 case PM_TOKEN_USTAR_STAR: {
13864 parser_lex(parser);
13865 pm_token_t operator = parser->previous;
13866 pm_node_t *value = NULL;
13867
13868 if (token_begins_expression_p(parser->current.type)) {
13869 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));
13870
13871 /* If the splatted value is itself a hash literal, its keys
13872 * become part of this hash for the duplicate key warning. */
13873 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
13874 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, PM_NODE_START(value));
13875 }
13876 } else {
13877 pm_parser_scope_forwarding_keywords_check(parser, &operator);
13878 }
13879
13880 element = UP(pm_assoc_splat_node_create(parser, value, &operator));
13881 contains_keyword_splat = true;
13882 break;
13883 }
13884 case PM_TOKEN_LABEL: {
13885 pm_token_t label = parser->current;
13886 parser_lex(parser);
13887
13888 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &label));
13889 pm_hash_key_static_literals_add(parser, literals, key);
13890
13891 pm_node_t *value = NULL;
13892
13893 if (token_begins_expression_p(parser->current.type)) {
13894 value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_EXPRESSION_AFTER_LABEL, (uint16_t) (depth + 1));
13895 } else {
13896 if (parser->encoding->isupper_char(label.start, (label.end - 1) - label.start)) {
13897 pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 };
13898 value = UP(pm_constant_read_node_create(parser, &constant));
13899 } else {
13900 int depth = -1;
13901 pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 };
13902
13903 if (identifier.end[-1] == '!' || identifier.end[-1] == '?') {
13904 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ);
13905 } else {
13906 depth = pm_parser_local_depth(parser, &identifier);
13907 }
13908
13909 if (depth == -1) {
13910 value = UP(pm_call_node_variable_call_create(parser, &identifier));
13911 } else {
13912 value = UP(pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth));
13913 }
13914 }
13915
13916 value->location.length++;
13917 value = UP(pm_implicit_node_create(parser, value));
13918 }
13919
13920 element = UP(pm_assoc_node_create(parser, key, NULL, value));
13921 break;
13922 }
13923 default: {
13924 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));
13925
13926 // Hash keys that are strings are automatically frozen. We will
13927 // mark that here.
13928 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
13929 pm_node_flag_set(key, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
13930 }
13931
13932 pm_hash_key_static_literals_add(parser, literals, key);
13933
13934 pm_token_t operator = { 0 };
13935 if (!pm_symbol_node_label_p(parser, key)) {
13936 expect1(parser, PM_TOKEN_EQUAL_GREATER, PM_ERR_HASH_ROCKET);
13937 operator = parser->previous;
13938 }
13939
13940 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));
13941 element = UP(pm_assoc_node_create(parser, key, NTOK2PTR(operator), value));
13942 break;
13943 }
13944 }
13945
13946 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
13947 pm_hash_node_elements_append(parser->arena, (pm_hash_node_t *) node, element);
13948 } else {
13949 pm_keyword_hash_node_elements_append(parser->arena, (pm_keyword_hash_node_t *) node, element);
13950 }
13951
13952 // If there's no comma after the element, then we're done.
13953 if (!accept1(parser, PM_TOKEN_COMMA)) break;
13954
13955 // If the next element starts with a label or a **, then we know we have
13956 // another element in the hash, so we'll continue parsing.
13957 if (match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)) continue;
13958
13959 // Otherwise we need to check if the subsequent token begins an expression.
13960 // If it does, then we'll continue parsing.
13961 if (token_begins_expression_p(parser->current.type)) continue;
13962
13963 // Otherwise by default we will exit out of this loop.
13964 break;
13965 }
13966
13967 return contains_keyword_splat;
13968}
13969
13970static PRISM_INLINE bool
13971argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) {
13972 if (pm_symbol_node_label_p(parser, argument)) {
13973 return true;
13974 }
13975
13976 switch (PM_NODE_TYPE(argument)) {
13977 case PM_CALL_NODE: {
13978 pm_call_node_t *cast = (pm_call_node_t *) argument;
13979 if (cast->opening_loc.length == 0 && cast->arguments != NULL) {
13980 if (PM_NODE_FLAG_P(cast->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS | PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
13981 return false;
13982 }
13983 if (cast->block != NULL) {
13984 return false;
13985 }
13986 }
13987 break;
13988 }
13989 default: break;
13990 }
13991 return accept1(parser, PM_TOKEN_EQUAL_GREATER);
13992}
13993
13997static PRISM_INLINE void
13998parse_arguments_append(pm_parser_t *parser, pm_arguments_t *arguments, pm_node_t *argument) {
13999 if (arguments->arguments == NULL) {
14000 arguments->arguments = pm_arguments_node_create(parser);
14001 }
14002
14003 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, argument);
14004}
14005
14010static PRISM_INLINE bool
14011pm_call_node_command_p(const pm_call_node_t *node) {
14012 return (
14013 (node->opening_loc.length == 0) &&
14014 (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) &&
14015 (node->arguments != NULL || node->block != NULL)
14016 );
14017}
14018
14028static bool
14029pm_constant_path_command_call_p(const pm_parser_t *parser, const pm_call_node_t *call) {
14030 return (
14031 call->receiver != NULL &&
14032 call->opening_loc.length == 0 &&
14033 call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE) &&
14034 call->call_operator_loc.length > 0 &&
14035 parser->start[call->call_operator_loc.start] == ':' &&
14036 call->message_loc.length > 0 &&
14037 parser->encoding->isupper_char(parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)
14038 );
14039}
14040
14046static bool
14047pm_command_call_value_p(const pm_parser_t *parser, const pm_node_t *node) {
14048 switch (PM_NODE_TYPE(node)) {
14049 case PM_CALL_NODE: {
14050 const pm_call_node_t *call = (const pm_call_node_t *) node;
14051
14052 /* Command-style calls (e.g., foo bar, obj.foo bar). Attribute
14053 * writes (e.g., a.b = 1) are not commands. */
14054 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)) {
14055 return true;
14056 }
14057
14058 /* A constant-path command with a brace block, e.g. `Foo::Bar { }`. */
14059 if (pm_constant_path_command_call_p(parser, call)) {
14060 return true;
14061 }
14062
14063 /* A `!` or `not` prefix wrapping a command call (e.g., `!foo bar`,
14064 * `not foo bar`) is also a command-call value. */
14065 if (call->receiver != NULL && call->arguments == NULL && call->opening_loc.length == 0 && call->call_operator_loc.length == 0) {
14066 return pm_command_call_value_p(parser, call->receiver);
14067 }
14068
14069 return false;
14070 }
14071 case PM_SUPER_NODE: {
14072 /* A command-style super (no parens). A super carrying a do-block is
14073 * a block call (it permits chaining), so it is excluded here and
14074 * handled by pm_block_call_p instead. */
14075 const pm_super_node_t *cast = (const pm_super_node_t *) node;
14076 return cast->lparen_loc.length == 0 &&
14077 (cast->arguments != NULL || cast->block != NULL) &&
14078 !(cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE));
14079 }
14080 case PM_YIELD_NODE: {
14081 const pm_yield_node_t *cast = (const pm_yield_node_t *) node;
14082 return cast->lparen_loc.length == 0 && cast->arguments != NULL;
14083 }
14084 case PM_RESCUE_MODIFIER_NODE:
14085 return pm_command_call_value_p(parser, ((const pm_rescue_modifier_node_t *) node)->expression);
14086 case PM_DEF_NODE: {
14087 const pm_def_node_t *cast = (const pm_def_node_t *) node;
14088 if (cast->equal_loc.length > 0 && cast->body != NULL) {
14089 const pm_node_t *body = cast->body;
14090 if (PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE)) {
14091 body = ((const pm_statements_node_t *) body)->body.nodes[((const pm_statements_node_t *) body)->body.size - 1];
14092 }
14093 return pm_command_call_value_p(parser, body);
14094 }
14095 return false;
14096 }
14097 default:
14098 return false;
14099 }
14100}
14101
14108static bool
14109pm_block_call_p(const pm_node_t *node) {
14110 while (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
14111 const pm_call_node_t *call = (const pm_call_node_t *) node;
14112
14113 /* Root: a command (no parentheses) carrying command arguments and a
14114 * block (brace or do), e.g. `foo bar do end`, `foo bar { }`. The
14115 * no-parentheses requirement is what distinguishes a command root from
14116 * a method call root like `foo.bar(1) { }`, which is a primary value
14117 * and may be used as an argument.
14118 */
14119 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE)) {
14120 return true;
14121 }
14122
14123 /* Walk up the receiver chain of a `.`/`::`/`&.` call (e.g.,
14124 * `foo bar do end.baz(1)`). Parentheses on the chained call are allowed
14125 * here -- in parse.y a `block_call` can be extended by
14126 * `call_op2 operation2 opt_paren_args` and remains a block call.
14127 */
14128 if (call->call_operator_loc.length > 0 && call->receiver != NULL) {
14129 node = call->receiver;
14130 continue;
14131 }
14132
14133 return false;
14134 }
14135
14136 /* A `super` with command arguments and a do-block is also a block-call root
14137 * (parse.y: `command do_block`, where the command is `keyword_super
14138 * command_args`). `super do end` with no arguments is a forwarding super
14139 * (a primary value) and is handled elsewhere.
14140 */
14141 if (PM_NODE_TYPE_P(node, PM_SUPER_NODE)) {
14142 const pm_super_node_t *super = (const pm_super_node_t *) node;
14143 return super->lparen_loc.length == 0 && super->block != NULL && PM_NODE_TYPE_P(super->block, PM_BLOCK_NODE);
14144 }
14145
14146 return false;
14147}
14148
14152static void
14153parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator, uint8_t flags, uint16_t depth) {
14154 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
14155
14156 // First we need to check if the next token is one that could be the start
14157 // of an argument. If it's not, then we can just return.
14158 if (
14159 match2(parser, terminator, PM_TOKEN_EOF) ||
14160 (binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) ||
14161 context_terminator(parser->current_context->context, &parser->current)
14162 ) {
14163 return;
14164 }
14165
14166 bool parsed_first_argument = false;
14167 bool parsed_bare_hash = false;
14168 bool parsed_block_argument = false;
14169 bool parsed_forwarding_arguments = false;
14170
14171 while (!match1(parser, PM_TOKEN_EOF)) {
14172 if (parsed_forwarding_arguments) {
14173 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES);
14174 }
14175
14176 pm_node_t *argument = NULL;
14177
14178 switch (parser->current.type) {
14179 case PM_TOKEN_USTAR_STAR:
14180 case PM_TOKEN_LABEL: {
14181 if (parsed_bare_hash) {
14182 pm_parser_err_current(parser, PM_ERR_ARGUMENT_BARE_HASH);
14183 }
14184
14185 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
14186 argument = UP(hash);
14187
14188 pm_static_literals_t hash_keys = { 0 };
14189 bool contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1));
14190
14191 parse_arguments_append(parser, arguments, argument);
14192
14193 pm_node_flags_t node_flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14194 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14195 pm_node_flag_set(UP(arguments->arguments), node_flags);
14196
14197 pm_static_literals_free(&hash_keys);
14198 parsed_bare_hash = true;
14199
14200 break;
14201 }
14202 case PM_TOKEN_UAMPERSAND: {
14203 parser_lex(parser);
14204 pm_token_t operator = parser->previous;
14205 pm_node_t *expression = NULL;
14206
14207 if (token_begins_expression_p(parser->current.type)) {
14208 expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14209 } else {
14210 pm_parser_scope_forwarding_block_check(parser, &operator);
14211 }
14212
14213 argument = UP(pm_block_argument_node_create(parser, &operator, expression));
14214 if (parsed_block_argument) {
14215 parse_arguments_append(parser, arguments, argument);
14216 } else {
14217 arguments->block = argument;
14218 }
14219
14220 if (match1(parser, PM_TOKEN_COMMA)) {
14221 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK);
14222 }
14223
14224 parsed_block_argument = true;
14225 break;
14226 }
14227 case PM_TOKEN_USTAR: {
14228 parser_lex(parser);
14229 pm_token_t operator = parser->previous;
14230
14231 if (match4(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_SEMICOLON, PM_TOKEN_BRACKET_RIGHT)) {
14232 pm_parser_scope_forwarding_positionals_check(parser, &operator);
14233 argument = UP(pm_splat_node_create(parser, &operator, NULL));
14234 if (parsed_bare_hash) {
14235 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT);
14236 }
14237 } else {
14238 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));
14239
14240 if (parsed_bare_hash) {
14241 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);
14242 }
14243
14244 argument = UP(pm_splat_node_create(parser, &operator, expression));
14245 }
14246
14247 parse_arguments_append(parser, arguments, argument);
14248 break;
14249 }
14250 case PM_TOKEN_UDOT_DOT_DOT: {
14251 if (accepts_forwarding) {
14252 parser_lex(parser);
14253
14254 if (token_begins_expression_p(parser->current.type)) {
14255 // If the token begins an expression then this ... was
14256 // not actually argument forwarding but was instead a
14257 // range.
14258 pm_token_t operator = parser->previous;
14259 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));
14260
14261 // If we parse a range, we need to validate that we
14262 // didn't accidentally violate the nonassoc rules of the
14263 // ... operator.
14264 if (PM_NODE_TYPE_P(right, PM_RANGE_NODE)) {
14265 pm_range_node_t *range = (pm_range_node_t *) right;
14266 pm_parser_err(parser, range->operator_loc.start, range->operator_loc.length, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
14267 }
14268
14269 argument = UP(pm_range_node_create(parser, NULL, &operator, right));
14270 } else {
14271 pm_parser_scope_forwarding_all_check(parser, &parser->previous);
14272 if (parsed_first_argument && terminator == PM_TOKEN_EOF) {
14273 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND);
14274 }
14275
14276 argument = UP(pm_forwarding_arguments_node_create(parser, &parser->previous));
14277 parse_arguments_append(parser, arguments, argument);
14278 pm_node_flag_set(UP(arguments->arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
14279 arguments->has_forwarding = true;
14280 parsed_forwarding_arguments = true;
14281 break;
14282 }
14283 }
14284 }
14286 default: {
14287 if (argument == NULL) {
14288 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));
14289 }
14290
14291 bool contains_keywords = false;
14292 bool contains_keyword_splat = false;
14293
14294 if (argument_allowed_for_bare_hash(parser, argument)) {
14295 if (parsed_bare_hash) {
14296 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH);
14297 }
14298
14299 /* A hash key must be an argument (`arg`). A command call or
14300 * block call (e.g. `Foo::Bar { } => v`, `foo bar do end =>
14301 * v`) is not an argument, so reject it as a key. Plain
14302 * command calls never reach here as a key because they
14303 * absorb the `=>` into their own arguments first.
14304 */
14305 if (pm_command_call_value_p(parser, argument) || pm_block_call_p(argument)) {
14306 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->previous.type));
14307 }
14308
14309 pm_token_t operator = { 0 };
14310 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
14311 operator = parser->previous;
14312 }
14313
14314 pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser);
14315 contains_keywords = true;
14316
14317 // Create the set of static literals for this hash.
14318 pm_static_literals_t hash_keys = { 0 };
14319 pm_hash_key_static_literals_add(parser, &hash_keys, argument);
14320
14321 // Finish parsing the one we are part way through.
14322 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));
14323 argument = UP(pm_assoc_node_create(parser, argument, NTOK2PTR(operator), value));
14324
14325 pm_keyword_hash_node_elements_append(parser->arena, bare_hash, argument);
14326 argument = UP(bare_hash);
14327
14328 // Then parse more if we have a comma
14329 if (accept1(parser, PM_TOKEN_COMMA) && (
14330 token_begins_expression_p(parser->current.type) ||
14331 match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)
14332 )) {
14333 contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(bare_hash), (uint16_t) (depth + 1));
14334 }
14335
14336 pm_static_literals_free(&hash_keys);
14337 parsed_bare_hash = true;
14338 }
14339
14340 parse_arguments_append(parser, arguments, argument);
14341
14342 pm_node_flags_t node_flags = 0;
14343 if (contains_keywords) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14344 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14345 pm_node_flag_set(UP(arguments->arguments), node_flags);
14346
14347 break;
14348 }
14349 }
14350
14351 parsed_first_argument = true;
14352
14353 // If parsing the argument failed, we need to stop parsing arguments.
14354 if (PM_NODE_TYPE_P(argument, PM_ERROR_RECOVERY_NODE) || parser->recovering) break;
14355
14356 // If the terminator of these arguments is not EOF, then we have a
14357 // specific token we're looking for. In that case we can accept a
14358 // newline here because it is not functioning as a statement terminator.
14359 bool accepted_newline = false;
14360 if (terminator != PM_TOKEN_EOF) {
14361 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14362 }
14363
14364 if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) {
14365 // If we previously were on a comma and we just parsed a bare hash,
14366 // then we want to continue parsing arguments. This is because the
14367 // comma was grabbed up by the hash parser.
14368 } else if (accept1(parser, PM_TOKEN_COMMA)) {
14369 // If there was a comma, then we need to check if we also accepted a
14370 // newline. If we did, then this is a syntax error.
14371 if (accepted_newline) {
14372 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14373 }
14374
14375 // If this is a command call and an argument takes a block,
14376 // there can be no further arguments. For example,
14377 // `foo(bar 1 do end, 2)` should be rejected.
14378 if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) {
14379 pm_call_node_t *call = (pm_call_node_t *) argument;
14380 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL) {
14381 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14382 break;
14383 }
14384 }
14385 } else {
14386 // If there is no comma at the end of the argument list then we're
14387 // done parsing arguments and can break out of this loop.
14388 break;
14389 }
14390
14391 // If we hit the terminator, then that means we have a trailing comma so
14392 // we can accept that output as well.
14393 if (match1(parser, terminator)) {
14394 // A forwarding `...` argument must be the last argument and cannot
14395 // be followed by a trailing comma, e.g. `foo(...,)`. A comma
14396 // followed by another argument is already rejected at the top of
14397 // this loop, so the only case left to reject here is the trailing
14398 // one.
14399 if (parsed_forwarding_arguments) {
14400 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14401 }
14402
14403 break;
14404 }
14405 }
14406}
14407
14419parse_required_destructured_parameter(pm_parser_t *parser) {
14420 expect1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER);
14421
14422 pm_multi_target_node_t *node = pm_multi_target_node_create(parser);
14423 pm_multi_target_node_opening_set(parser, node, &parser->previous);
14424
14425 do {
14426 pm_node_t *param;
14427
14428 // If we get here then we have a trailing comma, which isn't allowed in
14429 // the grammar. In other places, multi targets _do_ allow trailing
14430 // commas, so here we'll assume this is a mistake of the user not
14431 // knowing it's not allowed here.
14432 if (node->lefts.size > 0 && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
14433 param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14434 pm_multi_target_node_targets_append(parser, node, param);
14435 pm_parser_err_current(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14436 break;
14437 }
14438
14439 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
14440 param = UP(parse_required_destructured_parameter(parser));
14441 } else if (accept1(parser, PM_TOKEN_USTAR)) {
14442 pm_token_t star = parser->previous;
14443 pm_node_t *value = NULL;
14444
14445 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14446 pm_token_t name = parser->previous;
14447 value = UP(pm_required_parameter_node_create(parser, &name));
14448 if (pm_parser_parameter_name_check(parser, &name)) {
14449 pm_node_flag_set_repeated_parameter(value);
14450 }
14451 pm_parser_local_add_token(parser, &name, 1);
14452 }
14453
14454 param = UP(pm_splat_node_create(parser, &star, value));
14455 } else {
14456 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER);
14457 pm_token_t name = parser->previous;
14458
14459 param = UP(pm_required_parameter_node_create(parser, &name));
14460 if (pm_parser_parameter_name_check(parser, &name)) {
14461 pm_node_flag_set_repeated_parameter(param);
14462 }
14463 pm_parser_local_add_token(parser, &name, 1);
14464 }
14465
14466 pm_multi_target_node_targets_append(parser, node, param);
14467 } while (accept1(parser, PM_TOKEN_COMMA));
14468
14469 accept1(parser, PM_TOKEN_NEWLINE);
14470 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN_REQ_PARAMETER);
14471 pm_multi_target_node_closing_set(parser, node, &parser->previous);
14472
14473 return node;
14474}
14475
14480typedef enum {
14481 PM_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state
14482 PM_PARAMETERS_ORDER_NOTHING_AFTER = 1,
14483 PM_PARAMETERS_ORDER_KEYWORDS_REST,
14484 PM_PARAMETERS_ORDER_KEYWORDS,
14485 PM_PARAMETERS_ORDER_REST,
14486 PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14487 PM_PARAMETERS_ORDER_OPTIONAL,
14488 PM_PARAMETERS_ORDER_NAMED,
14489 PM_PARAMETERS_ORDER_NONE,
14490} pm_parameters_order_t;
14491
14495static pm_parameters_order_t parameters_ordering[PM_TOKEN_MAXIMUM] = {
14496 [0] = PM_PARAMETERS_NO_CHANGE,
14497 [PM_TOKEN_UAMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14498 [PM_TOKEN_AMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14499 [PM_TOKEN_UDOT_DOT_DOT] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14500 [PM_TOKEN_IDENTIFIER] = PM_PARAMETERS_ORDER_NAMED,
14501 [PM_TOKEN_PARENTHESIS_LEFT_GROUPING] = PM_PARAMETERS_ORDER_NAMED,
14502 [PM_TOKEN_EQUAL] = PM_PARAMETERS_ORDER_OPTIONAL,
14503 [PM_TOKEN_LABEL] = PM_PARAMETERS_ORDER_KEYWORDS,
14504 [PM_TOKEN_USTAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14505 [PM_TOKEN_STAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14506 [PM_TOKEN_USTAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST,
14507 [PM_TOKEN_STAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST
14508};
14509
14517static bool
14518update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_order_t *current) {
14519 pm_parameters_order_t state = parameters_ordering[token->type];
14520 if (state == PM_PARAMETERS_NO_CHANGE) return true;
14521
14522 // If we see another ordered argument after a optional argument
14523 // we only continue parsing ordered arguments until we stop seeing ordered arguments.
14524 if (*current == PM_PARAMETERS_ORDER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14525 *current = PM_PARAMETERS_ORDER_AFTER_OPTIONAL;
14526 return true;
14527 } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14528 return true;
14529 }
14530
14531 if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14532 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR);
14533 return false;
14534 } else if (token->type == PM_TOKEN_UDOT_DOT_DOT && (*current >= PM_PARAMETERS_ORDER_KEYWORDS_REST && *current <= PM_PARAMETERS_ORDER_AFTER_OPTIONAL)) {
14535 pm_parser_err_token(parser, token, *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL ? PM_ERR_PARAMETER_FORWARDING_AFTER_REST : PM_ERR_PARAMETER_ORDER);
14536 return false;
14537 } else if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) {
14538 // We know what transition we failed on, so we can provide a better error here.
14539 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_ORDER);
14540 return false;
14541 }
14542
14543 if (state < *current) *current = state;
14544 return true;
14545}
14546
14547static PRISM_INLINE void
14548parse_parameters_handle_trailing_comma(
14549 pm_parser_t *parser,
14550 pm_parameters_node_t *params,
14551 pm_parameters_order_t order,
14552 bool in_block,
14553 bool allows_trailing_comma
14554) {
14555 if (!allows_trailing_comma) {
14556 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14557 return;
14558 }
14559
14560 if (in_block) {
14561 if (order >= PM_PARAMETERS_ORDER_NAMED) {
14562 // foo do |bar,|; end
14563 pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14564
14565 if (params->rest == NULL) {
14566 pm_parameters_node_rest_set(params, param);
14567 } else {
14568 pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI);
14569 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14570 }
14571 } else {
14572 // foo do |*bar,|; end
14573 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14574 }
14575 } else {
14576 // https://bugs.ruby-lang.org/issues/19107
14577 // Allow `def foo(bar,); end`, `def foo(*bar,); end`, etc. but not `def foo(...,); end`
14578 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1 || order == PM_PARAMETERS_ORDER_NOTHING_AFTER) {
14579 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14580 }
14581 }
14582}
14583
14587static pm_parameters_node_t *
14588parse_parameters(
14589 pm_parser_t *parser,
14590 pm_binding_power_t binding_power,
14591 bool uses_parentheses,
14592 bool allows_trailing_comma,
14593 bool allows_forwarding_parameters,
14594 bool accepts_blocks_in_defaults,
14595 bool in_block,
14596 pm_diagnostic_id_t diag_id_forwarding,
14597 uint16_t depth
14598) {
14599 pm_do_loop_stack_push(parser, false);
14600
14601 pm_parameters_node_t *params = pm_parameters_node_create(parser);
14602 pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE;
14603
14604 while (true) {
14605 bool parsing = true;
14606
14607 switch (parser->current.type) {
14608 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
14609 update_parameter_state(parser, &parser->current, &order);
14610 pm_node_t *param = UP(parse_required_destructured_parameter(parser));
14611
14612 if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14613 pm_parameters_node_requireds_append(parser->arena, params, param);
14614 } else {
14615 pm_parameters_node_posts_append(parser->arena, params, param);
14616 }
14617 break;
14618 }
14619 case PM_TOKEN_UAMPERSAND:
14620 case PM_TOKEN_AMPERSAND: {
14621 update_parameter_state(parser, &parser->current, &order);
14622 parser_lex(parser);
14623
14624 pm_token_t operator = parser->previous;
14625 pm_node_t *param;
14626
14627 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1 && accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
14628 param = (pm_node_t *) pm_no_block_parameter_node_create(parser, &operator, &parser->previous);
14629 } else {
14630 pm_token_t name = {0};
14631
14632 bool repeated = false;
14633 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14634 name = parser->previous;
14635 repeated = pm_parser_parameter_name_check(parser, &name);
14636 pm_parser_local_add_token(parser, &name, 1);
14637 } else {
14638 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
14639 }
14640
14641 param = (pm_node_t *) pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator);
14642 if (repeated) {
14643 pm_node_flag_set_repeated_parameter(param);
14644 }
14645 }
14646
14647 if (params->block == NULL) {
14648 pm_parameters_node_block_set(params, param);
14649 } else {
14650 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_BLOCK_MULTI);
14651 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
14652 }
14653
14654 break;
14655 }
14656 case PM_TOKEN_UDOT_DOT_DOT: {
14657 if (!allows_forwarding_parameters) {
14658 pm_parser_err_current(parser, diag_id_forwarding);
14659 }
14660
14661 bool succeeded = update_parameter_state(parser, &parser->current, &order);
14662 parser_lex(parser);
14663
14664 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
14665 pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
14666
14667 if (params->keyword_rest != NULL) {
14668 // If we already have a keyword rest parameter, then we replace it with the
14669 // forwarding parameter and move the keyword rest parameter to the posts list.
14670 pm_node_t *keyword_rest = params->keyword_rest;
14671 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, keyword_rest)));
14672 if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD);
14673 params->keyword_rest = NULL;
14674 }
14675
14676 pm_parameters_node_keyword_rest_set(params, UP(param));
14677 break;
14678 }
14679 case PM_TOKEN_CLASS_VARIABLE:
14680 case PM_TOKEN_IDENTIFIER:
14681 case PM_TOKEN_CONSTANT:
14682 case PM_TOKEN_INSTANCE_VARIABLE:
14683 case PM_TOKEN_GLOBAL_VARIABLE:
14684 case PM_TOKEN_METHOD_NAME: {
14685 parser_lex(parser);
14686 switch (parser->previous.type) {
14687 case PM_TOKEN_CONSTANT:
14688 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14689 break;
14690 case PM_TOKEN_INSTANCE_VARIABLE:
14691 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
14692 break;
14693 case PM_TOKEN_GLOBAL_VARIABLE:
14694 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
14695 break;
14696 case PM_TOKEN_CLASS_VARIABLE:
14697 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
14698 break;
14699 case PM_TOKEN_METHOD_NAME:
14700 pm_parser_err_previous(parser, PM_ERR_PARAMETER_METHOD_NAME);
14701 break;
14702 default: break;
14703 }
14704
14705 if (parser->current.type == PM_TOKEN_EQUAL) {
14706 update_parameter_state(parser, &parser->current, &order);
14707 } else {
14708 update_parameter_state(parser, &parser->previous, &order);
14709 }
14710
14711 pm_token_t name = parser->previous;
14712 bool repeated = pm_parser_parameter_name_check(parser, &name);
14713 pm_parser_local_add_token(parser, &name, 1);
14714
14715 if (match1(parser, PM_TOKEN_EQUAL)) {
14716 pm_token_t operator = parser->current;
14717 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14718 parser_lex(parser);
14719
14720 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
14721 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14722
14723 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14724 pm_node_t *value = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1));
14725 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14726
14727 pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
14728
14729 if (repeated) {
14730 pm_node_flag_set_repeated_parameter(UP(param));
14731 }
14732 pm_parameters_node_optionals_append(parser->arena, params, param);
14733
14734 // If the value of the parameter increased the number of
14735 // reads of that parameter, then we need to warn that we
14736 // have a circular definition.
14737 if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14738 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &name, PM_ERR_PARAMETER_CIRCULAR);
14739 }
14740
14741 context_pop(parser);
14742
14743 // If parsing the value of the parameter resulted in error recovery,
14744 // then we can put a missing node in its place and stop parsing the
14745 // parameters entirely now.
14746 if (parser->recovering) {
14747 parsing = false;
14748 break;
14749 }
14750 } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14751 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14752 if (repeated) {
14753 pm_node_flag_set_repeated_parameter(UP(param));
14754 }
14755 pm_parameters_node_requireds_append(parser->arena, params, UP(param));
14756 } else {
14757 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14758 if (repeated) {
14759 pm_node_flag_set_repeated_parameter(UP(param));
14760 }
14761 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14762 }
14763
14764 break;
14765 }
14766 case PM_TOKEN_LABEL: {
14767 if (!uses_parentheses && !in_block) parser->in_keyword_arg = true;
14768 update_parameter_state(parser, &parser->current, &order);
14769
14770 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14771 parser_lex(parser);
14772
14773 pm_token_t name = parser->previous;
14774 pm_token_t local = name;
14775 local.end -= 1;
14776
14777 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)) {
14778 pm_parser_err(parser, PM_TOKEN_START(parser, &local), PM_TOKEN_LENGTH(&local), PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14779 } else if (local.end[-1] == '!' || local.end[-1] == '?') {
14780 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE);
14781 }
14782
14783 bool repeated = pm_parser_parameter_name_check(parser, &local);
14784 pm_parser_local_add_token(parser, &local, 1);
14785
14786 switch (parser->current.type) {
14787 case PM_TOKEN_COMMA:
14788 case PM_TOKEN_PARENTHESIS_RIGHT:
14789 case PM_TOKEN_PIPE: {
14790 context_pop(parser);
14791
14792 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14793 if (repeated) {
14794 pm_node_flag_set_repeated_parameter(param);
14795 }
14796
14797 pm_parameters_node_keywords_append(parser->arena, params, param);
14798 break;
14799 }
14800 case PM_TOKEN_SEMICOLON:
14801 case PM_TOKEN_NEWLINE: {
14802 context_pop(parser);
14803
14804 if (uses_parentheses) {
14805 parsing = false;
14806 break;
14807 }
14808
14809 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14810 if (repeated) {
14811 pm_node_flag_set_repeated_parameter(param);
14812 }
14813
14814 pm_parameters_node_keywords_append(parser->arena, params, param);
14815 break;
14816 }
14817 default: {
14818 pm_node_t *param;
14819
14820 if (token_begins_expression_p(parser->current.type)) {
14821 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
14822 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14823
14824 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14825 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));
14826 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14827
14828 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14829 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_PARAMETER_CIRCULAR);
14830 }
14831
14832 param = UP(pm_optional_keyword_parameter_node_create(parser, &name, value));
14833 }
14834 else {
14835 param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14836 }
14837
14838 if (repeated) {
14839 pm_node_flag_set_repeated_parameter(param);
14840 }
14841
14842 context_pop(parser);
14843 pm_parameters_node_keywords_append(parser->arena, params, param);
14844
14845 // If parsing the value of the parameter resulted in error recovery,
14846 // then we can put a missing node in its place and stop parsing the
14847 // parameters entirely now.
14848 if (parser->recovering) {
14849 parsing = false;
14850 break;
14851 }
14852 }
14853 }
14854
14855 parser->in_keyword_arg = false;
14856 break;
14857 }
14858 case PM_TOKEN_USTAR:
14859 case PM_TOKEN_STAR: {
14860 update_parameter_state(parser, &parser->current, &order);
14861 parser_lex(parser);
14862
14863 pm_token_t operator = parser->previous;
14864 pm_token_t name = { 0 };
14865 bool repeated = false;
14866
14867 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14868 name = parser->previous;
14869 repeated = pm_parser_parameter_name_check(parser, &name);
14870 pm_parser_local_add_token(parser, &name, 1);
14871 } else {
14872 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
14873 }
14874
14875 pm_node_t *param = UP(pm_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
14876 if (repeated) {
14877 pm_node_flag_set_repeated_parameter(param);
14878 }
14879
14880 if (params->rest == NULL) {
14881 pm_parameters_node_rest_set(params, param);
14882 } else {
14883 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_SPLAT_MULTI);
14884 pm_parameters_node_posts_append(parser->arena, params, param);
14885 }
14886
14887 break;
14888 }
14889 case PM_TOKEN_STAR_STAR:
14890 case PM_TOKEN_USTAR_STAR: {
14891 pm_parameters_order_t previous_order = order;
14892 update_parameter_state(parser, &parser->current, &order);
14893 parser_lex(parser);
14894
14895 pm_token_t operator = parser->previous;
14896 pm_node_t *param;
14897
14898 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
14899 if (previous_order <= PM_PARAMETERS_ORDER_KEYWORDS) {
14900 pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW);
14901 }
14902
14903 param = UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
14904 } else {
14905 pm_token_t name = { 0 };
14906
14907 bool repeated = false;
14908 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14909 name = parser->previous;
14910 repeated = pm_parser_parameter_name_check(parser, &name);
14911 pm_parser_local_add_token(parser, &name, 1);
14912 } else {
14913 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS;
14914 }
14915
14916 param = UP(pm_keyword_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
14917 if (repeated) {
14918 pm_node_flag_set_repeated_parameter(param);
14919 }
14920 }
14921
14922 if (params->keyword_rest == NULL) {
14923 pm_parameters_node_keyword_rest_set(params, param);
14924 } else {
14925 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI);
14926 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
14927 }
14928
14929 break;
14930 }
14931 default:
14932 if (parser->previous.type == PM_TOKEN_COMMA) {
14933 parse_parameters_handle_trailing_comma(parser, params, order, in_block, allows_trailing_comma);
14934 }
14935
14936 parsing = false;
14937 break;
14938 }
14939
14940 // If we hit some kind of issue while parsing the parameter, this would
14941 // have been set to false. In that case, we need to break out of the
14942 // loop.
14943 if (!parsing) break;
14944
14945 bool accepted_newline = false;
14946 if (uses_parentheses) {
14947 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14948 }
14949
14950 if (accept1(parser, PM_TOKEN_COMMA)) {
14951 // If there was a comma, but we also accepted a newline, then this
14952 // is a syntax error.
14953 if (accepted_newline) {
14954 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14955 }
14956 } else {
14957 // If there was no comma, then we're done parsing parameters.
14958 break;
14959 }
14960 }
14961
14962 pm_do_loop_stack_pop(parser);
14963
14964 // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`.
14965 if (PM_NODE_START(params) == PM_NODE_END(params)) {
14966 return NULL;
14967 }
14968
14969 return params;
14970}
14971
14976static size_t
14977token_newline_index(const pm_parser_t *parser) {
14978 if (parser->heredoc_end == NULL) {
14979 // This is the common case. In this case we can look at the previously
14980 // recorded newline in the newline list and subtract from the current
14981 // offset.
14982 return parser->line_offsets.size - 1;
14983 } else {
14984 // This is unlikely. This is the case that we have already parsed the
14985 // start of a heredoc, so we cannot rely on looking at the previous
14986 // offset of the newline list, and instead must go through the whole
14987 // process of a binary search for the line number.
14988 return (size_t) pm_line_offset_list_line(&parser->line_offsets, PM_TOKEN_START(parser, &parser->current), 0);
14989 }
14990}
14991
14996static int64_t
14997token_column(const pm_parser_t *parser, size_t newline_index, const pm_token_t *token, bool break_on_non_space) {
14998 const uint8_t *cursor = parser->start + parser->line_offsets.offsets[newline_index];
14999 const uint8_t *end = token->start;
15000
15001 // Skip over the BOM if it is present.
15002 if (
15003 newline_index == 0 &&
15004 parser->start[0] == 0xef &&
15005 parser->start[1] == 0xbb &&
15006 parser->start[2] == 0xbf
15007 ) cursor += 3;
15008
15009 int64_t column = 0;
15010 for (; cursor < end; cursor++) {
15011 switch (*cursor) {
15012 case '\t':
15013 column = ((column / PM_TAB_WHITESPACE_SIZE) + 1) * PM_TAB_WHITESPACE_SIZE;
15014 break;
15015 case ' ':
15016 column++;
15017 break;
15018 default:
15019 column++;
15020 if (break_on_non_space) return -1;
15021 break;
15022 }
15023 }
15024
15025 return column;
15026}
15027
15032static void
15033parser_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) {
15034 // If these warnings are disabled (unlikely), then we can just return.
15035 if (!parser->warn_mismatched_indentation) return;
15036
15037 // If the tokens are on the same line, we do not warn.
15038 size_t closing_newline_index = token_newline_index(parser);
15039 if (opening_newline_index == closing_newline_index) return;
15040
15041 // If the opening token has anything other than spaces or tabs before it,
15042 // then we do not warn. This is unless we are matching up an `if`/`end` pair
15043 // and the `if` immediately follows an `else` keyword.
15044 int64_t opening_column = token_column(parser, opening_newline_index, opening_token, !if_after_else);
15045 if (!if_after_else && (opening_column == -1)) return;
15046
15047 // Get a reference to the closing token off the current parser. This assumes
15048 // that the caller has placed this in the correct position.
15049 pm_token_t *closing_token = &parser->current;
15050
15051 // If the tokens are at the same indentation, we do not warn.
15052 int64_t closing_column = token_column(parser, closing_newline_index, closing_token, true);
15053 if ((closing_column == -1) || (opening_column == closing_column)) return;
15054
15055 // If the closing column is greater than the opening column and we are
15056 // allowing indentation, then we do not warn.
15057 if (allow_indent && (closing_column > opening_column)) return;
15058
15059 // Otherwise, add a warning.
15060 PM_PARSER_WARN_FORMAT(
15061 parser,
15062 PM_TOKEN_START(parser, closing_token),
15063 PM_TOKEN_LENGTH(closing_token),
15064 PM_WARN_INDENTATION_MISMATCH,
15065 (int) (closing_token->end - closing_token->start),
15066 (const char *) closing_token->start,
15067 (int) (opening_token->end - opening_token->start),
15068 (const char *) opening_token->start,
15069 ((int32_t) opening_newline_index) + parser->start_line
15070 );
15071}
15072
15073typedef enum {
15074 PM_RESCUES_BEGIN = 1,
15075 PM_RESCUES_BLOCK,
15076 PM_RESCUES_CLASS,
15077 PM_RESCUES_DEF,
15078 PM_RESCUES_LAMBDA,
15079 PM_RESCUES_MODULE,
15080 PM_RESCUES_SCLASS
15081} pm_rescues_type_t;
15082
15087static PRISM_INLINE void
15088parse_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) {
15089 pm_rescue_node_t *current = NULL;
15090
15091 while (match1(parser, PM_TOKEN_KEYWORD_RESCUE)) {
15092 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15093 parser_lex(parser);
15094
15095 pm_rescue_node_t *rescue = pm_rescue_node_create(parser, &parser->previous);
15096
15097 switch (parser->current.type) {
15098 case PM_TOKEN_EQUAL_GREATER: {
15099 // Here we have an immediate => after the rescue keyword, in which case
15100 // we're going to have an empty list of exceptions to rescue (which
15101 // implies StandardError).
15102 parser_lex(parser);
15103 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15104
15105 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15106 reference = parse_target(parser, reference, false, false);
15107
15108 pm_rescue_node_reference_set(rescue, reference);
15109 break;
15110 }
15111 case PM_TOKEN_NEWLINE:
15112 case PM_TOKEN_SEMICOLON:
15113 case PM_TOKEN_KEYWORD_THEN:
15114 // Here we have a terminator for the rescue keyword, in which
15115 // case we're going to just continue on.
15116 break;
15117 default: {
15118 if (token_begins_expression_p(parser->current.type) || match1(parser, PM_TOKEN_USTAR)) {
15119 // Here we have something that could be an exception expression, so
15120 // we'll attempt to parse it here and any others delimited by commas.
15121
15122 do {
15123 pm_node_t *expression = parse_starred_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_RESCUE_EXPRESSION, (uint16_t) (depth + 1));
15124 pm_rescue_node_exceptions_append(parser->arena, rescue, expression);
15125
15126 // If we hit a newline, then this is the end of the rescue expression. We
15127 // can continue on to parse the statements.
15128 if (match3(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_THEN)) break;
15129
15130 // If we hit a `=>` then we're going to parse the exception variable. Once
15131 // we've done that, we'll break out of the loop and parse the statements.
15132 if (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
15133 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15134
15135 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15136 reference = parse_target(parser, reference, false, false);
15137
15138 pm_rescue_node_reference_set(rescue, reference);
15139 break;
15140 }
15141 } while (accept1(parser, PM_TOKEN_COMMA));
15142 }
15143 }
15144 }
15145
15146 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
15147 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
15148 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15149 }
15150 } else {
15151 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM);
15152 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15153 }
15154
15155 if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
15156 pm_accepts_block_stack_push(parser, true);
15157 pm_context_t context;
15158
15159 switch (type) {
15160 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break;
15161 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break;
15162 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break;
15163 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break;
15164 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break;
15165 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break;
15166 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break;
15167 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15168 }
15169
15170 pm_statements_node_t *statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15171 if (statements != NULL) pm_rescue_node_statements_set(rescue, statements);
15172
15173 pm_accepts_block_stack_pop(parser);
15174 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15175 }
15176
15177 if (current == NULL) {
15178 pm_begin_node_rescue_clause_set(parent_node, rescue);
15179 } else {
15180 pm_rescue_node_subsequent_set(current, rescue);
15181 }
15182
15183 current = rescue;
15184 }
15185
15186 // The end node locations on rescue nodes will not be set correctly
15187 // since we won't know the end until we've found all subsequent
15188 // clauses. This sets the end location on all rescues once we know it.
15189 if (current != NULL) {
15190 pm_rescue_node_t *clause = parent_node->rescue_clause;
15191
15192 while (clause != NULL) {
15193 PM_NODE_LENGTH_SET_NODE(clause, current);
15194 clause = clause->subsequent;
15195 }
15196 }
15197
15198 pm_token_t else_keyword;
15199 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
15200 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15201 opening_newline_index = token_newline_index(parser);
15202
15203 else_keyword = parser->current;
15204 opening = &else_keyword;
15205
15206 parser_lex(parser);
15207 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15208
15209 pm_statements_node_t *else_statements = NULL;
15210 if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) {
15211 pm_accepts_block_stack_push(parser, true);
15212 pm_context_t context;
15213
15214 switch (type) {
15215 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break;
15216 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break;
15217 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break;
15218 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break;
15219 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break;
15220 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break;
15221 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break;
15222 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_ELSE; break;
15223 }
15224
15225 else_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15226 pm_accepts_block_stack_pop(parser);
15227
15228 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15229 }
15230
15231 pm_else_node_t *else_clause = pm_else_node_create(parser, &else_keyword, else_statements, &parser->current);
15232 pm_begin_node_else_clause_set(parent_node, else_clause);
15233
15234 // If we don't have a `current` rescue node, then this is a dangling
15235 // else, and it's an error.
15236 if (current == NULL) pm_parser_err_node(parser, UP(else_clause), PM_ERR_BEGIN_LONELY_ELSE);
15237 }
15238
15239 if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) {
15240 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15241 pm_token_t ensure_keyword = parser->current;
15242
15243 parser_lex(parser);
15244 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15245
15246 pm_statements_node_t *ensure_statements = NULL;
15247 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15248 pm_accepts_block_stack_push(parser, true);
15249 pm_context_t context;
15250
15251 switch (type) {
15252 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break;
15253 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break;
15254 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break;
15255 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break;
15256 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break;
15257 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break;
15258 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break;
15259 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15260 }
15261
15262 ensure_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15263 pm_accepts_block_stack_pop(parser);
15264
15265 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15266 }
15267
15268 pm_ensure_node_t *ensure_clause = pm_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current);
15269 pm_begin_node_ensure_clause_set(parent_node, ensure_clause);
15270 }
15271
15272 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
15273 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15274 pm_begin_node_end_keyword_set(parser, parent_node, &parser->current);
15275 } else {
15276 pm_token_t end_keyword = (pm_token_t) { .type = PM_TOKEN_KEYWORD_END, .start = parser->previous.end, .end = parser->previous.end };
15277 pm_begin_node_end_keyword_set(parser, parent_node, &end_keyword);
15278 }
15279}
15280
15285static pm_begin_node_t *
15286parse_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) {
15287 pm_begin_node_t *node = pm_begin_node_create(parser, NULL, statements);
15288 parse_rescues(parser, opening_newline_index, opening, node, type, (uint16_t) (depth + 1));
15289
15290 node->base.location.start = U32(start - parser->start);
15291 PM_NODE_LENGTH_SET_TOKEN(parser, node, &parser->current);
15292
15293 return node;
15294}
15295
15300parse_block_parameters(
15301 pm_parser_t *parser,
15302 bool allows_trailing_comma,
15303 const pm_token_t *opening,
15304 bool is_lambda_literal,
15305 bool accepts_blocks_in_defaults,
15306 uint16_t depth
15307) {
15308 pm_parameters_node_t *parameters = NULL;
15309 if (!match1(parser, PM_TOKEN_SEMICOLON)) {
15310 if (!is_lambda_literal) {
15311 context_push(parser, PM_CONTEXT_BLOCK_PARAMETERS);
15312 }
15313 parameters = parse_parameters(
15314 parser,
15315 is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX,
15316 false,
15317 allows_trailing_comma,
15318 false,
15319 accepts_blocks_in_defaults,
15320 true,
15321 is_lambda_literal ? PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA : PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK,
15322 (uint16_t) (depth + 1)
15323 );
15324 if (!is_lambda_literal) {
15325 context_pop(parser);
15326 }
15327 }
15328
15329 pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening);
15330 if (opening != NULL) {
15331 accept1(parser, PM_TOKEN_NEWLINE);
15332
15333 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
15334 do {
15335 switch (parser->current.type) {
15336 case PM_TOKEN_CONSTANT:
15337 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
15338 parser_lex(parser);
15339 break;
15340 case PM_TOKEN_INSTANCE_VARIABLE:
15341 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
15342 parser_lex(parser);
15343 break;
15344 case PM_TOKEN_GLOBAL_VARIABLE:
15345 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
15346 parser_lex(parser);
15347 break;
15348 case PM_TOKEN_CLASS_VARIABLE:
15349 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
15350 parser_lex(parser);
15351 break;
15352 default:
15353 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
15354 break;
15355 }
15356
15357 bool repeated = pm_parser_parameter_name_check(parser, &parser->previous);
15358 pm_parser_local_add_token(parser, &parser->previous, 1);
15359
15360 pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous);
15361 if (repeated) pm_node_flag_set_repeated_parameter(UP(local));
15362
15363 pm_block_parameters_node_append_local(parser->arena, block_parameters, local);
15364 } while (accept1(parser, PM_TOKEN_COMMA));
15365 }
15366 }
15367
15368 return block_parameters;
15369}
15370
15375static bool
15376outer_scope_using_numbered_parameters_p(pm_parser_t *parser) {
15377 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15378 if (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) return true;
15379 }
15380
15381 return false;
15382}
15383
15389static const char * const pm_numbered_parameter_names[] = {
15390 "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9"
15391};
15392
15398static pm_node_t *
15399parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_token_t *opening, const pm_token_t *closing) {
15400 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
15401
15402 // If we have ordinary parameters, then we will return them as the set of
15403 // parameters.
15404 if (parameters != NULL) {
15405 // If we also have implicit parameters, then this is an error.
15406 if (implicit_parameters->size > 0) {
15407 pm_node_t *node = implicit_parameters->nodes[0];
15408
15409 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15410 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_ORDINARY);
15411 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15412 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_ORDINARY);
15413 } else {
15414 assert(false && "unreachable");
15415 }
15416 }
15417
15418 return parameters;
15419 }
15420
15421 // If we don't have any implicit parameters, then the set of parameters is
15422 // NULL.
15423 if (implicit_parameters->size == 0) {
15424 return NULL;
15425 }
15426
15427 // If we don't have ordinary parameters, then we now must validate our set
15428 // of implicit parameters. We can only have numbered parameters or it, but
15429 // they cannot be mixed.
15430 uint8_t numbered_parameter = 0;
15431 bool it_parameter = false;
15432
15433 for (size_t index = 0; index < implicit_parameters->size; index++) {
15434 pm_node_t *node = implicit_parameters->nodes[index];
15435
15436 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15437 if (it_parameter) {
15438 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_IT);
15439 } else if (outer_scope_using_numbered_parameters_p(parser)) {
15440 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK);
15441 } else if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_INNER) {
15442 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK);
15443 } else if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
15444 numbered_parameter = MAX(numbered_parameter, (uint8_t) (parser->start[node->location.start + 1] - '0'));
15445 } else {
15446 assert(false && "unreachable");
15447 }
15448 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15449 if (numbered_parameter > 0) {
15450 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_NUMBERED);
15451 } else {
15452 it_parameter = true;
15453 }
15454 }
15455 }
15456
15457 if (numbered_parameter > 0) {
15458 // Go through the parent scopes and mark them as being disallowed from
15459 // using numbered parameters because this inner scope is using them.
15460 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15461 scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_INNER;
15462 }
15463 return UP(pm_numbered_parameters_node_create(parser, opening, closing, numbered_parameter));
15464 }
15465
15466 if (it_parameter) {
15467 return UP(pm_it_parameters_node_create(parser, opening, closing));
15468 }
15469
15470 return NULL;
15471}
15472
15476static pm_block_node_t *
15477parse_block(pm_parser_t *parser, uint16_t depth) {
15478 pm_token_t opening = parser->previous;
15479 accept1(parser, PM_TOKEN_NEWLINE);
15480
15481 /* A brace block is delimited by `{`/`}`, whose block-accepting frame is
15482 * managed by the lexer. A `do`/`end` block is delimited by keywords, so we
15483 * push the frame here (covering the block parameters and body) and pop it
15484 * before consuming `end`, mirroring parse.y's `do_body` rule. */
15485 bool do_block = opening.type != PM_TOKEN_BRACE_LEFT && opening.type != PM_TOKEN_BRACE_LEFT_ARGUMENT;
15486 if (do_block) pm_accepts_block_stack_push(parser, true);
15487 pm_parser_scope_push(parser, false);
15488
15489 pm_block_parameters_node_t *block_parameters = NULL;
15490
15491 if (accept1(parser, PM_TOKEN_PIPE)) {
15492 pm_token_t block_parameters_opening = parser->previous;
15493 if (match1(parser, PM_TOKEN_PIPE)) {
15494 block_parameters = pm_block_parameters_node_create(parser, NULL, &block_parameters_opening);
15495 parser->command_start = true;
15496 parser_lex(parser);
15497 } else {
15498 block_parameters = parse_block_parameters(parser, true, &block_parameters_opening, false, true, (uint16_t) (depth + 1));
15499 accept1(parser, PM_TOKEN_NEWLINE);
15500 parser->command_start = true;
15501 expect1(parser, PM_TOKEN_PIPE, PM_ERR_BLOCK_PARAM_PIPE_TERM);
15502 }
15503
15504 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
15505 }
15506
15507 accept1(parser, PM_TOKEN_NEWLINE);
15508 pm_node_t *statements = NULL;
15509
15510 if (!do_block) {
15511 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
15512 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1)));
15513 }
15514
15515 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening);
15516 } else {
15517 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15518 if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) {
15519 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS, (uint16_t) (depth + 1)));
15520 }
15521
15522 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
15523 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
15524 statements = UP(parse_rescues_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1)));
15525 }
15526 }
15527
15528 /* Pop the `do`/`end` frame before consuming `end` so the token
15529 * following the block is lexed in the enclosing context. */
15530 pm_accepts_block_stack_pop(parser);
15531 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening);
15532 }
15533
15534 pm_constant_id_list_t locals;
15535 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
15536 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &opening, &parser->previous);
15537
15538 pm_parser_scope_pop(parser);
15539 return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous);
15540}
15541
15553static bool
15554parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_arguments, uint8_t flags, uint16_t depth) {
15555 /* Fast path: if the current token can't begin an expression and isn't
15556 * a parenthesis, block opener, or splat/block-pass operator, there are
15557 * no arguments to parse. */
15558 if (
15559 !token_begins_expression_p(parser->current.type) &&
15560 !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)
15561 ) {
15562 return false;
15563 }
15564
15565 bool found = false;
15566 bool parsed_command_args = false;
15567
15568 if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
15569 found |= true;
15570 arguments->opening_loc = TOK2LOC(parser, &parser->previous);
15571
15572 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15573 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15574 } else {
15575 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_PARENTHESIS_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
15576
15577 // `yield` parses its arguments through the restricted `call_args`
15578 // grammar, which (unlike the `opt_call_args` that method calls and
15579 // `super` use) permits neither a block argument nor a trailing
15580 // comma. `full_arguments` is false only for `yield`, so we use it
15581 // to reject the trailing comma in `yield(a,)` that the arguments
15582 // parser otherwise accepts before the closing parenthesis.
15583 if (!full_arguments && parser->previous.type == PM_TOKEN_COMMA) {
15584 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15585 }
15586
15587 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15588 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_str(parser->current.type));
15589 parser->previous.start = parser->previous.end;
15590 parser->previous.type = 0;
15591 }
15592
15593 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15594 }
15595 } 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)) {
15596 found |= true;
15597 parsed_command_args = true;
15598
15599 /* The command-args frame does not accept blocks, so that a trailing
15600 * `do` binds to this command rather than to an argument. Mirroring
15601 * parse.y's `command_args` rule: when the first argument begins with an
15602 * opening delimiter, the lexer has already pushed that delimiter's
15603 * (block-accepting) frame. We must push the command-args frame beneath
15604 * it, so pop the delimiter frame, push the command-args frame, and then
15605 * restore the delimiter frame on top (the delimiter's closing token
15606 * will pop it back off during argument parsing). */
15607 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);
15608 if (lookahead_delimiter) pm_accepts_block_stack_pop(parser);
15609 pm_accepts_block_stack_push(parser, false);
15610 if (lookahead_delimiter) pm_accepts_block_stack_push(parser, true);
15611
15612 // If we get here, then the subsequent token cannot be used as an infix
15613 // operator. In this case we assume the subsequent token is part of an
15614 // argument to this method call.
15615 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
15616
15617 // If we have done with the arguments and still not consumed the comma,
15618 // then we have a trailing comma where we need to check whether it is
15619 // allowed or not.
15620 if (parser->previous.type == PM_TOKEN_COMMA && !match1(parser, PM_TOKEN_SEMICOLON)) {
15621 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15622 }
15623
15624 /* Symmetrically, if the command arguments are followed by a brace block
15625 * (`m args { }`), the lexer has already pushed that block's frame. Pop
15626 * it, pop the command-args frame beneath it, and restore the block
15627 * frame so the block's `}` still pops it. This mirrors the `tLBRACE_ARG`
15628 * lookahead handling in parse.y's `command_args` rule. */
15629 bool lookahead_brace = match2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT);
15630 if (lookahead_brace) pm_accepts_block_stack_pop(parser);
15631 pm_accepts_block_stack_pop(parser);
15632 if (lookahead_brace) pm_accepts_block_stack_push(parser, true);
15633 }
15634
15635 // If we're at the end of the arguments, we can now check if there is a block
15636 // node that starts with a {. If there is, then we can parse it and add it to
15637 // the arguments.
15638 if (full_arguments) {
15639 pm_block_node_t *block = NULL;
15640
15641 if (accept2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT)) {
15642 found |= true;
15643 block = parse_block(parser, (uint16_t) (depth + 1));
15644 pm_arguments_validate_block(parser, arguments, block);
15645 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
15646 found |= true;
15647 block = parse_block(parser, (uint16_t) (depth + 1));
15648 } else if (parsed_command_args && pm_accepts_block_stack_p(parser) && (flags & PM_PARSE_ACCEPTS_DO_BLOCK) && accept1(parser, PM_TOKEN_KEYWORD_DO_BLOCK)) {
15649 found |= true;
15650 block = parse_block(parser, (uint16_t) (depth + 1));
15651 }
15652
15653 if (block != NULL) {
15654 if (arguments->block == NULL && !arguments->has_forwarding) {
15655 arguments->block = UP(block);
15656 } else {
15657 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_BLOCK_MULTI);
15658
15659 if (arguments->block != NULL) {
15660 if (arguments->arguments == NULL) {
15661 arguments->arguments = pm_arguments_node_create(parser);
15662 }
15663 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, arguments->block);
15664 }
15665 arguments->block = UP(block);
15666 }
15667 }
15668 }
15669
15670 return found;
15671}
15672
15677static void
15678parse_return(pm_parser_t *parser, pm_node_t *node) {
15679 bool in_sclass = false;
15680 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15681 switch (context_node->context) {
15682 case PM_CONTEXT_BEGIN_ELSE:
15683 case PM_CONTEXT_BEGIN_ENSURE:
15684 case PM_CONTEXT_BEGIN_RESCUE:
15685 case PM_CONTEXT_BEGIN:
15686 case PM_CONTEXT_CASE_IN:
15687 case PM_CONTEXT_CASE_WHEN:
15688 case PM_CONTEXT_DEFAULT_PARAMS:
15689 case PM_CONTEXT_DEFINED:
15690 case PM_CONTEXT_ELSE:
15691 case PM_CONTEXT_ELSIF:
15692 case PM_CONTEXT_EMBEXPR:
15693 case PM_CONTEXT_FOR_INDEX:
15694 case PM_CONTEXT_FOR:
15695 case PM_CONTEXT_IF:
15696 case PM_CONTEXT_LOOP_PREDICATE:
15697 case PM_CONTEXT_MAIN:
15698 case PM_CONTEXT_MULTI_TARGET:
15699 case PM_CONTEXT_PARENS:
15700 case PM_CONTEXT_POSTEXE:
15701 case PM_CONTEXT_PREDICATE:
15702 case PM_CONTEXT_PREEXE:
15703 case PM_CONTEXT_RESCUE_MODIFIER:
15704 case PM_CONTEXT_TERNARY:
15705 case PM_CONTEXT_UNLESS:
15706 case PM_CONTEXT_UNTIL:
15707 case PM_CONTEXT_WHILE:
15708 // Keep iterating up the lists of contexts, because returns can
15709 // see through these.
15710 continue;
15711 case PM_CONTEXT_SCLASS_ELSE:
15712 case PM_CONTEXT_SCLASS_ENSURE:
15713 case PM_CONTEXT_SCLASS_RESCUE:
15714 case PM_CONTEXT_SCLASS:
15715 in_sclass = true;
15716 continue;
15717 case PM_CONTEXT_CLASS_ELSE:
15718 case PM_CONTEXT_CLASS_ENSURE:
15719 case PM_CONTEXT_CLASS_RESCUE:
15720 case PM_CONTEXT_CLASS:
15721 case PM_CONTEXT_MODULE_ELSE:
15722 case PM_CONTEXT_MODULE_ENSURE:
15723 case PM_CONTEXT_MODULE_RESCUE:
15724 case PM_CONTEXT_MODULE:
15725 // These contexts are invalid for a return.
15726 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15727 return;
15728 case PM_CONTEXT_BLOCK_BRACES:
15729 case PM_CONTEXT_BLOCK_ELSE:
15730 case PM_CONTEXT_BLOCK_ENSURE:
15731 case PM_CONTEXT_BLOCK_KEYWORDS:
15732 case PM_CONTEXT_BLOCK_RESCUE:
15733 case PM_CONTEXT_BLOCK_PARAMETERS:
15734 case PM_CONTEXT_DEF_ELSE:
15735 case PM_CONTEXT_DEF_ENSURE:
15736 case PM_CONTEXT_DEF_PARAMS:
15737 case PM_CONTEXT_DEF_RESCUE:
15738 case PM_CONTEXT_DEF:
15739 case PM_CONTEXT_LAMBDA_BRACES:
15740 case PM_CONTEXT_LAMBDA_DO_END:
15741 case PM_CONTEXT_LAMBDA_ELSE:
15742 case PM_CONTEXT_LAMBDA_ENSURE:
15743 case PM_CONTEXT_LAMBDA_RESCUE:
15744 // These contexts are valid for a return, and we should not
15745 // continue to loop.
15746 return;
15747 case PM_CONTEXT_NONE:
15748 // This case should never happen.
15749 assert(false && "unreachable");
15750 break;
15751 }
15752 }
15753 if (in_sclass && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
15754 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15755 }
15756}
15757
15762static void
15763parse_block_exit(pm_parser_t *parser, pm_node_t *node) {
15764 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15765 switch (context_node->context) {
15766 case PM_CONTEXT_BLOCK_BRACES:
15767 case PM_CONTEXT_BLOCK_KEYWORDS:
15768 case PM_CONTEXT_BLOCK_ELSE:
15769 case PM_CONTEXT_BLOCK_ENSURE:
15770 case PM_CONTEXT_BLOCK_PARAMETERS:
15771 case PM_CONTEXT_BLOCK_RESCUE:
15772 case PM_CONTEXT_DEFINED:
15773 case PM_CONTEXT_FOR:
15774 case PM_CONTEXT_LAMBDA_BRACES:
15775 case PM_CONTEXT_LAMBDA_DO_END:
15776 case PM_CONTEXT_LAMBDA_ELSE:
15777 case PM_CONTEXT_LAMBDA_ENSURE:
15778 case PM_CONTEXT_LAMBDA_RESCUE:
15779 case PM_CONTEXT_LOOP_PREDICATE:
15780 case PM_CONTEXT_UNTIL:
15781 case PM_CONTEXT_WHILE:
15782 // These are the good cases. We're allowed to have a block exit
15783 // in these contexts.
15784 return;
15785 case PM_CONTEXT_POSTEXE:
15786 // https://bugs.ruby-lang.org/issues/20409
15787 if (context_node->context == PM_CONTEXT_POSTEXE) {
15788 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
15789 return;
15790 }
15791 }
15793 case PM_CONTEXT_DEF:
15794 case PM_CONTEXT_DEF_PARAMS:
15795 case PM_CONTEXT_DEF_ELSE:
15796 case PM_CONTEXT_DEF_ENSURE:
15797 case PM_CONTEXT_DEF_RESCUE:
15798 case PM_CONTEXT_MAIN:
15799 case PM_CONTEXT_PREEXE:
15800 case PM_CONTEXT_SCLASS:
15801 case PM_CONTEXT_SCLASS_ELSE:
15802 case PM_CONTEXT_SCLASS_ENSURE:
15803 case PM_CONTEXT_SCLASS_RESCUE:
15804 // These are the bad cases. We're not allowed to have a block
15805 // exit in these contexts.
15806 //
15807 // If we get here, then we're about to mark this block exit
15808 // as invalid. However, it could later _become_ valid if we
15809 // find a trailing while/until on the expression. In this
15810 // case instead of adding the error here, we'll add the
15811 // block exit to the list of exits for the expression, and
15812 // the node parsing will handle validating it instead.
15813 assert(parser->current_block_exits != NULL);
15814 pm_node_list_append(parser->arena, parser->current_block_exits, node);
15815 return;
15816 case PM_CONTEXT_BEGIN_ELSE:
15817 case PM_CONTEXT_BEGIN_ENSURE:
15818 case PM_CONTEXT_BEGIN_RESCUE:
15819 case PM_CONTEXT_BEGIN:
15820 case PM_CONTEXT_CASE_IN:
15821 case PM_CONTEXT_CASE_WHEN:
15822 case PM_CONTEXT_CLASS_ELSE:
15823 case PM_CONTEXT_CLASS_ENSURE:
15824 case PM_CONTEXT_CLASS_RESCUE:
15825 case PM_CONTEXT_CLASS:
15826 case PM_CONTEXT_DEFAULT_PARAMS:
15827 case PM_CONTEXT_ELSE:
15828 case PM_CONTEXT_ELSIF:
15829 case PM_CONTEXT_EMBEXPR:
15830 case PM_CONTEXT_FOR_INDEX:
15831 case PM_CONTEXT_IF:
15832 case PM_CONTEXT_MODULE_ELSE:
15833 case PM_CONTEXT_MODULE_ENSURE:
15834 case PM_CONTEXT_MODULE_RESCUE:
15835 case PM_CONTEXT_MODULE:
15836 case PM_CONTEXT_MULTI_TARGET:
15837 case PM_CONTEXT_PARENS:
15838 case PM_CONTEXT_PREDICATE:
15839 case PM_CONTEXT_RESCUE_MODIFIER:
15840 case PM_CONTEXT_TERNARY:
15841 case PM_CONTEXT_UNLESS:
15842 // In these contexts we should continue walking up the list of
15843 // contexts.
15844 break;
15845 case PM_CONTEXT_NONE:
15846 // This case should never happen.
15847 assert(false && "unreachable");
15848 break;
15849 }
15850 }
15851}
15852
15857static pm_node_list_t *
15858push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
15859 pm_node_list_t *previous_block_exits = parser->current_block_exits;
15860 parser->current_block_exits = current_block_exits;
15861 return previous_block_exits;
15862}
15863
15869static void
15870flush_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
15871 pm_node_t *block_exit;
15872 PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
15873 const char *type;
15874
15875 switch (PM_NODE_TYPE(block_exit)) {
15876 case PM_BREAK_NODE: type = "break"; break;
15877 case PM_NEXT_NODE: type = "next"; break;
15878 case PM_REDO_NODE: type = "redo"; break;
15879 default: assert(false && "unreachable"); type = ""; break;
15880 }
15881
15882 PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
15883 }
15884
15885 parser->current_block_exits = previous_block_exits;
15886}
15887
15892static void
15893pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
15894 if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) {
15895 // If we matched a trailing while/until, then all of the block exits in
15896 // the contained list are valid. In this case we do not need to do
15897 // anything.
15898 parser->current_block_exits = previous_block_exits;
15899 } else if (previous_block_exits != NULL) {
15900 // If we did not matching a trailing while/until, then all of the block
15901 // exits contained in the list are invalid for this specific context.
15902 // However, they could still become valid in a higher level context if
15903 // there is another list above this one. In this case we'll push all of
15904 // the block exits up to the previous list.
15905 pm_node_list_concat(parser->arena, previous_block_exits, parser->current_block_exits);
15906 parser->current_block_exits = previous_block_exits;
15907 } else {
15908 // If we did not match a trailing while/until and this was the last
15909 // chance to do so, then all of the block exits in the list are invalid
15910 // and we need to add an error for each of them.
15911 flush_block_exits(parser, previous_block_exits);
15912 }
15913}
15914
15915static PRISM_INLINE pm_node_t *
15916parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword, uint16_t depth) {
15917 context_push(parser, PM_CONTEXT_PREDICATE);
15918 pm_diagnostic_id_t error_id = context == PM_CONTEXT_IF ? PM_ERR_CONDITIONAL_IF_PREDICATE : PM_ERR_CONDITIONAL_UNLESS_PREDICATE;
15919 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));
15920
15921 // Predicates are closed by a term, a "then", or a term and then a "then".
15922 bool predicate_closed = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15923
15924 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
15925 predicate_closed = true;
15926 *then_keyword = parser->previous;
15927 }
15928
15929 if (!predicate_closed) {
15930 pm_parser_err_current(parser, PM_ERR_CONDITIONAL_PREDICATE_TERM);
15931 }
15932
15933 context_pop(parser);
15934 return predicate;
15935}
15936
15937static PRISM_INLINE pm_node_t *
15938parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newline_index, bool if_after_else, uint16_t depth) {
15939 pm_node_list_t current_block_exits = { 0 };
15940 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
15941
15942 pm_token_t keyword = parser->previous;
15943 pm_token_t then_keyword = { 0 };
15944
15945 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, context, &then_keyword, (uint16_t) (depth + 1));
15946 pm_statements_node_t *statements = NULL;
15947
15948 if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
15949 pm_accepts_block_stack_push(parser, true);
15950 statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15951 pm_accepts_block_stack_pop(parser);
15952 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15953 }
15954
15955 pm_node_t *parent = NULL;
15956
15957 switch (context) {
15958 case PM_CONTEXT_IF:
15959 parent = UP(pm_if_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
15960 break;
15961 case PM_CONTEXT_UNLESS:
15962 parent = UP(pm_unless_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements));
15963 break;
15964 default:
15965 assert(false && "unreachable");
15966 break;
15967 }
15968
15969 pm_node_t *current = parent;
15970
15971 // Parse any number of elsif clauses. This will form a linked list of if
15972 // nodes pointing to each other from the top.
15973 if (context == PM_CONTEXT_IF) {
15974 while (match1(parser, PM_TOKEN_KEYWORD_ELSIF)) {
15975 if (parser_end_of_line_p(parser)) {
15976 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
15977 }
15978
15979 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
15980 pm_token_t elsif_keyword = parser->current;
15981 parser_lex(parser);
15982
15983 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1));
15984 pm_accepts_block_stack_push(parser, true);
15985
15986 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1));
15987 pm_accepts_block_stack_pop(parser);
15988 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15989
15990 pm_node_t *elsif = UP(pm_if_node_create(parser, &elsif_keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
15991 ((pm_if_node_t *) current)->subsequent = elsif;
15992 current = elsif;
15993 }
15994 }
15995
15996 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
15997 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
15998 opening_newline_index = token_newline_index(parser);
15999
16000 parser_lex(parser);
16001 pm_token_t else_keyword = parser->previous;
16002
16003 pm_accepts_block_stack_push(parser, true);
16004 pm_statements_node_t *else_statements = parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1));
16005 pm_accepts_block_stack_pop(parser);
16006
16007 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16008 parser_warn_indentation_mismatch(parser, opening_newline_index, &else_keyword, false, false);
16009 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE, &keyword);
16010
16011 pm_else_node_t *else_node = pm_else_node_create(parser, &else_keyword, else_statements, &parser->previous);
16012
16013 switch (context) {
16014 case PM_CONTEXT_IF:
16015 ((pm_if_node_t *) current)->subsequent = UP(else_node);
16016 break;
16017 case PM_CONTEXT_UNLESS:
16018 ((pm_unless_node_t *) parent)->else_clause = else_node;
16019 break;
16020 default:
16021 assert(false && "unreachable");
16022 break;
16023 }
16024 } else {
16025 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false);
16026 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM, &keyword);
16027 }
16028
16029 // Set the appropriate end location for all of the nodes in the subtree.
16030 switch (context) {
16031 case PM_CONTEXT_IF: {
16032 pm_node_t *current = parent;
16033 bool recursing = true;
16034
16035 while (recursing) {
16036 switch (PM_NODE_TYPE(current)) {
16037 case PM_IF_NODE:
16038 pm_if_node_end_keyword_loc_set(parser, (pm_if_node_t *) current, &parser->previous);
16039 current = ((pm_if_node_t *) current)->subsequent;
16040 recursing = current != NULL;
16041 break;
16042 case PM_ELSE_NODE:
16043 pm_else_node_end_keyword_loc_set(parser, (pm_else_node_t *) current, &parser->previous);
16044 recursing = false;
16045 break;
16046 default: {
16047 recursing = false;
16048 break;
16049 }
16050 }
16051 }
16052 break;
16053 }
16054 case PM_CONTEXT_UNLESS:
16055 pm_unless_node_end_keyword_loc_set(parser, (pm_unless_node_t *) parent, &parser->previous);
16056 break;
16057 default:
16058 assert(false && "unreachable");
16059 break;
16060 }
16061
16062 pop_block_exits(parser, previous_block_exits);
16063 return parent;
16064}
16065
16070#define PM_CASE_KEYWORD PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16071 case PM_TOKEN_KEYWORD_ALIAS: case PM_TOKEN_KEYWORD_AND: case PM_TOKEN_KEYWORD_BEGIN: case PM_TOKEN_KEYWORD_BEGIN_UPCASE: \
16072 case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_CASE: case PM_TOKEN_KEYWORD_CLASS: case PM_TOKEN_KEYWORD_DEF: \
16073 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: \
16074 case PM_TOKEN_KEYWORD_ELSIF: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_END_UPCASE: case PM_TOKEN_KEYWORD_ENSURE: \
16075 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD_FOR: case PM_TOKEN_KEYWORD_IF: case PM_TOKEN_KEYWORD_IN: \
16076 case PM_TOKEN_KEYWORD_MODULE: case PM_TOKEN_KEYWORD_NEXT: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_NOT: \
16077 case PM_TOKEN_KEYWORD_OR: case PM_TOKEN_KEYWORD_REDO: case PM_TOKEN_KEYWORD_RESCUE: case PM_TOKEN_KEYWORD_RETRY: \
16078 case PM_TOKEN_KEYWORD_RETURN: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_SUPER: case PM_TOKEN_KEYWORD_THEN: \
16079 case PM_TOKEN_KEYWORD_TRUE: case PM_TOKEN_KEYWORD_UNDEF: case PM_TOKEN_KEYWORD_UNLESS: case PM_TOKEN_KEYWORD_UNTIL: \
16080 case PM_TOKEN_KEYWORD_WHEN: case PM_TOKEN_KEYWORD_WHILE: case PM_TOKEN_KEYWORD_YIELD
16081
16086#define PM_CASE_OPERATOR PM_TOKEN_AMPERSAND: case PM_TOKEN_BACKTICK: case PM_TOKEN_BANG_EQUAL: \
16087 case PM_TOKEN_BANG_TILDE: case PM_TOKEN_BANG: case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \
16088 case PM_TOKEN_BRACKET_LEFT_RIGHT: case PM_TOKEN_CARET: case PM_TOKEN_EQUAL_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL: \
16089 case PM_TOKEN_EQUAL_TILDE: case PM_TOKEN_GREATER_EQUAL: case PM_TOKEN_GREATER_GREATER: case PM_TOKEN_GREATER: \
16090 case PM_TOKEN_LESS_EQUAL_GREATER: case PM_TOKEN_LESS_EQUAL: case PM_TOKEN_LESS_LESS: case PM_TOKEN_LESS: \
16091 case PM_TOKEN_MINUS: case PM_TOKEN_PERCENT: case PM_TOKEN_PIPE: case PM_TOKEN_PLUS: case PM_TOKEN_SLASH: \
16092 case PM_TOKEN_STAR_STAR: case PM_TOKEN_STAR: case PM_TOKEN_TILDE: case PM_TOKEN_UAMPERSAND: case PM_TOKEN_UMINUS: \
16093 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_UPLUS: case PM_TOKEN_USTAR: case PM_TOKEN_USTAR_STAR
16094
16100#define PM_CASE_PRIMITIVE PM_TOKEN_INTEGER: case PM_TOKEN_INTEGER_IMAGINARY: case PM_TOKEN_INTEGER_RATIONAL: \
16101 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: case PM_TOKEN_FLOAT: case PM_TOKEN_FLOAT_IMAGINARY: \
16102 case PM_TOKEN_FLOAT_RATIONAL: case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: case PM_TOKEN_SYMBOL_BEGIN: \
16103 case PM_TOKEN_REGEXP_BEGIN: case PM_TOKEN_XSTRING_BEGIN: case PM_TOKEN_PERCENT_LOWER_X: case PM_TOKEN_PERCENT_LOWER_I: \
16104 case PM_TOKEN_PERCENT_LOWER_W: case PM_TOKEN_PERCENT_UPPER_I: case PM_TOKEN_PERCENT_UPPER_W: \
16105 case PM_TOKEN_STRING_BEGIN: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_TRUE: \
16106 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16107 case PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_MINUS_GREATER: case PM_TOKEN_HEREDOC_START: \
16108 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_CHARACTER_LITERAL
16109
16114#define PM_CASE_PARAMETER PM_TOKEN_UAMPERSAND: case PM_TOKEN_AMPERSAND: case PM_TOKEN_UDOT_DOT_DOT: \
16115 case PM_TOKEN_IDENTIFIER: case PM_TOKEN_LABEL: case PM_TOKEN_USTAR: case PM_TOKEN_STAR: case PM_TOKEN_STAR_STAR: \
16116 case PM_TOKEN_USTAR_STAR: case PM_TOKEN_CONSTANT: case PM_TOKEN_INSTANCE_VARIABLE: case PM_TOKEN_GLOBAL_VARIABLE: \
16117 case PM_TOKEN_CLASS_VARIABLE
16118
16123#define PM_CASE_WRITABLE PM_CLASS_VARIABLE_READ_NODE: case PM_CONSTANT_PATH_NODE: \
16124 case PM_CONSTANT_READ_NODE: case PM_GLOBAL_VARIABLE_READ_NODE: case PM_LOCAL_VARIABLE_READ_NODE: \
16125 case PM_INSTANCE_VARIABLE_READ_NODE: case PM_MULTI_TARGET_NODE: case PM_BACK_REFERENCE_READ_NODE: \
16126 case PM_NUMBERED_REFERENCE_READ_NODE: case PM_IT_LOCAL_VARIABLE_READ_NODE
16127
16128// Assert here that the flags are the same so that we can safely switch the type
16129// of the node without having to move the flags.
16130PM_STATIC_ASSERT(__LINE__, ((int) PM_STRING_FLAGS_FORCED_UTF8_ENCODING) == ((int) PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING), "Expected the flags to match.");
16131
16136static PRISM_INLINE pm_node_flags_t
16137parse_unescaped_encoding(const pm_parser_t *parser, const pm_encoding_t *explicit_encoding) {
16138 if (explicit_encoding != NULL) {
16139 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
16140 // If the there's an explicit encoding and it's using a UTF-8 escape
16141 // sequence, then mark the string as UTF-8.
16142 return PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
16143 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
16144 // If there's a non-UTF-8 escape sequence being used, then the
16145 // string uses the source encoding, unless the source is marked as
16146 // US-ASCII. In that case the string is forced as ASCII-8BIT in
16147 // order to keep the string valid.
16148 return PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
16149 }
16150 }
16151 return 0;
16152}
16153
16158static pm_node_t *
16159parse_string_part(pm_parser_t *parser, uint16_t depth) {
16160 switch (parser->current.type) {
16161 // Here the lexer has returned to us plain string content. In this case
16162 // we'll create a string node that has no opening or closing and return that
16163 // as the part. These kinds of parts look like:
16164 //
16165 // "aaa #{bbb} #@ccc ddd"
16166 // ^^^^ ^ ^^^^
16167 case PM_TOKEN_STRING_CONTENT: {
16168 pm_node_t *node = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16169 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
16170
16171 parser_lex(parser);
16172 return node;
16173 }
16174 // Here the lexer has returned the beginning of an embedded expression. In
16175 // that case we'll parse the inner statements and return that as the part.
16176 // These kinds of parts look like:
16177 //
16178 // "aaa #{bbb} #@ccc ddd"
16179 // ^^^^^^
16180 case PM_TOKEN_EMBEXPR_BEGIN: {
16181 // Ruby disallows seeing encoding around interpolation in strings,
16182 // even though it is known at parse time.
16183 parser->explicit_encoding = NULL;
16184
16185 pm_lex_state_t state = parser->lex_state;
16186 int brace_nesting = parser->brace_nesting;
16187
16188 parser->brace_nesting = 0;
16189 lex_state_set(parser, PM_LEX_STATE_BEG);
16190 parser_lex(parser);
16191
16192 pm_token_t opening = parser->previous;
16193 pm_statements_node_t *statements = NULL;
16194
16195 if (!match3(parser, PM_TOKEN_EMBEXPR_END, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
16196 statements = parse_statements(parser, PM_CONTEXT_EMBEXPR, (uint16_t) (depth + 1));
16197 }
16198
16199 parser->brace_nesting = brace_nesting;
16200 lex_state_set(parser, state);
16201 expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END);
16202
16203 // If this set of embedded statements only contains a single
16204 // statement, then Ruby does not consider it as a possible statement
16205 // that could emit a line event.
16206 if (statements != NULL && statements->body.size == 1) {
16207 pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE);
16208 }
16209
16210 return UP(pm_embedded_statements_node_create(parser, &opening, statements, &parser->previous));
16211 }
16212
16213 // Here the lexer has returned the beginning of an embedded variable.
16214 // In that case we'll parse the variable and create an appropriate node
16215 // for it and then return that node. These kinds of parts look like:
16216 //
16217 // "aaa #{bbb} #@ccc ddd"
16218 // ^^^^^
16219 case PM_TOKEN_EMBVAR: {
16220 // Ruby disallows seeing encoding around interpolation in strings,
16221 // even though it is known at parse time.
16222 parser->explicit_encoding = NULL;
16223
16224 lex_state_set(parser, PM_LEX_STATE_BEG);
16225 parser_lex(parser);
16226
16227 pm_token_t operator = parser->previous;
16228 pm_node_t *variable;
16229
16230 switch (parser->current.type) {
16231 // In this case a back reference is being interpolated. We'll
16232 // create a global variable read node.
16233 case PM_TOKEN_BACK_REFERENCE:
16234 parser_lex(parser);
16235 variable = UP(pm_back_reference_read_node_create(parser, &parser->previous));
16236 break;
16237 // In this case an nth reference is being interpolated. We'll
16238 // create a global variable read node.
16239 case PM_TOKEN_NUMBERED_REFERENCE:
16240 parser_lex(parser);
16241 variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16242 break;
16243 // In this case a global variable is being interpolated. We'll
16244 // create a global variable read node.
16245 case PM_TOKEN_GLOBAL_VARIABLE:
16246 parser_lex(parser);
16247 variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
16248 break;
16249 // In this case an instance variable is being interpolated.
16250 // We'll create an instance variable read node.
16251 case PM_TOKEN_INSTANCE_VARIABLE:
16252 parser_lex(parser);
16253 variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
16254 break;
16255 // In this case a class variable is being interpolated. We'll
16256 // create a class variable read node.
16257 case PM_TOKEN_CLASS_VARIABLE:
16258 parser_lex(parser);
16259 variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
16260 break;
16261 // We can hit here if we got an invalid token. In that case
16262 // we'll not attempt to lex this token and instead just return a
16263 // missing node.
16264 default:
16265 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EMBVAR_INVALID);
16266 variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16267 break;
16268 }
16269
16270 return UP(pm_embedded_variable_node_create(parser, &operator, variable));
16271 }
16272 default:
16273 parser_lex(parser);
16274 pm_parser_err_previous(parser, PM_ERR_CANNOT_PARSE_STRING_PART);
16275 return NULL;
16276 }
16277}
16278
16284static const uint8_t *
16285parse_operator_symbol_name(const pm_token_t *name) {
16286 switch (name->type) {
16287 case PM_TOKEN_TILDE:
16288 case PM_TOKEN_BANG:
16289 if (name->end[-1] == '@') return name->end - 1;
16291 default:
16292 return name->end;
16293 }
16294}
16295
16296static pm_node_t *
16297parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_state_t next_state) {
16298 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, opening, &parser->current, NULL);
16299 const uint8_t *end = parse_operator_symbol_name(&parser->current);
16300
16301 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16302 parser_lex(parser);
16303
16304 pm_string_shared_init(&symbol->unescaped, parser->previous.start, end);
16305 pm_node_flag_set(UP(symbol), PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING);
16306
16307 return UP(symbol);
16308}
16309
16315static pm_node_t *
16316parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_state, uint16_t depth) {
16317 const pm_token_t opening = parser->previous;
16318
16319 if (lex_mode->mode != PM_LEX_STRING) {
16320 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16321
16322 switch (parser->current.type) {
16323 case PM_CASE_OPERATOR:
16324 return parse_operator_symbol(parser, &opening, next_state == PM_LEX_STATE_NONE ? PM_LEX_STATE_ENDFN : next_state);
16325 case PM_TOKEN_IDENTIFIER:
16326 case PM_TOKEN_CONSTANT:
16327 case PM_TOKEN_INSTANCE_VARIABLE:
16328 case PM_TOKEN_METHOD_NAME:
16329 case PM_TOKEN_CLASS_VARIABLE:
16330 case PM_TOKEN_GLOBAL_VARIABLE:
16331 case PM_TOKEN_NUMBERED_REFERENCE:
16332 case PM_TOKEN_BACK_REFERENCE:
16333 case PM_CASE_KEYWORD:
16334 parser_lex(parser);
16335 break;
16336 default:
16337 expect2(parser, PM_TOKEN_IDENTIFIER, PM_TOKEN_METHOD_NAME, PM_ERR_SYMBOL_INVALID);
16338 break;
16339 }
16340
16341 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, NULL);
16342 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16343 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16344
16345 return UP(symbol);
16346 }
16347
16348 if (lex_mode->as.string.interpolation) {
16349 // If we have the end of the symbol, then we can return an empty symbol.
16350 if (match1(parser, PM_TOKEN_STRING_END)) {
16351 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16352 parser_lex(parser);
16353 pm_token_t content = {
16354 .type = PM_TOKEN_STRING_CONTENT,
16355 .start = parser->previous.start,
16356 .end = parser->previous.start
16357 };
16358
16359 return UP(pm_symbol_node_create(parser, &opening, &content, &parser->previous));
16360 }
16361
16362 // Now we can parse the first part of the symbol.
16363 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
16364
16365 // If we got a string part, then it's possible that we could transform
16366 // what looks like an interpolated symbol into a regular symbol.
16367 if (part && PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16368 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16369 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16370
16371 return UP(pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous));
16372 }
16373
16374 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16375 if (part) pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16376
16377 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16378 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16379 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16380 }
16381 }
16382
16383 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16384 if (match1(parser, PM_TOKEN_EOF)) {
16385 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16386 } else {
16387 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16388 }
16389
16390 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16391 return UP(symbol);
16392 }
16393
16394 pm_token_t content;
16395 pm_string_t unescaped;
16396
16397 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16398 content = parser->current;
16399 unescaped = parser->current_string;
16400 parser_lex(parser);
16401
16402 // If we have two string contents in a row, then the content of this
16403 // symbol is split because of heredoc contents. This looks like:
16404 //
16405 // <<A; :'a
16406 // A
16407 // b'
16408 //
16409 // In this case, the best way we have to represent this is as an
16410 // interpolated string node, so that's what we'll do here.
16411 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16412 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16413 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16414 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16415
16416 part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->current, NULL, &parser->current_string));
16417 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16418
16419 if (next_state != PM_LEX_STATE_NONE) {
16420 lex_state_set(parser, next_state);
16421 }
16422
16423 parser_lex(parser);
16424 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16425
16426 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16427 return UP(symbol);
16428 }
16429 } else {
16430 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end };
16431 pm_string_shared_init(&unescaped, content.start, content.end);
16432 }
16433
16434 if (next_state != PM_LEX_STATE_NONE) {
16435 lex_state_set(parser, next_state);
16436 }
16437
16438 if (match1(parser, PM_TOKEN_EOF)) {
16439 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_DYNAMIC);
16440 } else {
16441 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16442 }
16443
16444 return UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, false)));
16445}
16446
16451static PRISM_INLINE pm_node_t *
16452parse_undef_argument(pm_parser_t *parser, uint16_t depth) {
16453 switch (parser->current.type) {
16454 case PM_CASE_OPERATOR:
16455 return parse_operator_symbol(parser, NULL, PM_LEX_STATE_NONE);
16456 case PM_CASE_KEYWORD:
16457 case PM_TOKEN_CONSTANT:
16458 case PM_TOKEN_IDENTIFIER:
16459 case PM_TOKEN_METHOD_NAME: {
16460 parser_lex(parser);
16461
16462 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16463 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16464 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16465
16466 return UP(symbol);
16467 }
16468 case PM_TOKEN_SYMBOL_BEGIN: {
16469 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
16470 parser_lex(parser);
16471
16472 return parse_symbol(parser, &lex_mode, PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16473 }
16474 default:
16475 pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT);
16476 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16477 }
16478}
16479
16486static PRISM_INLINE pm_node_t *
16487parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) {
16488 switch (parser->current.type) {
16489 case PM_CASE_OPERATOR:
16490 return parse_operator_symbol(parser, NULL, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE);
16491 case PM_CASE_KEYWORD:
16492 case PM_TOKEN_CONSTANT:
16493 case PM_TOKEN_IDENTIFIER:
16494 case PM_TOKEN_METHOD_NAME: {
16495 if (first) lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
16496 parser_lex(parser);
16497
16498 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16499 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16500 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16501
16502 return UP(symbol);
16503 }
16504 case PM_TOKEN_SYMBOL_BEGIN: {
16505 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
16506 parser_lex(parser);
16507
16508 return parse_symbol(parser, &lex_mode, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16509 }
16510 case PM_TOKEN_BACK_REFERENCE:
16511 parser_lex(parser);
16512 return UP(pm_back_reference_read_node_create(parser, &parser->previous));
16513 case PM_TOKEN_NUMBERED_REFERENCE:
16514 parser_lex(parser);
16515 return UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16516 case PM_TOKEN_GLOBAL_VARIABLE:
16517 parser_lex(parser);
16518 return UP(pm_global_variable_read_node_create(parser, &parser->previous));
16519 default:
16520 pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT);
16521 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16522 }
16523}
16524
16529static pm_node_t *
16530parse_variable(pm_parser_t *parser) {
16531 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &parser->previous);
16532 int depth;
16533 bool is_numbered_param = pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
16534
16535 if (!is_numbered_param && ((depth = pm_parser_local_depth_constant_id(parser, name_id)) != -1)) {
16536 return UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false));
16537 }
16538
16539 pm_scope_t *current_scope = parser->current_scope;
16540 if (!current_scope->closed && !(current_scope->parameters & PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED)) {
16541 if (is_numbered_param) {
16542 // When you use a numbered parameter, it implies the existence of
16543 // all of the locals that exist before it. For example, referencing
16544 // _2 means that _1 must exist. Therefore here we loop through all
16545 // of the possibilities and add them into the constant pool.
16546 uint8_t maximum = (uint8_t) (parser->previous.start[1] - '0');
16547 for (uint8_t number = 1; number <= maximum; number++) {
16548 pm_parser_local_add_constant(parser, pm_numbered_parameter_names[number - 1], 2);
16549 }
16550
16551 if (!match1(parser, PM_TOKEN_EQUAL)) {
16552 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_FOUND;
16553 }
16554
16555 pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false));
16556 pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16557
16558 return node;
16559 } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16560 pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous));
16561 pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16562
16563 return node;
16564 }
16565 }
16566
16567 return NULL;
16568}
16569
16573static pm_node_t *
16574parse_variable_call(pm_parser_t *parser) {
16575 pm_node_flags_t flags = 0;
16576
16577 if (!match1(parser, PM_TOKEN_PARENTHESIS_LEFT) && (parser->previous.end[-1] != '!') && (parser->previous.end[-1] != '?')) {
16578 pm_node_t *node = parse_variable(parser);
16579 if (node != NULL) return node;
16580 flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL;
16581 }
16582
16583 pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous);
16584 pm_node_flag_set(UP(node), flags);
16585
16586 return UP(node);
16587}
16588
16595parse_method_definition_name(pm_parser_t *parser) {
16596 switch (parser->current.type) {
16597 case PM_CASE_KEYWORD:
16598 case PM_TOKEN_CONSTANT:
16599 case PM_TOKEN_METHOD_NAME:
16600 parser_lex(parser);
16601 return parser->previous;
16602 case PM_TOKEN_IDENTIFIER:
16603 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current));
16604 parser_lex(parser);
16605 return parser->previous;
16606 case PM_CASE_OPERATOR:
16607 lex_state_set(parser, PM_LEX_STATE_ENDFN);
16608 parser_lex(parser);
16609 return parser->previous;
16610 default:
16611 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_NAME, pm_token_str(parser->current.type));
16612 return (pm_token_t) { .type = 0, .start = parser->current.start, .end = parser->current.end };
16613 }
16614}
16615
16616static void
16617parse_heredoc_dedent_string(pm_arena_t *arena, pm_string_t *string, size_t common_whitespace) {
16618 // Make a writable copy in the arena if the string isn't already writable.
16619 // We keep a mutable pointer to the arena memory so we can memmove into it
16620 // below without casting away const from the string's source field.
16621 uint8_t *writable;
16622
16623 if (string->type != PM_STRING_OWNED) {
16624 size_t length = pm_string_length(string);
16625 writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
16626 pm_string_constant_init(string, (const char *) writable, length);
16627 } else {
16628 writable = (uint8_t *) string->source;
16629 }
16630
16631 // Now get the bounds of the existing string. We'll use this as a
16632 // destination to move bytes into. We'll also use it for bounds checking
16633 // since we don't require that these strings be null terminated.
16634 size_t dest_length = pm_string_length(string);
16635 const uint8_t *source_cursor = writable;
16636 const uint8_t *source_end = source_cursor + dest_length;
16637
16638 // We're going to move bytes backward in the string when we get leading
16639 // whitespace, so we'll maintain a pointer to the current position in the
16640 // string that we're writing to.
16641 size_t trimmed_whitespace = 0;
16642
16643 // While we haven't reached the amount of common whitespace that we need to
16644 // trim and we haven't reached the end of the string, we'll keep trimming
16645 // whitespace. Trimming in this context means skipping over these bytes such
16646 // that they aren't copied into the new string.
16647 while ((source_cursor < source_end) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
16648 if (*source_cursor == '\t') {
16649 trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
16650 if (trimmed_whitespace > common_whitespace) break;
16651 } else {
16652 trimmed_whitespace++;
16653 }
16654
16655 source_cursor++;
16656 dest_length--;
16657 }
16658
16659 memmove(writable, source_cursor, (size_t) (source_end - source_cursor));
16660 string->length = dest_length;
16661}
16662
16667static PRISM_INLINE bool
16668heredoc_dedent_discard_string_node(pm_parser_t *parser, pm_string_node_t *string_node) {
16669 if (string_node->unescaped.length == 0) {
16670 const uint8_t *cursor = parser->start + PM_LOCATION_START(&string_node->content_loc);
16671 return pm_memchr(cursor, '\\', string_node->content_loc.length, parser->encoding_changed, parser->encoding) == NULL;
16672 }
16673 return false;
16674}
16675
16679static void
16680parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_whitespace) {
16681 // The next node should be dedented if it's the first node in the list or if
16682 // it follows a string node.
16683 bool dedent_next = true;
16684
16685 // Iterate over all nodes, and trim whitespace accordingly. We're going to
16686 // keep around two indices: a read and a write.
16687 size_t write_index = 0;
16688
16689 pm_node_t *node;
16690 PM_NODE_LIST_FOREACH(nodes, read_index, node) {
16691 // We're not manipulating child nodes that aren't strings. In this case
16692 // we'll skip past it and indicate that the subsequent node should not
16693 // be dedented.
16694 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE)) {
16695 nodes->nodes[write_index++] = node;
16696 dedent_next = false;
16697 continue;
16698 }
16699
16700 pm_string_node_t *string_node = ((pm_string_node_t *) node);
16701 if (dedent_next) {
16702 parse_heredoc_dedent_string(parser->arena, &string_node->unescaped, common_whitespace);
16703 }
16704
16705 if (heredoc_dedent_discard_string_node(parser, string_node)) {
16706 } else {
16707 nodes->nodes[write_index++] = node;
16708 }
16709
16710 // We always dedent the next node if it follows a string node.
16711 dedent_next = true;
16712 }
16713
16714 nodes->size = write_index;
16715}
16716
16720static pm_token_t
16721parse_strings_empty_content(const uint8_t *location) {
16722 return (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = location, .end = location };
16723}
16724
16728static PRISM_INLINE pm_node_t *
16729parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint16_t depth) {
16730 assert(parser->current.type == PM_TOKEN_STRING_BEGIN);
16731 bool concating = false;
16732
16733 while (match1(parser, PM_TOKEN_STRING_BEGIN)) {
16734 pm_node_t *node = NULL;
16735
16736 // Here we have found a string literal. We'll parse it and add it to
16737 // the list of strings.
16738 const pm_lex_mode_t *lex_mode = parser->lex_modes.current;
16739 assert(lex_mode->mode == PM_LEX_STRING);
16740 bool lex_interpolation = lex_mode->as.string.interpolation;
16741 bool label_allowed = lex_mode->as.string.label_allowed && accepts_label;
16742
16743 pm_token_t opening = parser->current;
16744 parser_lex(parser);
16745
16746 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16747 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16748 // If we get here, then we have an end immediately after a
16749 // start. In that case we'll create an empty content token and
16750 // return an uninterpolated string.
16751 pm_token_t content = parse_strings_empty_content(parser->previous.start);
16752 pm_string_node_t *string = pm_string_node_create(parser, &opening, &content, &parser->previous);
16753
16754 pm_string_shared_init(&string->unescaped, content.start, content.end);
16755 node = UP(string);
16756 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16757 // If we get here, then we have an end of a label immediately
16758 // after a start. In that case we'll create an empty symbol
16759 // node.
16760 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, NULL, &parser->previous);
16761 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.start);
16762 node = UP(symbol);
16763
16764 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16765 } else if (!lex_interpolation) {
16766 // If we don't accept interpolation then we expect the string to
16767 // start with a single string content node.
16768 pm_string_t unescaped;
16769 pm_token_t content;
16770
16771 if (match1(parser, PM_TOKEN_EOF)) {
16772 unescaped = PM_STRING_EMPTY;
16773 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start, .end = parser->start };
16774 } else {
16775 unescaped = parser->current_string;
16776 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_EXPECT_STRING_CONTENT);
16777 content = parser->previous;
16778 }
16779
16780 // It is unfortunately possible to have multiple string content
16781 // nodes in a row in the case that there's heredoc content in
16782 // the middle of the string, like this cursed example:
16783 //
16784 // <<-END+'b
16785 // a
16786 // END
16787 // c'+'d'
16788 //
16789 // In that case we need to switch to an interpolated string to
16790 // be able to contain all of the parts.
16791 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16792 pm_node_list_t parts = { 0 };
16793 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16794 pm_node_list_append(parser->arena, &parts, part);
16795
16796 do {
16797 part = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16798 pm_node_list_append(parser->arena, &parts, part);
16799 parser_lex(parser);
16800 } while (match1(parser, PM_TOKEN_STRING_CONTENT));
16801
16802 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16803 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16804 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16805 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, true)));
16806 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16807 } else if (match1(parser, PM_TOKEN_EOF)) {
16808 pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF);
16809 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16810 } else if (accept1(parser, PM_TOKEN_STRING_END)) {
16811 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16812 } else {
16813 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_str(parser->previous.type));
16814 parser->previous.start = parser->previous.end;
16815 parser->previous.type = 0;
16816 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16817 }
16818 } else if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16819 // In this case we've hit string content so we know the string
16820 // at least has something in it. We'll need to check if the
16821 // following token is the end (in which case we can return a
16822 // plain string) or if it's not then it has interpolation.
16823 pm_token_t content = parser->current;
16824 pm_string_t unescaped = parser->current_string;
16825 const pm_encoding_t *explicit_encoding = parser->explicit_encoding;
16826 parser_lex(parser);
16827
16828 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16829 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16830 pm_node_flag_set(node, parse_unescaped_encoding(parser, explicit_encoding));
16831
16832 // Kind of odd behavior, but basically if we have an
16833 // unterminated string and it ends in a newline, we back up one
16834 // character so that the error message is on the last line of
16835 // content in the string.
16836 if (!accept1(parser, PM_TOKEN_STRING_END)) {
16837 const uint8_t *location = parser->previous.end;
16838 if (location > parser->start && location[-1] == '\n') location--;
16839 pm_parser_err(parser, U32(location - parser->start), 0, PM_ERR_STRING_LITERAL_EOF);
16840
16841 parser->previous.start = parser->previous.end;
16842 parser->previous.type = 0;
16843 }
16844 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16845 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, explicit_encoding, &content, &unescaped, true)));
16846 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16847 } else {
16848 // If we get here, then we have interpolation so we'll need
16849 // to create a string or symbol node with interpolation.
16850 pm_node_list_t parts = { 0 };
16851 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
16852 pm_node_flag_set(part, parse_unescaped_encoding(parser, explicit_encoding));
16853 pm_node_list_append(parser->arena, &parts, part);
16854
16855 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
16856 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16857 pm_node_list_append(parser->arena, &parts, part);
16858 }
16859 }
16860
16861 if (accept1(parser, PM_TOKEN_LABEL_END)) {
16862 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
16863 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16864 } else if (match1(parser, PM_TOKEN_EOF)) {
16865 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
16866 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
16867 } else {
16868 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
16869 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16870 }
16871 }
16872 } else {
16873 // If we get here, then the first part of the string is not plain
16874 // string content, in which case we need to parse the string as an
16875 // interpolated string.
16876 pm_node_list_t parts = { 0 };
16877 pm_node_t *part;
16878
16879 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
16880 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16881 pm_node_list_append(parser->arena, &parts, part);
16882 }
16883 }
16884
16885 if (accept1(parser, PM_TOKEN_LABEL_END)) {
16886 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
16887 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16888 } else if (match1(parser, PM_TOKEN_EOF)) {
16889 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
16890 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
16891 } else {
16892 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
16893 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16894 }
16895 }
16896
16897 if (current == NULL) {
16898 // If the node we just parsed is a symbol node, then we can't
16899 // concatenate it with anything else, so we can now return that
16900 // node.
16901 if (PM_NODE_TYPE_P(node, PM_SYMBOL_NODE) || PM_NODE_TYPE_P(node, PM_INTERPOLATED_SYMBOL_NODE)) {
16902 return node;
16903 }
16904
16905 // If we don't already have a node, then it's fine and we can just
16906 // set the result to be the node we just parsed.
16907 current = node;
16908 } else {
16909 // Otherwise we need to check the type of the node we just parsed.
16910 // If it cannot be concatenated with the previous node, then we'll
16911 // need to add a syntax error.
16912 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE) && !PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
16913 pm_parser_err_node(parser, node, PM_ERR_STRING_CONCATENATION);
16914 }
16915
16916 // If we haven't already created our container for concatenation,
16917 // we'll do that now.
16918 if (!concating) {
16919 if (!PM_NODE_TYPE_P(current, PM_STRING_NODE) && !PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
16920 pm_parser_err_node(parser, current, PM_ERR_STRING_CONCATENATION);
16921 }
16922
16923 concating = true;
16924 pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
16925 pm_interpolated_string_node_append(parser, container, current);
16926 current = UP(container);
16927 }
16928
16929 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, node);
16930 }
16931 }
16932
16933 return current;
16934}
16935
16936#define PM_PARSE_PATTERN_SINGLE 0
16937#define PM_PARSE_PATTERN_TOP 1
16938#define PM_PARSE_PATTERN_MULTI 2
16939
16940static pm_node_t *
16941parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
16942
16948static void
16949parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_constant_id_t capture, const pm_location_t *location) {
16950 // Skip this capture if it starts with an underscore.
16951 if (peek_at(parser, parser->start + location->start) == '_') return;
16952
16953 if (pm_constant_id_list_includes(captures, capture)) {
16954 pm_parser_err(parser, location->start, location->length, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
16955 } else {
16956 pm_constant_id_list_append(parser->arena, captures, capture);
16957 }
16958}
16959
16963static pm_node_t *
16964parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *node, uint16_t depth) {
16965 // Now, if there are any :: operators that follow, parse them as constant
16966 // path nodes.
16967 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
16968 pm_token_t delimiter = parser->previous;
16969 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
16970 node = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
16971 }
16972
16973 // If there is a [ or ( that follows, then this is part of a larger pattern
16974 // expression. We'll parse the inner pattern here, then modify the returned
16975 // inner pattern with our constant path attached.
16976 if (!match2(parser, PM_TOKEN_BRACKET_LEFT, PM_TOKEN_PARENTHESIS_LEFT)) {
16977 return node;
16978 }
16979
16980 pm_token_t opening;
16981 pm_token_t closing;
16982 pm_node_t *inner = NULL;
16983
16984 if (accept1(parser, PM_TOKEN_BRACKET_LEFT)) {
16985 opening = parser->previous;
16986 accept1(parser, PM_TOKEN_NEWLINE);
16987
16988 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
16989 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
16990 accept1(parser, PM_TOKEN_NEWLINE);
16991 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
16992 }
16993
16994 closing = parser->previous;
16995 } else {
16996 parser_lex(parser);
16997 opening = parser->previous;
16998 accept1(parser, PM_TOKEN_NEWLINE);
16999
17000 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
17001 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17002 accept1(parser, PM_TOKEN_NEWLINE);
17003 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17004 }
17005
17006 closing = parser->previous;
17007 }
17008
17009 if (!inner) {
17010 // If there was no inner pattern, then we have something like Foo() or
17011 // Foo[]. In that case we'll create an array pattern with no requireds.
17012 return UP(pm_array_pattern_node_constant_create(parser, node, &opening, &closing));
17013 }
17014
17015 // Now that we have the inner pattern, check to see if it's an array, find,
17016 // or hash pattern. If it is, then we'll attach our constant path to it if
17017 // it doesn't already have a constant. If it's not one of those node types
17018 // or it does have a constant, then we'll create an array pattern.
17019 switch (PM_NODE_TYPE(inner)) {
17020 case PM_ARRAY_PATTERN_NODE: {
17021 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17022
17023 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17024 PM_NODE_START_SET_NODE(pattern_node, node);
17025 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17026
17027 pattern_node->constant = node;
17028 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17029 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17030
17031 return UP(pattern_node);
17032 }
17033
17034 break;
17035 }
17036 case PM_FIND_PATTERN_NODE: {
17037 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17038
17039 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17040 PM_NODE_START_SET_NODE(pattern_node, node);
17041 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17042
17043 pattern_node->constant = node;
17044 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17045 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17046
17047 return UP(pattern_node);
17048 }
17049
17050 break;
17051 }
17052 case PM_HASH_PATTERN_NODE: {
17053 pm_hash_pattern_node_t *pattern_node = (pm_hash_pattern_node_t *) inner;
17054
17055 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17056 PM_NODE_START_SET_NODE(pattern_node, node);
17057 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17058
17059 pattern_node->constant = node;
17060 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17061 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17062
17063 return UP(pattern_node);
17064 }
17065
17066 break;
17067 }
17068 default:
17069 break;
17070 }
17071
17072 // If we got here, then we didn't return one of the inner patterns by
17073 // attaching its constant. In this case we'll create an array pattern and
17074 // attach our constant to it.
17075 pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing);
17076 pm_array_pattern_node_requireds_append(parser->arena, pattern_node, inner);
17077 return UP(pattern_node);
17078}
17079
17083static pm_splat_node_t *
17084parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
17085 assert(parser->previous.type == PM_TOKEN_USTAR);
17086 pm_token_t operator = parser->previous;
17087 pm_node_t *name = NULL;
17088
17089 // Rest patterns don't necessarily have a name associated with them. So we
17090 // will check for that here. If they do, then we'll add it to the local
17091 // table since this pattern will cause it to become a local variable.
17092 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17093 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17094
17095 int depth;
17096 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17097 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17098 }
17099
17100 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17101 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17102 name = UP(pm_local_variable_target_node_create(
17103 parser,
17104 &previous_loc,
17105 constant_id,
17106 (uint32_t) (depth == -1 ? 0 : depth)
17107 ));
17108 }
17109
17110 // Finally we can return the created node.
17111 return pm_splat_node_create(parser, &operator, name);
17112}
17113
17117static pm_node_t *
17118parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
17119 assert(parser->current.type == PM_TOKEN_USTAR_STAR);
17120 parser_lex(parser);
17121
17122 pm_token_t operator = parser->previous;
17123 pm_node_t *value = NULL;
17124
17125 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
17126 return UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
17127 }
17128
17129 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17130 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17131
17132 int depth;
17133 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17134 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17135 }
17136
17137 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17138 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17139 value = UP(pm_local_variable_target_node_create(
17140 parser,
17141 &previous_loc,
17142 constant_id,
17143 (uint32_t) (depth == -1 ? 0 : depth)
17144 ));
17145 }
17146
17147 return UP(pm_assoc_splat_node_create(parser, value, &operator));
17148}
17149
17154static bool
17155pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
17156 ptrdiff_t length = end - start;
17157 if (length == 0) return false;
17158
17159 // First ensure that it starts with a valid identifier starting character.
17160 size_t width = char_is_identifier_start(parser, start, end - start);
17161 if (width == 0) return false;
17162
17163 // Next, ensure that it's not an uppercase character.
17164 if (parser->encoding_changed) {
17165 if (parser->encoding->isupper_char(start, length)) return false;
17166 } else {
17167 if (pm_encoding_utf_8_isupper_char(start, length)) return false;
17168 }
17169
17170 // Next, iterate through all of the bytes of the string to ensure that they
17171 // are all valid identifier characters.
17172 const uint8_t *cursor = start + width;
17173 while ((width = char_is_identifier(parser, cursor, end - cursor))) cursor += width;
17174 return cursor == end;
17175}
17176
17181static pm_node_t *
17182parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_symbol_node_t *key) {
17183 const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc;
17184 const uint8_t *start = parser->start + PM_LOCATION_START(value_loc);
17185 const uint8_t *end = parser->start + PM_LOCATION_END(value_loc);
17186
17187 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
17188 int depth = -1;
17189
17190 if (pm_slice_is_valid_local(parser, start, end)) {
17191 depth = pm_parser_local_depth_constant_id(parser, constant_id);
17192 } else {
17193 pm_parser_err(parser, PM_NODE_START(key), PM_NODE_LENGTH(key), PM_ERR_PATTERN_HASH_KEY_LOCALS);
17194
17195 if ((end > start) && ((end[-1] == '!') || (end[-1] == '?'))) {
17196 PM_PARSER_ERR_FORMAT(parser, value_loc->start, value_loc->length, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (end - start), (const char *) start);
17197 }
17198 }
17199
17200 if (depth == -1) {
17201 pm_parser_local_add(parser, constant_id, start, end, 0);
17202 }
17203
17204 parse_pattern_capture(parser, captures, constant_id, value_loc);
17205 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17206 parser,
17207 value_loc,
17208 constant_id,
17209 (uint32_t) (depth == -1 ? 0 : depth)
17210 );
17211
17212 return UP(pm_implicit_node_create(parser, UP(target)));
17213}
17214
17219static void
17220parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) {
17221 if (pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, keys, node, true) != NULL) {
17222 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE);
17223 }
17224}
17225
17230parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, uint16_t depth) {
17231 pm_node_list_t assocs = { 0 };
17232 pm_static_literals_t keys = { 0 };
17233 pm_node_t *rest = NULL;
17234
17235 switch (PM_NODE_TYPE(first_node)) {
17236 case PM_ASSOC_SPLAT_NODE:
17237 case PM_NO_KEYWORDS_PARAMETER_NODE:
17238 rest = first_node;
17239 break;
17240 case PM_INTERPOLATED_SYMBOL_NODE:
17241 case PM_SYMBOL_NODE: {
17242 if (pm_symbol_node_label_p(parser, first_node)) {
17243 if (PM_NODE_TYPE_P(first_node, PM_INTERPOLATED_SYMBOL_NODE)) {
17244 pm_parser_err_node(parser, first_node, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17245 } else {
17246 parse_pattern_hash_key(parser, &keys, first_node);
17247 }
17248
17249 pm_node_t *value;
17250
17251 /*
17252 * The label has an implicit value when the next token cannot
17253 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17254 * reduction.
17255 */
17256 if (!token_begins_pattern_p(parser->current.type)) {
17257 if (PM_NODE_TYPE_P(first_node, PM_SYMBOL_NODE)) {
17258 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
17259 } else {
17260 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(first_node), 0));
17261 }
17262 } else {
17263 // Here we have a value for the first assoc in the list, so
17264 // we will parse it now.
17265 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17266 }
17267
17268 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17269 pm_node_list_append(parser->arena, &assocs, assoc);
17270 break;
17271 }
17272 }
17274 default: {
17275 // If we get anything else, then this is an error. For this we'll
17276 // create a missing node for the value and create an assoc node for
17277 // the first node in the list.
17278 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;
17279 pm_parser_err_node(parser, first_node, diag_id);
17280
17281 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_START(first_node), PM_NODE_LENGTH(first_node)));
17282 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17283
17284 pm_node_list_append(parser->arena, &assocs, assoc);
17285 break;
17286 }
17287 }
17288
17289 // If there are any other assocs, then we'll parse them now.
17290 while (accept1(parser, PM_TOKEN_COMMA)) {
17291 /*
17292 * A trailing comma ends the pattern when the next token cannot begin
17293 * another element, mirroring the grammar's `p_kwargs: p_kwarg ','`
17294 * reduction.
17295 */
17296 if (!token_begins_pattern_p(parser->current.type)) {
17297 // Trailing commas are not allowed to follow a rest pattern.
17298 if (rest != NULL) {
17299 pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17300 }
17301
17302 break;
17303 }
17304
17305 if (match1(parser, PM_TOKEN_USTAR_STAR)) {
17306 pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures);
17307
17308 if (rest == NULL) {
17309 rest = assoc;
17310 } else {
17311 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17312 pm_node_list_append(parser->arena, &assocs, assoc);
17313 }
17314 } else {
17315 pm_node_t *key;
17316
17317 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
17318 key = parse_strings(parser, NULL, true, (uint16_t) (depth + 1));
17319
17320 if (PM_NODE_TYPE_P(key, PM_INTERPOLATED_SYMBOL_NODE)) {
17321 pm_parser_err_node(parser, key, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17322 } else if (!pm_symbol_node_label_p(parser, key)) {
17323 pm_parser_err_node(parser, key, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17324 }
17325 } else if (accept1(parser, PM_TOKEN_LABEL)) {
17326 key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17327 } else {
17328 expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17329
17330 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = parser->previous.end, .end = parser->previous.end };
17331 key = UP(pm_symbol_node_create(parser, NULL, &label, NULL));
17332 }
17333
17334 parse_pattern_hash_key(parser, &keys, key);
17335 pm_node_t *value = NULL;
17336
17337 /*
17338 * The label has an implicit value when the next token cannot
17339 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17340 * reduction.
17341 */
17342 if (!token_begins_pattern_p(parser->current.type)) {
17343 if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) {
17344 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key);
17345 } else {
17346 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(key), 0));
17347 }
17348 } else {
17349 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17350 }
17351
17352 pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, NULL, value));
17353
17354 if (rest != NULL) {
17355 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17356 }
17357
17358 pm_node_list_append(parser->arena, &assocs, assoc);
17359 }
17360 }
17361
17362 pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest);
17363 // assocs.nodes is arena-allocated; no explicit free needed.
17364
17365 pm_static_literals_free(&keys);
17366 return node;
17367}
17368
17372static pm_node_t *
17373parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_diagnostic_id_t diag_id, uint16_t depth) {
17374 switch (parser->current.type) {
17375 case PM_TOKEN_IDENTIFIER:
17376 case PM_TOKEN_METHOD_NAME: {
17377 parser_lex(parser);
17378 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17379
17380 int depth;
17381 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17382 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17383 }
17384
17385 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17386 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17387 return UP(pm_local_variable_target_node_create(
17388 parser,
17389 &previous_loc,
17390 constant_id,
17391 (uint32_t) (depth == -1 ? 0 : depth)
17392 ));
17393 }
17394 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
17395 pm_token_t opening = parser->current;
17396 parser_lex(parser);
17397
17398 if (accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17399 // If we have an empty array pattern, then we'll just return a new
17400 // array pattern node.
17401 return UP(pm_array_pattern_node_empty_create(parser, &opening, &parser->previous));
17402 }
17403
17404 // Otherwise, we'll parse the inner pattern, then deal with it depending
17405 // on the type it returns.
17406 pm_node_t *inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
17407
17408 accept1(parser, PM_TOKEN_NEWLINE);
17409 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
17410 pm_token_t closing = parser->previous;
17411
17412 switch (PM_NODE_TYPE(inner)) {
17413 case PM_ARRAY_PATTERN_NODE: {
17414 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17415 if (pattern_node->opening_loc.length == 0) {
17416 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17417 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17418
17419 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17420 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17421
17422 return UP(pattern_node);
17423 }
17424
17425 break;
17426 }
17427 case PM_FIND_PATTERN_NODE: {
17428 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17429 if (pattern_node->opening_loc.length == 0) {
17430 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17431 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17432
17433 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17434 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17435
17436 return UP(pattern_node);
17437 }
17438
17439 break;
17440 }
17441 default:
17442 break;
17443 }
17444
17445 pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing);
17446 pm_array_pattern_node_requireds_append(parser->arena, node, inner);
17447 return UP(node);
17448 }
17449 case PM_TOKEN_BRACE_LEFT_HASH: {
17450 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17451 parser->pattern_matching_newlines = false;
17452
17454 pm_token_t opening = parser->current;
17455 parser_lex(parser);
17456
17457 if (accept1(parser, PM_TOKEN_BRACE_RIGHT)) {
17458 // If we have an empty hash pattern, then we'll just return a new hash
17459 // pattern node.
17460 node = pm_hash_pattern_node_empty_create(parser, &opening, &parser->previous);
17461 } else {
17462 pm_node_t *first_node;
17463
17464 switch (parser->current.type) {
17465 case PM_TOKEN_LABEL:
17466 parser_lex(parser);
17467 first_node = UP(pm_symbol_node_label_create(parser, &parser->previous));
17468 break;
17469 case PM_TOKEN_USTAR_STAR:
17470 first_node = parse_pattern_keyword_rest(parser, captures);
17471 break;
17472 case PM_TOKEN_STRING_BEGIN:
17473 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));
17474 break;
17475 default: {
17476 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_str(parser->current.type));
17477 parser_lex(parser);
17478
17479 first_node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
17480 break;
17481 }
17482 }
17483
17484 node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1));
17485
17486 accept1(parser, PM_TOKEN_NEWLINE);
17487 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE, &opening);
17488 pm_token_t closing = parser->previous;
17489
17490 PM_NODE_START_SET_TOKEN(parser, node, &opening);
17491 PM_NODE_LENGTH_SET_TOKEN(parser, node, &closing);
17492
17493 node->opening_loc = TOK2LOC(parser, &opening);
17494 node->closing_loc = TOK2LOC(parser, &closing);
17495 }
17496
17497 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17498 return UP(node);
17499 }
17500 case PM_TOKEN_UDOT_DOT:
17501 case PM_TOKEN_UDOT_DOT_DOT: {
17502 pm_token_t operator = parser->current;
17503 parser_lex(parser);
17504
17505 // Since we have a unary range operator, we need to parse the subsequent
17506 // expression as the right side of the range.
17507 switch (parser->current.type) {
17508 case PM_CASE_PRIMITIVE: {
17509 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));
17510 return UP(pm_range_node_create(parser, NULL, &operator, right));
17511 }
17512 default: {
17513 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE);
17514 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17515 return UP(pm_range_node_create(parser, NULL, &operator, right));
17516 }
17517 }
17518 }
17519 case PM_CASE_PRIMITIVE: {
17520 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));
17521
17522 // If we found a label, we need to immediately return to the caller.
17523 if (pm_symbol_node_label_p(parser, node)) return node;
17524
17525 // Call nodes (arithmetic operations) are not allowed in patterns
17526 if (PM_NODE_TYPE(node) == PM_CALL_NODE) {
17527 pm_parser_err_node(parser, node, diag_id);
17528 return UP(pm_error_recovery_node_create_unexpected(parser, node));
17529 }
17530
17531 // Now that we have a primitive, we need to check if it's part of a range.
17532 if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
17533 pm_token_t operator = parser->previous;
17534
17535 // Now that we have the operator, we need to check if this is followed
17536 // by another expression. If it is, then we will create a full range
17537 // node. Otherwise, we'll create an endless 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, node, &operator, right));
17542 }
17543 default:
17544 return UP(pm_range_node_create(parser, node, &operator, NULL));
17545 }
17546 }
17547
17548 return node;
17549 }
17550 case PM_TOKEN_CARET: {
17551 parser_lex(parser);
17552 pm_token_t operator = parser->previous;
17553
17554 // At this point we have a pin operator. We need to check the subsequent
17555 // expression to determine if it's a variable or an expression.
17556 switch (parser->current.type) {
17557 case PM_TOKEN_IDENTIFIER: {
17558 parser_lex(parser);
17559 pm_node_t *variable = UP(parse_variable(parser));
17560
17561 if (variable == NULL) {
17562 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->previous, PM_ERR_NO_LOCAL_VARIABLE);
17563 variable = UP(pm_local_variable_read_node_missing_create(parser, &parser->previous, 0));
17564 }
17565
17566 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17567 }
17568 case PM_TOKEN_INSTANCE_VARIABLE: {
17569 parser_lex(parser);
17570 pm_node_t *variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
17571
17572 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17573 }
17574 case PM_TOKEN_CLASS_VARIABLE: {
17575 parser_lex(parser);
17576 pm_node_t *variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
17577
17578 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17579 }
17580 case PM_TOKEN_GLOBAL_VARIABLE: {
17581 parser_lex(parser);
17582 pm_node_t *variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
17583
17584 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17585 }
17586 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
17587 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17588 parser->pattern_matching_newlines = false;
17589
17590 pm_token_t lparen = parser->current;
17591 parser_lex(parser);
17592
17593 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));
17594 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17595
17596 accept1(parser, PM_TOKEN_NEWLINE);
17597 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &lparen);
17598 return UP(pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous));
17599 }
17600 default: {
17601 // If we get here, then we have a pin operator followed by something
17602 // not understood. We'll create a missing node and return that.
17603 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN);
17604 pm_node_t *variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17605 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17606 }
17607 }
17608 }
17609 case PM_TOKEN_UCOLON_COLON: {
17610 pm_token_t delimiter = parser->current;
17611 parser_lex(parser);
17612
17613 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
17614 pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous);
17615
17616 return parse_pattern_constant_path(parser, captures, UP(node), (uint16_t) (depth + 1));
17617 }
17618 case PM_TOKEN_CONSTANT: {
17619 pm_token_t constant = parser->current;
17620 parser_lex(parser);
17621
17622 pm_node_t *node = UP(pm_constant_read_node_create(parser, &constant));
17623 return parse_pattern_constant_path(parser, captures, node, (uint16_t) (depth + 1));
17624 }
17625 default:
17626 pm_parser_err_current(parser, diag_id);
17627 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17628 }
17629}
17630
17631static bool
17632parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
17633 switch (PM_NODE_TYPE(node)) {
17634 case PM_LOCAL_VARIABLE_TARGET_NODE: {
17635 pm_parser_t *parser = (pm_parser_t *) data;
17636 pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
17637 return false;
17638 }
17639 default:
17640 return true;
17641 }
17642}
17643
17648static void
17649parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) {
17650 pm_visit_node(node, parse_pattern_alternation_error_each, parser);
17651}
17652
17657static pm_node_t *
17658parse_pattern_primitives(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, pm_diagnostic_id_t diag_id, uint16_t depth) {
17659 pm_node_t *node = first_node;
17660 bool alternation = false;
17661
17662 while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) {
17663 if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) {
17664 parse_pattern_alternation_error(parser, node);
17665 }
17666
17667 switch (parser->current.type) {
17668 case PM_TOKEN_IDENTIFIER:
17669 case PM_TOKEN_BRACKET_LEFT_ARRAY:
17670 case PM_TOKEN_BRACE_LEFT_HASH:
17671 case PM_TOKEN_CARET:
17672 case PM_TOKEN_CONSTANT:
17673 case PM_TOKEN_UCOLON_COLON:
17674 case PM_TOKEN_UDOT_DOT:
17675 case PM_TOKEN_UDOT_DOT_DOT:
17676 case PM_CASE_PRIMITIVE: {
17677 if (!alternation) {
17678 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17679 } else {
17680 pm_token_t operator = parser->previous;
17681 pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1));
17682
17683 if (captures->size) parse_pattern_alternation_error(parser, right);
17684 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17685 }
17686
17687 break;
17688 }
17689 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
17690 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
17691 pm_token_t operator = parser->previous;
17692 pm_token_t opening = parser->current;
17693 parser_lex(parser);
17694
17695 pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17696 accept1(parser, PM_TOKEN_NEWLINE);
17697 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17698 pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0));
17699
17700 if (!alternation) {
17701 node = right;
17702 } else {
17703 if (captures->size) parse_pattern_alternation_error(parser, right);
17704 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17705 }
17706
17707 break;
17708 }
17709 default: {
17710 pm_parser_err_current(parser, diag_id);
17711 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17712
17713 if (!alternation) {
17714 node = right;
17715 } else {
17716 if (captures->size) parse_pattern_alternation_error(parser, right);
17717 node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous));
17718 }
17719
17720 break;
17721 }
17722 }
17723 }
17724
17725 // If we have an =>, then we are assigning this pattern to a variable.
17726 // In this case we should create an assignment node.
17727 while (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
17728 pm_token_t operator = parser->previous;
17729 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET);
17730
17731 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17732 int depth;
17733
17734 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17735 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17736 }
17737
17738 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17739 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17740 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17741 parser,
17742 &previous_loc,
17743 constant_id,
17744 (uint32_t) (depth == -1 ? 0 : depth)
17745 );
17746
17747 node = UP(pm_capture_pattern_node_create(parser, node, target, &operator));
17748 }
17749
17750 return node;
17751}
17752
17756static pm_node_t *
17757parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
17758 pm_node_t *node = NULL;
17759
17760 bool leading_rest = false;
17761 bool trailing_rest = false;
17762
17763 switch (parser->current.type) {
17764 case PM_TOKEN_LABEL: {
17765 parser_lex(parser);
17766 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17767 node = UP(parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)));
17768
17769 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17770 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17771 }
17772
17773 return node;
17774 }
17775 case PM_TOKEN_USTAR_STAR: {
17776 node = parse_pattern_keyword_rest(parser, captures);
17777 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17778
17779 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17780 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17781 }
17782
17783 return node;
17784 }
17785 case PM_TOKEN_STRING_BEGIN: {
17786 // We need special handling for string beginnings because they could
17787 // be dynamic symbols leading to hash patterns.
17788 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17789
17790 if (pm_symbol_node_label_p(parser, node)) {
17791 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17792
17793 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17794 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17795 }
17796
17797 return node;
17798 }
17799
17800 node = parse_pattern_primitives(parser, captures, node, diag_id, (uint16_t) (depth + 1));
17801 break;
17802 }
17803 case PM_TOKEN_USTAR: {
17804 if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) {
17805 parser_lex(parser);
17806 node = UP(parse_pattern_rest(parser, captures));
17807 leading_rest = true;
17808 break;
17809 }
17810 }
17812 default:
17813 node = parse_pattern_primitives(parser, captures, NULL, diag_id, (uint16_t) (depth + 1));
17814 break;
17815 }
17816
17817 // If we got a dynamic label symbol, then we need to treat it like the
17818 // beginning of a hash pattern.
17819 if (pm_symbol_node_label_p(parser, node)) {
17820 return UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17821 }
17822
17823 if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) {
17824 // If we have a comma, then we are now parsing either an array pattern
17825 // or a find pattern. We need to parse all of the patterns, put them
17826 // into a big list, and then determine which type of node we have.
17827 pm_node_list_t nodes = { 0 };
17828 pm_node_list_append(parser->arena, &nodes, node);
17829
17830 // Gather up all of the patterns into the list.
17831 while (accept1(parser, PM_TOKEN_COMMA)) {
17832 /*
17833 * A trailing comma ends the pattern when the next token cannot
17834 * begin another pattern element, leaving the token for the
17835 * enclosing context to accept or reject.
17836 */
17837 if (!token_begins_pattern_p(parser->current.type)) {
17838 // A trailing comma forms an implicit rest pattern (`[a,]` is
17839 // `[a, *]`). If a rest pattern has already been parsed, then
17840 // this is a second rest, which is not allowed (e.g. `[a, *b,]`
17841 // or `x => a, *b,`).
17842 if (trailing_rest) {
17843 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
17844 }
17845
17846 node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
17847 pm_node_list_append(parser->arena, &nodes, node);
17848 trailing_rest = true;
17849 break;
17850 }
17851
17852 if (accept1(parser, PM_TOKEN_USTAR)) {
17853 node = UP(parse_pattern_rest(parser, captures));
17854
17855 // If we have already parsed a splat pattern, then this is an
17856 // error. We will continue to parse the rest of the patterns,
17857 // but we will indicate it as an error.
17858 if (trailing_rest) {
17859 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
17860 }
17861
17862 trailing_rest = true;
17863 } else {
17864 node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
17865 }
17866
17867 pm_node_list_append(parser->arena, &nodes, node);
17868 }
17869
17870 // If the first pattern and the last pattern are rest patterns, then we
17871 // will call this a find pattern, regardless of how many rest patterns
17872 // are in between because we know we already added the appropriate
17873 // errors. Otherwise we will create an array pattern.
17874 if (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) {
17875 node = UP(pm_find_pattern_node_create(parser, &nodes));
17876
17877 if (nodes.size == 2) {
17878 pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER);
17879 }
17880 } else {
17881 node = UP(pm_array_pattern_node_node_list_create(parser, &nodes));
17882
17883 if (leading_rest && trailing_rest) {
17884 pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS);
17885 }
17886 }
17887
17888 // nodes.nodes is arena-allocated; no explicit free needed.
17889 } else if (leading_rest) {
17890 // Otherwise, if we parsed a single splat pattern, then we know we have
17891 // an array pattern, so we can go ahead and create that node.
17892 node = UP(pm_array_pattern_node_rest_create(parser, node));
17893 }
17894
17895 return node;
17896}
17897
17903static PRISM_INLINE void
17904parse_negative_numeric(pm_node_t *node) {
17905 switch (PM_NODE_TYPE(node)) {
17906 case PM_INTEGER_NODE: {
17907 pm_integer_node_t *cast = (pm_integer_node_t *) node;
17908 cast->base.location.start--;
17909 cast->base.location.length++;
17910 cast->value.negative = true;
17911 break;
17912 }
17913 case PM_FLOAT_NODE: {
17914 pm_float_node_t *cast = (pm_float_node_t *) node;
17915 cast->base.location.start--;
17916 cast->base.location.length++;
17917 cast->value = -cast->value;
17918 break;
17919 }
17920 case PM_RATIONAL_NODE: {
17921 pm_rational_node_t *cast = (pm_rational_node_t *) node;
17922 cast->base.location.start--;
17923 cast->base.location.length++;
17924 cast->numerator.negative = true;
17925 break;
17926 }
17927 case PM_IMAGINARY_NODE:
17928 node->location.start--;
17929 node->location.length++;
17930 parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric);
17931 break;
17932 default:
17933 assert(false && "unreachable");
17934 break;
17935 }
17936}
17937
17943static void
17944pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
17945 switch (diag_id) {
17946 case PM_ERR_HASH_KEY: {
17947 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, pm_token_str(parser->previous.type));
17948 break;
17949 }
17950 case PM_ERR_HASH_VALUE:
17951 case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: {
17952 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
17953 break;
17954 }
17955 case PM_ERR_UNARY_RECEIVER: {
17956 const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_str(parser->current.type));
17957 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, human, parser->previous.start[0]);
17958 break;
17959 }
17960 case PM_ERR_UNARY_DISALLOWED:
17961 case PM_ERR_EXPECT_ARGUMENT: {
17962 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
17963 break;
17964 }
17965 default:
17966 pm_parser_err_previous(parser, diag_id);
17967 break;
17968 }
17969}
17970
17974static void
17975parse_retry(pm_parser_t *parser, const pm_node_t *node) {
17976#define CONTEXT_NONE 0
17977#define CONTEXT_THROUGH_ENSURE 1
17978#define CONTEXT_THROUGH_ELSE 2
17979
17980 pm_context_node_t *context_node = parser->current_context;
17981 int context = CONTEXT_NONE;
17982
17983 while (context_node != NULL) {
17984 switch (context_node->context) {
17985 case PM_CONTEXT_BEGIN_RESCUE:
17986 case PM_CONTEXT_BLOCK_RESCUE:
17987 case PM_CONTEXT_CLASS_RESCUE:
17988 case PM_CONTEXT_DEF_RESCUE:
17989 case PM_CONTEXT_LAMBDA_RESCUE:
17990 case PM_CONTEXT_MODULE_RESCUE:
17991 case PM_CONTEXT_SCLASS_RESCUE:
17992 case PM_CONTEXT_DEFINED:
17993 case PM_CONTEXT_RESCUE_MODIFIER:
17994 // These are the good cases. We're allowed to have a retry here.
17995 return;
17996 case PM_CONTEXT_CLASS:
17997 case PM_CONTEXT_DEF:
17998 case PM_CONTEXT_DEF_PARAMS:
17999 case PM_CONTEXT_MAIN:
18000 case PM_CONTEXT_MODULE:
18001 case PM_CONTEXT_PREEXE:
18002 case PM_CONTEXT_SCLASS:
18003 // These are the bad cases. We're not allowed to have a retry in
18004 // these contexts.
18005 if (context == CONTEXT_NONE) {
18006 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE);
18007 } else if (context == CONTEXT_THROUGH_ENSURE) {
18008 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE);
18009 } else if (context == CONTEXT_THROUGH_ELSE) {
18010 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE);
18011 }
18012 return;
18013 case PM_CONTEXT_BEGIN_ELSE:
18014 case PM_CONTEXT_BLOCK_ELSE:
18015 case PM_CONTEXT_CLASS_ELSE:
18016 case PM_CONTEXT_DEF_ELSE:
18017 case PM_CONTEXT_LAMBDA_ELSE:
18018 case PM_CONTEXT_MODULE_ELSE:
18019 case PM_CONTEXT_SCLASS_ELSE:
18020 // These are also bad cases, but with a more specific error
18021 // message indicating the else.
18022 context = CONTEXT_THROUGH_ELSE;
18023 break;
18024 case PM_CONTEXT_BEGIN_ENSURE:
18025 case PM_CONTEXT_BLOCK_ENSURE:
18026 case PM_CONTEXT_CLASS_ENSURE:
18027 case PM_CONTEXT_DEF_ENSURE:
18028 case PM_CONTEXT_LAMBDA_ENSURE:
18029 case PM_CONTEXT_MODULE_ENSURE:
18030 case PM_CONTEXT_SCLASS_ENSURE:
18031 // These are also bad cases, but with a more specific error
18032 // message indicating the ensure.
18033 context = CONTEXT_THROUGH_ENSURE;
18034 break;
18035 case PM_CONTEXT_NONE:
18036 // This case should never happen.
18037 assert(false && "unreachable");
18038 break;
18039 case PM_CONTEXT_BEGIN:
18040 case PM_CONTEXT_BLOCK_BRACES:
18041 case PM_CONTEXT_BLOCK_KEYWORDS:
18042 case PM_CONTEXT_BLOCK_PARAMETERS:
18043 case PM_CONTEXT_CASE_IN:
18044 case PM_CONTEXT_CASE_WHEN:
18045 case PM_CONTEXT_DEFAULT_PARAMS:
18046 case PM_CONTEXT_ELSE:
18047 case PM_CONTEXT_ELSIF:
18048 case PM_CONTEXT_EMBEXPR:
18049 case PM_CONTEXT_FOR_INDEX:
18050 case PM_CONTEXT_FOR:
18051 case PM_CONTEXT_IF:
18052 case PM_CONTEXT_LAMBDA_BRACES:
18053 case PM_CONTEXT_LAMBDA_DO_END:
18054 case PM_CONTEXT_LOOP_PREDICATE:
18055 case PM_CONTEXT_MULTI_TARGET:
18056 case PM_CONTEXT_PARENS:
18057 case PM_CONTEXT_POSTEXE:
18058 case PM_CONTEXT_PREDICATE:
18059 case PM_CONTEXT_TERNARY:
18060 case PM_CONTEXT_UNLESS:
18061 case PM_CONTEXT_UNTIL:
18062 case PM_CONTEXT_WHILE:
18063 // In these contexts we should continue walking up the list of
18064 // contexts.
18065 break;
18066 }
18067
18068 context_node = context_node->prev;
18069 }
18070
18071#undef CONTEXT_NONE
18072#undef CONTEXT_ENSURE
18073#undef CONTEXT_ELSE
18074}
18075
18079static void
18080parse_yield(pm_parser_t *parser, const pm_node_t *node) {
18081 pm_context_node_t *context_node = parser->current_context;
18082
18083 while (context_node != NULL) {
18084 switch (context_node->context) {
18085 case PM_CONTEXT_DEF:
18086 case PM_CONTEXT_DEF_PARAMS:
18087 case PM_CONTEXT_DEFINED:
18088 case PM_CONTEXT_DEF_ENSURE:
18089 case PM_CONTEXT_DEF_RESCUE:
18090 case PM_CONTEXT_DEF_ELSE:
18091 // These are the good cases. We're allowed to have a block exit
18092 // in these contexts.
18093 return;
18094 case PM_CONTEXT_CLASS:
18095 case PM_CONTEXT_CLASS_ENSURE:
18096 case PM_CONTEXT_CLASS_RESCUE:
18097 case PM_CONTEXT_CLASS_ELSE:
18098 case PM_CONTEXT_MAIN:
18099 case PM_CONTEXT_MODULE:
18100 case PM_CONTEXT_MODULE_ENSURE:
18101 case PM_CONTEXT_MODULE_RESCUE:
18102 case PM_CONTEXT_MODULE_ELSE:
18103 case PM_CONTEXT_SCLASS:
18104 case PM_CONTEXT_SCLASS_RESCUE:
18105 case PM_CONTEXT_SCLASS_ENSURE:
18106 case PM_CONTEXT_SCLASS_ELSE:
18107 // These are the bad cases. We're not allowed to have a retry in
18108 // these contexts.
18109 pm_parser_err_node(parser, node, PM_ERR_INVALID_YIELD);
18110 return;
18111 case PM_CONTEXT_NONE:
18112 // This case should never happen.
18113 assert(false && "unreachable");
18114 break;
18115 case PM_CONTEXT_BEGIN:
18116 case PM_CONTEXT_BEGIN_ELSE:
18117 case PM_CONTEXT_BEGIN_ENSURE:
18118 case PM_CONTEXT_BEGIN_RESCUE:
18119 case PM_CONTEXT_BLOCK_BRACES:
18120 case PM_CONTEXT_BLOCK_KEYWORDS:
18121 case PM_CONTEXT_BLOCK_ELSE:
18122 case PM_CONTEXT_BLOCK_ENSURE:
18123 case PM_CONTEXT_BLOCK_PARAMETERS:
18124 case PM_CONTEXT_BLOCK_RESCUE:
18125 case PM_CONTEXT_CASE_IN:
18126 case PM_CONTEXT_CASE_WHEN:
18127 case PM_CONTEXT_DEFAULT_PARAMS:
18128 case PM_CONTEXT_ELSE:
18129 case PM_CONTEXT_ELSIF:
18130 case PM_CONTEXT_EMBEXPR:
18131 case PM_CONTEXT_FOR_INDEX:
18132 case PM_CONTEXT_FOR:
18133 case PM_CONTEXT_IF:
18134 case PM_CONTEXT_LAMBDA_BRACES:
18135 case PM_CONTEXT_LAMBDA_DO_END:
18136 case PM_CONTEXT_LAMBDA_ELSE:
18137 case PM_CONTEXT_LAMBDA_ENSURE:
18138 case PM_CONTEXT_LAMBDA_RESCUE:
18139 case PM_CONTEXT_LOOP_PREDICATE:
18140 case PM_CONTEXT_MULTI_TARGET:
18141 case PM_CONTEXT_PARENS:
18142 case PM_CONTEXT_POSTEXE:
18143 case PM_CONTEXT_PREDICATE:
18144 case PM_CONTEXT_PREEXE:
18145 case PM_CONTEXT_RESCUE_MODIFIER:
18146 case PM_CONTEXT_TERNARY:
18147 case PM_CONTEXT_UNLESS:
18148 case PM_CONTEXT_UNTIL:
18149 case PM_CONTEXT_WHILE:
18150 // In these contexts we should continue walking up the list of
18151 // contexts.
18152 break;
18153 }
18154
18155 context_node = context_node->prev;
18156 }
18157}
18158
18163static pm_node_t *
18164parse_case(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18165 size_t opening_newline_index = token_newline_index(parser);
18166 parser_lex(parser);
18167
18168 pm_token_t case_keyword = parser->previous;
18169 pm_node_t *predicate = NULL;
18170
18171 pm_node_list_t current_block_exits = { 0 };
18172 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18173
18174 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18175 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18176 predicate = NULL;
18177 } else if (match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_END)) {
18178 predicate = NULL;
18179 } else if (!token_begins_expression_p(parser->current.type)) {
18180 predicate = NULL;
18181 } else {
18182 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));
18183 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18184 }
18185
18186 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
18187 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18188 parser_lex(parser);
18189 pop_block_exits(parser, previous_block_exits);
18190 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18191 return UP(pm_case_node_create(parser, &case_keyword, predicate, &parser->previous));
18192 }
18193
18194 /* At this point we can create a case node, though we don't yet know if it
18195 * is a case-in or case-when node. */
18196 pm_node_t *node;
18197
18198 if (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18199 pm_case_node_t *case_node = pm_case_node_create(parser, &case_keyword, predicate, NULL);
18200 pm_static_literals_t literals = { 0 };
18201
18202 /* At this point we've seen a when keyword, so we know this is a
18203 * case-when node. We will continue to parse the when nodes until we hit
18204 * the end of the list. */
18205 while (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18206 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18207 parser_lex(parser);
18208
18209 pm_token_t when_keyword = parser->previous;
18210 pm_when_node_t *when_node = pm_when_node_create(parser, &when_keyword);
18211
18212 do {
18213 if (accept1(parser, PM_TOKEN_USTAR)) {
18214 pm_token_t operator = parser->previous;
18215 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));
18216
18217 pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression);
18218 pm_when_node_conditions_append(parser->arena, when_node, UP(splat_node));
18219
18220 if (PM_NODE_TYPE_P(expression, PM_ERROR_RECOVERY_NODE)) break;
18221 } else {
18222 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));
18223 pm_when_node_conditions_append(parser->arena, when_node, condition);
18224
18225 /* If we found a missing node, then this is a syntax error
18226 * and we should stop looping. */
18227 if (PM_NODE_TYPE_P(condition, PM_ERROR_RECOVERY_NODE)) break;
18228
18229 /* If this is a string node, then we need to mark it as
18230 * frozen because when clause strings are frozen. */
18231 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
18232 pm_node_flag_set(condition, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
18233 } else if (PM_NODE_TYPE_P(condition, PM_SOURCE_FILE_NODE) && parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
18234 pm_node_flag_set(condition, PM_NODE_FLAG_STATIC_LITERAL);
18235 }
18236
18237 pm_when_clause_static_literals_add(parser, &literals, condition);
18238 }
18239 } while (accept1(parser, PM_TOKEN_COMMA));
18240
18241 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18242 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18243 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18244 }
18245 } else {
18246 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER);
18247 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18248 }
18249
18250 if (!match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18251 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_CASE_WHEN, (uint16_t) (depth + 1));
18252 if (statements != NULL) {
18253 pm_when_node_statements_set(when_node, statements);
18254 }
18255 }
18256
18257 pm_case_node_condition_append(parser->arena, case_node, UP(when_node));
18258 }
18259
18260 /* If we didn't parse any conditions (in or when) then we need to
18261 * indicate that we have an error. */
18262 if (case_node->conditions.size == 0) {
18263 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18264 }
18265
18266 pm_static_literals_free(&literals);
18267 node = UP(case_node);
18268 } else {
18269 pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate);
18270
18271 /* If this is a case-match node (i.e., it is a pattern matching case
18272 * statement) then we must have a predicate. */
18273 if (predicate == NULL) {
18274 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MATCH_MISSING_PREDICATE);
18275 }
18276
18277 /* At this point we expect that we're parsing a case-in node. We will
18278 * continue to parse the in nodes until we hit the end of the list. */
18279 while (match1(parser, PM_TOKEN_KEYWORD_IN)) {
18280 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18281
18282 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
18283 parser->pattern_matching_newlines = true;
18284
18285 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
18286 parser->command_start = false;
18287 parser_lex(parser);
18288
18289 pm_token_t in_keyword = parser->previous;
18290
18291 pm_constant_id_list_t captures = { 0 };
18292 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));
18293
18294 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
18295
18296 /* Since we're in the top-level of the case-in node we need to
18297 * check for guard clauses in the form of `if` or `unless`
18298 * statements. */
18299 if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) {
18300 pm_token_t keyword = parser->previous;
18301 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));
18302 pattern = UP(pm_if_node_modifier_create(parser, pattern, &keyword, predicate));
18303 } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) {
18304 pm_token_t keyword = parser->previous;
18305 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));
18306 pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate));
18307 }
18308
18309 /* Now we need to check for the terminator of the in node's pattern.
18310 * It can be a newline or semicolon optionally followed by a `then`
18311 * keyword. */
18312 pm_token_t then_keyword = { 0 };
18313 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18314 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18315 then_keyword = parser->previous;
18316 }
18317 } else {
18318 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_IN_DELIMITER);
18319 then_keyword = parser->previous;
18320 }
18321
18322 /* Now we can actually parse the statements associated with the in
18323 * node. */
18324 pm_statements_node_t *statements;
18325 if (match3(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18326 statements = NULL;
18327 } else {
18328 statements = parse_statements(parser, PM_CONTEXT_CASE_IN, (uint16_t) (depth + 1));
18329 }
18330
18331 /* Now that we have the full pattern and statements, we can create
18332 * the node and attach it to the case node. */
18333 pm_node_t *condition = UP(pm_in_node_create(parser, pattern, statements, &in_keyword, NTOK2PTR(then_keyword)));
18334 pm_case_match_node_condition_append(parser->arena, case_node, condition);
18335 }
18336
18337 /* If we didn't parse any conditions (in or when) then we need to
18338 * indicate that we have an error. */
18339 if (case_node->conditions.size == 0) {
18340 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18341 }
18342
18343 node = UP(case_node);
18344 }
18345
18346 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18347 if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) {
18348 pm_token_t else_keyword = parser->previous;
18349 pm_else_node_t *else_node;
18350
18351 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
18352 else_node = pm_else_node_create(parser, &else_keyword, parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1)), &parser->current);
18353 } else {
18354 else_node = pm_else_node_create(parser, &else_keyword, NULL, &parser->current);
18355 }
18356
18357 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18358 pm_case_node_else_clause_set((pm_case_node_t *) node, else_node);
18359 } else {
18360 pm_case_match_node_else_clause_set((pm_case_match_node_t *) node, else_node);
18361 }
18362 }
18363
18364 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18365 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword);
18366
18367 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18368 pm_case_node_end_keyword_loc_set(parser, (pm_case_node_t *) node, &parser->previous);
18369 } else {
18370 pm_case_match_node_end_keyword_loc_set(parser, (pm_case_match_node_t *) node, &parser->previous);
18371 }
18372
18373 pop_block_exits(parser, previous_block_exits);
18374 return node;
18375}
18376
18381static pm_node_t *
18382parse_class(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18383 size_t opening_newline_index = token_newline_index(parser);
18384 parser_lex(parser);
18385
18386 pm_token_t class_keyword = parser->previous;
18387 pm_do_loop_stack_push(parser, false);
18388
18389 pm_node_list_t current_block_exits = { 0 };
18390 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18391
18392 if (accept1(parser, PM_TOKEN_LESS_LESS)) {
18393 pm_token_t operator = parser->previous;
18394 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));
18395
18396 pm_parser_scope_push(parser, true);
18397 if (!match2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18398 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_str(parser->current.type));
18399 }
18400
18401 pm_node_t *statements = NULL;
18402 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18403 pm_accepts_block_stack_push(parser, true);
18404 statements = UP(parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1)));
18405 pm_accepts_block_stack_pop(parser);
18406 }
18407
18408 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18409 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18410 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)));
18411 } else {
18412 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18413 }
18414
18415 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18416
18417 pm_constant_id_list_t locals;
18418 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18419
18420 pm_parser_scope_pop(parser);
18421 pm_do_loop_stack_pop(parser);
18422
18423 flush_block_exits(parser, previous_block_exits);
18424 return UP(pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous));
18425 }
18426
18427 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));
18428 pm_token_t name = parser->previous;
18429 if (name.type != PM_TOKEN_CONSTANT) {
18430 pm_parser_err_token(parser, &name, PM_ERR_CLASS_NAME);
18431 }
18432
18433 pm_token_t inheritance_operator = { 0 };
18434 pm_node_t *superclass;
18435
18436 if (match1(parser, PM_TOKEN_LESS)) {
18437 inheritance_operator = parser->current;
18438 lex_state_set(parser, PM_LEX_STATE_BEG);
18439
18440 parser->command_start = true;
18441 parser_lex(parser);
18442
18443 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));
18444 } else {
18445 superclass = NULL;
18446 }
18447
18448 pm_parser_scope_push(parser, true);
18449
18450 if (inheritance_operator.start != NULL) {
18451 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CLASS_UNEXPECTED_END);
18452 } else {
18453 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18454 }
18455 pm_node_t *statements = NULL;
18456
18457 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18458 pm_accepts_block_stack_push(parser, true);
18459 statements = UP(parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1)));
18460 pm_accepts_block_stack_pop(parser);
18461 }
18462
18463 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18464 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18465 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)));
18466 } else {
18467 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18468 }
18469
18470 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18471
18472 if (context_def_p(parser)) {
18473 pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD);
18474 }
18475
18476 pm_constant_id_list_t locals;
18477 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18478
18479 pm_parser_scope_pop(parser);
18480 pm_do_loop_stack_pop(parser);
18481
18482 if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) {
18483 pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME);
18484 if (!PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18485 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18486 }
18487 }
18488
18489 pop_block_exits(parser, previous_block_exits);
18490 return UP(pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, NTOK2PTR(inheritance_operator), superclass, statements, &parser->previous));
18491}
18492
18496static pm_node_t *
18497parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
18498 pm_node_list_t current_block_exits = { 0 };
18499 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18500
18501 pm_token_t def_keyword = parser->current;
18502 size_t opening_newline_index = token_newline_index(parser);
18503
18504 pm_node_t *receiver = NULL;
18505 pm_token_t operator = { 0 };
18506 pm_token_t name;
18507
18508 /* This context is necessary for lexing `...` in a bare params correctly. It
18509 * must be pushed before lexing the first param, so it is here. */
18510 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18511 parser_lex(parser);
18512
18513 /* This will be false if the method name is not a valid identifier but could
18514 * be followed by an operator. */
18515 bool valid_name = true;
18516
18517 switch (parser->current.type) {
18518 case PM_CASE_OPERATOR:
18519 pm_parser_scope_push(parser, true);
18520 lex_state_set(parser, PM_LEX_STATE_ENDFN);
18521 parser_lex(parser);
18522
18523 name = parser->previous;
18524 break;
18525 case PM_TOKEN_IDENTIFIER: {
18526 parser_lex(parser);
18527
18528 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18529 receiver = parse_variable_call(parser);
18530
18531 pm_parser_scope_push(parser, true);
18532 lex_state_set(parser, PM_LEX_STATE_FNAME);
18533 parser_lex(parser);
18534
18535 operator = parser->previous;
18536 name = parse_method_definition_name(parser);
18537 } else {
18538 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
18539 pm_parser_scope_push(parser, true);
18540
18541 name = parser->previous;
18542 }
18543
18544 break;
18545 }
18546 case PM_TOKEN_INSTANCE_VARIABLE:
18547 case PM_TOKEN_CLASS_VARIABLE:
18548 case PM_TOKEN_GLOBAL_VARIABLE:
18549 valid_name = false;
18551 case PM_TOKEN_CONSTANT:
18552 case PM_TOKEN_KEYWORD_NIL:
18553 case PM_TOKEN_KEYWORD_SELF:
18554 case PM_TOKEN_KEYWORD_TRUE:
18555 case PM_TOKEN_KEYWORD_FALSE:
18556 case PM_TOKEN_KEYWORD___FILE__:
18557 case PM_TOKEN_KEYWORD___LINE__:
18558 case PM_TOKEN_KEYWORD___ENCODING__: {
18559 pm_parser_scope_push(parser, true);
18560 parser_lex(parser);
18561
18562 pm_token_t identifier = parser->previous;
18563
18564 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18565 lex_state_set(parser, PM_LEX_STATE_FNAME);
18566 parser_lex(parser);
18567 operator = parser->previous;
18568
18569 switch (identifier.type) {
18570 case PM_TOKEN_CONSTANT:
18571 receiver = UP(pm_constant_read_node_create(parser, &identifier));
18572 break;
18573 case PM_TOKEN_INSTANCE_VARIABLE:
18574 receiver = UP(pm_instance_variable_read_node_create(parser, &identifier));
18575 break;
18576 case PM_TOKEN_CLASS_VARIABLE:
18577 receiver = UP(pm_class_variable_read_node_create(parser, &identifier));
18578 break;
18579 case PM_TOKEN_GLOBAL_VARIABLE:
18580 receiver = UP(pm_global_variable_read_node_create(parser, &identifier));
18581 break;
18582 case PM_TOKEN_KEYWORD_NIL:
18583 receiver = UP(pm_nil_node_create(parser, &identifier));
18584 break;
18585 case PM_TOKEN_KEYWORD_SELF:
18586 receiver = UP(pm_self_node_create(parser, &identifier));
18587 break;
18588 case PM_TOKEN_KEYWORD_TRUE:
18589 receiver = UP(pm_true_node_create(parser, &identifier));
18590 break;
18591 case PM_TOKEN_KEYWORD_FALSE:
18592 receiver = UP(pm_false_node_create(parser, &identifier));
18593 break;
18594 case PM_TOKEN_KEYWORD___FILE__:
18595 receiver = UP(pm_source_file_node_create(parser, &identifier));
18596 break;
18597 case PM_TOKEN_KEYWORD___LINE__:
18598 receiver = UP(pm_source_line_node_create(parser, &identifier));
18599 break;
18600 case PM_TOKEN_KEYWORD___ENCODING__:
18601 receiver = UP(pm_source_encoding_node_create(parser, &identifier));
18602 break;
18603 default:
18604 break;
18605 }
18606
18607 name = parse_method_definition_name(parser);
18608 } else {
18609 if (!valid_name) {
18610 PM_PARSER_ERR_TOKEN_FORMAT(parser, &identifier, PM_ERR_DEF_NAME, pm_token_str(identifier.type));
18611 }
18612
18613 name = identifier;
18614 }
18615 break;
18616 }
18617 case PM_TOKEN_PARENTHESIS_LEFT: {
18618 /* The current context is `PM_CONTEXT_DEF_PARAMS`, however the inner
18619 * expression of this parenthesis should not be processed under this
18620 * context. Thus, the context is popped here. */
18621 context_pop(parser);
18622 parser_lex(parser);
18623
18624 pm_token_t lparen = parser->previous;
18625 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));
18626
18627 accept1(parser, PM_TOKEN_NEWLINE);
18628 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
18629 pm_token_t rparen = parser->previous;
18630
18631 lex_state_set(parser, PM_LEX_STATE_FNAME);
18632 expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM);
18633
18634 operator = parser->previous;
18635 receiver = UP(pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0));
18636
18637 /* To push `PM_CONTEXT_DEF_PARAMS` again is for the same reason as
18638 * described the above. */
18639 pm_parser_scope_push(parser, true);
18640 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18641 name = parse_method_definition_name(parser);
18642 break;
18643 }
18644 default:
18645 pm_parser_scope_push(parser, true);
18646 name = parse_method_definition_name(parser);
18647 break;
18648 }
18649
18650 pm_token_t lparen = { 0 };
18651 pm_token_t rparen = { 0 };
18652 pm_parameters_node_t *params;
18653
18654 bool accept_endless_def = true;
18655 switch (parser->current.type) {
18656 case PM_TOKEN_PARENTHESIS_LEFT: {
18657 parser_lex(parser);
18658 lparen = parser->previous;
18659
18660 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18661 params = NULL;
18662 } else {
18663 /* https://bugs.ruby-lang.org/issues/19107 */
18664 bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1;
18665 params = parse_parameters(
18666 parser,
18667 PM_BINDING_POWER_DEFINED,
18668 true,
18669 allow_trailing_comma,
18670 true,
18671 true,
18672 false,
18673 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18674 (uint16_t) (depth + 1)
18675 );
18676 }
18677
18678 lex_state_set(parser, PM_LEX_STATE_BEG);
18679 parser->command_start = true;
18680
18681 context_pop(parser);
18682 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18683 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_str(parser->current.type));
18684 parser->previous.start = parser->previous.end;
18685 parser->previous.type = 0;
18686 }
18687
18688 rparen = parser->previous;
18689 break;
18690 }
18691 case PM_CASE_PARAMETER: {
18692 /* If we're about to lex a label, we need to add the label state to
18693 * make sure the next newline is ignored. */
18694 if (parser->current.type == PM_TOKEN_LABEL) {
18695 lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL);
18696 }
18697
18698 params = parse_parameters(
18699 parser,
18700 PM_BINDING_POWER_DEFINED,
18701 false,
18702 false,
18703 true,
18704 true,
18705 false,
18706 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18707 (uint16_t) (depth + 1)
18708 );
18709
18710 /* Reject `def * = 1` and similar. We have to specifically check for
18711 * them because they create ambiguity with optional arguments. */
18712 accept_endless_def = false;
18713
18714 context_pop(parser);
18715 break;
18716 }
18717 default: {
18718 params = NULL;
18719 context_pop(parser);
18720 break;
18721 }
18722 }
18723
18724 pm_node_t *statements = NULL;
18725 pm_token_t equal = { 0 };
18726 pm_token_t end_keyword = { 0 };
18727
18728 if (accept1(parser, PM_TOKEN_EQUAL)) {
18729 if (token_is_setter_name(&name)) {
18730 pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER);
18731 }
18732 if (!accept_endless_def) {
18733 pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
18734 }
18735 if (
18736 parser->current_context->context == PM_CONTEXT_DEFAULT_PARAMS &&
18737 parser->current_context->prev->context == PM_CONTEXT_BLOCK_PARAMETERS
18738 ) {
18739 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");
18740 }
18741 equal = parser->previous;
18742
18743 context_push(parser, PM_CONTEXT_DEF);
18744 pm_do_loop_stack_push(parser, false);
18745 statements = UP(pm_statements_node_create(parser));
18746
18747 uint8_t allow_flags;
18748 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
18749 allow_flags = flags & PM_PARSE_ACCEPTS_COMMAND_CALL;
18750 } else {
18751 /* Allow `def foo = puts "Hello"` but not
18752 * `private def foo = puts "Hello"` */
18753 allow_flags = (binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
18754 }
18755
18756 /* Inside a def body, we push true onto the accepts_block_stack so that
18757 * `do` is lexed as PM_TOKEN_KEYWORD_DO (which can only start a block
18758 * for primary-level constructs, not commands). During command argument
18759 * parsing, the stack is pushed to false, causing `do` to be lexed as
18760 * PM_TOKEN_KEYWORD_DO_BLOCK, which is not consumed inside the endless
18761 * def body and instead left for the outer context. A method definition
18762 * opens a fresh context all the way through its rescue modifier, so
18763 * this frame spans the rescue modifier value as well: the `do` in
18764 * `baz def f = a rescue z do end` lexes as a plain keyword that
18765 * attaches to `z` rather than to `baz`. */
18766 pm_accepts_block_stack_push(parser, true);
18767 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));
18768
18769 /* If an unconsumed PM_TOKEN_KEYWORD_DO follows the body, it is an error
18770 * (e.g., `def f = 1 do end`). PM_TOKEN_KEYWORD_DO_BLOCK is
18771 * intentionally not caught here — it should bubble up to the outer
18772 * context (e.g., `private def f = puts "Hello" do end` where the block
18773 * attaches to `private`). */
18774 if (accept1(parser, PM_TOKEN_KEYWORD_DO)) {
18775 pm_block_node_t *block = parse_block(parser, (uint16_t) (depth + 1));
18776 pm_parser_err_node(parser, UP(block), PM_ERR_DEF_ENDLESS_DO_BLOCK);
18777 }
18778
18779 /* Any number of rescue modifiers chain onto the body within the method
18780 * definition itself, associating to the left: `def f = a rescue b
18781 * rescue c` defines a method whose body is `(a rescue b) rescue c`,
18782 * rather than a rescue modifier guarding the definition. */
18783 while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
18784 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
18785
18786 pm_token_t rescue_keyword = parser->previous;
18787
18788 /* In the Ruby grammar, the rescue value of an endless method
18789 * command excludes and/or and in/=>. */
18790 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));
18791 context_pop(parser);
18792
18793 statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value));
18794 }
18795
18796 pm_accepts_block_stack_pop(parser);
18797
18798 /* A nested endless def whose body is a command call (e.g.,
18799 * `def f = def g = foo bar`) is a command assignment and cannot appear
18800 * as a def body. */
18801 if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(parser, statement)) {
18802 PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
18803 }
18804
18805 pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false);
18806 pm_do_loop_stack_pop(parser);
18807 context_pop(parser);
18808 } else {
18809 if (lparen.start == NULL) {
18810 lex_state_set(parser, PM_LEX_STATE_BEG);
18811 parser->command_start = true;
18812 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_DEF_PARAMS_TERM);
18813 } else {
18814 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18815 }
18816
18817 pm_accepts_block_stack_push(parser, true);
18818 pm_do_loop_stack_push(parser, false);
18819
18820 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18821 pm_accepts_block_stack_push(parser, true);
18822 statements = UP(parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1)));
18823 pm_accepts_block_stack_pop(parser);
18824 }
18825
18826 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18827 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18828 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)));
18829 } else {
18830 parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false);
18831 }
18832
18833 pm_accepts_block_stack_pop(parser);
18834 pm_do_loop_stack_pop(parser);
18835
18836 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword);
18837 end_keyword = parser->previous;
18838 }
18839
18840 pm_constant_id_list_t locals;
18841 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18842 pm_parser_scope_pop(parser);
18843
18844 /* If the final character is `@` as is the case when defining methods to
18845 * override the unary operators, we should ignore the @ in the same way we
18846 * do for symbols. */
18847 pm_constant_id_t name_id = pm_parser_constant_id_raw(parser, name.start, parse_operator_symbol_name(&name));
18848
18849 flush_block_exits(parser, previous_block_exits);
18850
18851 return UP(pm_def_node_create(
18852 parser,
18853 name_id,
18854 &name,
18855 receiver,
18856 params,
18857 statements,
18858 &locals,
18859 &def_keyword,
18860 NTOK2PTR(operator),
18861 NTOK2PTR(lparen),
18862 NTOK2PTR(rparen),
18863 NTOK2PTR(equal),
18864 NTOK2PTR(end_keyword)
18865 ));
18866}
18867
18871static pm_node_t *
18872parse_module(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18873 pm_node_list_t current_block_exits = { 0 };
18874 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18875
18876 size_t opening_newline_index = token_newline_index(parser);
18877 parser_lex(parser);
18878 pm_token_t module_keyword = parser->previous;
18879
18880 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));
18881 pm_token_t name;
18882
18883 /* If we can recover from a syntax error that occurred while parsing the
18884 * name of the module, then we'll handle that here. */
18885 if (PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18886 pop_block_exits(parser, previous_block_exits);
18887
18888 pm_token_t missing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
18889 return UP(pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing));
18890 }
18891
18892 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
18893 pm_token_t double_colon = parser->previous;
18894
18895 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
18896 constant_path = UP(pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous));
18897 }
18898
18899 /* Here we retrieve the name of the module. If it wasn't a constant, then
18900 * it's possible that `module foo` was passed, which is a syntax error. We
18901 * handle that here as well. */
18902 name = parser->previous;
18903 if (name.type != PM_TOKEN_CONSTANT) {
18904 pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME);
18905 }
18906
18907 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)) {
18908 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18909 }
18910
18911 pm_parser_scope_push(parser, true);
18912 accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE);
18913 pm_node_t *statements = NULL;
18914
18915 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18916 pm_accepts_block_stack_push(parser, true);
18917 statements = UP(parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1)));
18918 pm_accepts_block_stack_pop(parser);
18919 }
18920
18921 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18922 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18923 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)));
18924 } else {
18925 parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false);
18926 }
18927
18928 pm_constant_id_list_t locals;
18929 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18930
18931 pm_parser_scope_pop(parser);
18932 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword);
18933
18934 if (context_def_p(parser)) {
18935 pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
18936 }
18937
18938 pop_block_exits(parser, previous_block_exits);
18939
18940 return UP(pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous));
18941}
18942
18946static pm_node_t *
18947parse_string_array(pm_parser_t *parser, uint16_t depth) {
18948 parser_lex(parser);
18949 pm_token_t opening = parser->previous;
18950 pm_array_node_t *array = pm_array_node_create(parser, &opening);
18951
18952 /* This is the current node that we are parsing that will be added to the
18953 * list of elements. */
18954 pm_node_t *current = NULL;
18955
18956 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
18957 switch (parser->current.type) {
18958 case PM_TOKEN_WORDS_SEP: {
18959 /* Reset the explicit encoding if we hit a separator since each
18960 * element can have its own encoding. */
18961 parser->explicit_encoding = NULL;
18962
18963 if (current == NULL) {
18964 /* If we hit a separator before we have any content, then we
18965 * don't need to do anything. */
18966 } else {
18967 /* If we hit a separator after we've hit content, then we
18968 * need to append that content to the list and reset the
18969 * current node. */
18970 pm_array_node_elements_append(parser->arena, array, current);
18971 current = NULL;
18972 }
18973
18974 parser_lex(parser);
18975 break;
18976 }
18977 case PM_TOKEN_STRING_CONTENT: {
18978 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
18979 pm_node_flag_set(string, parse_unescaped_encoding(parser, parser->explicit_encoding));
18980 parser_lex(parser);
18981
18982 if (current == NULL) {
18983 /* If we hit content and the current node is NULL, then this
18984 * is the first string content we've seen. In that case
18985 * we're going to create a new string node and set that to
18986 * the current. */
18987 current = string;
18988 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
18989 /* If we hit string content and the current node is an
18990 * interpolated string, then we need to append the string
18991 * content to the list of child nodes. */
18992 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
18993 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
18994 /* If we hit string content and the current node is a string
18995 * node, then we need to convert the current node into an
18996 * interpolated string and add the string content to the
18997 * list of child nodes. */
18998 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
18999 pm_interpolated_string_node_append(parser, interpolated, current);
19000 pm_interpolated_string_node_append(parser, interpolated, string);
19001 current = UP(interpolated);
19002 } else {
19003 assert(false && "unreachable");
19004 }
19005
19006 break;
19007 }
19008 case PM_TOKEN_EMBVAR: {
19009 if (current == NULL) {
19010 /* If we hit an embedded variable and the current node is
19011 * NULL, then this is the start of a new string. We'll set
19012 * the current node to a new interpolated string. */
19013 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
19014 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19015 /* If we hit an embedded variable and the current node is a
19016 * string node, then we'll convert the current into an
19017 * interpolated string and add the string node to the list
19018 * of parts. */
19019 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
19020 pm_interpolated_string_node_append(parser, interpolated, current);
19021 current = UP(interpolated);
19022 } else {
19023 /* If we hit an embedded variable and the current node is an
19024 * interpolated string, then we'll just add the embedded
19025 * variable. */
19026 }
19027
19028 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19029 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
19030 break;
19031 }
19032 case PM_TOKEN_EMBEXPR_BEGIN: {
19033 if (current == NULL) {
19034 /* If we hit an embedded expression and the current node is
19035 * NULL, then this is the start of a new string. We'll set
19036 * the current node to a new interpolated string. */
19037 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
19038 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19039 /* If we hit an embedded expression and the current node is
19040 * a string node, then we'll convert the current into an
19041 * interpolated string and add the string node to the list
19042 * of parts. */
19043 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
19044 pm_interpolated_string_node_append(parser, interpolated, current);
19045 current = UP(interpolated);
19046 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
19047 /* If we hit an embedded expression and the current node is
19048 * an interpolated string, then we'll just continue on. */
19049 } else {
19050 assert(false && "unreachable");
19051 }
19052
19053 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19054 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
19055 break;
19056 }
19057 default:
19058 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_UPPER_ELEMENT);
19059 parser_lex(parser);
19060 break;
19061 }
19062 }
19063
19064 /* If we have a current node, then we need to append it to the list. */
19065 if (current) {
19066 pm_array_node_elements_append(parser->arena, array, current);
19067 }
19068
19069 pm_token_t closing = parser->current;
19070 if (match1(parser, PM_TOKEN_EOF)) {
19071 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_UPPER_TERM);
19072 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19073 } else {
19074 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM);
19075 }
19076
19077 pm_array_node_close_set(parser, array, &closing);
19078 return UP(array);
19079}
19080
19084static pm_node_t *
19085parse_symbol_array(pm_parser_t *parser, uint16_t depth) {
19086 parser_lex(parser);
19087 pm_token_t opening = parser->previous;
19088 pm_array_node_t *array = pm_array_node_create(parser, &opening);
19089
19090 /* This is the current node that we are parsing that will be added to the
19091 * list of elements. */
19092 pm_node_t *current = NULL;
19093
19094 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
19095 switch (parser->current.type) {
19096 case PM_TOKEN_WORDS_SEP: {
19097 /* Reset the explicit encoding if we hit a separator since each
19098 * element can have its own encoding. */
19099 parser->explicit_encoding = NULL;
19100
19101 if (current == NULL) {
19102 /* If we hit a separator before we have any content, then we
19103 * don't need to do anything. */
19104 } else {
19105 /* If we hit a separator after we've hit content, then we
19106 * need to append that content to the list and reset the
19107 * current node. */
19108 pm_array_node_elements_append(parser->arena, array, current);
19109 current = NULL;
19110 }
19111
19112 parser_lex(parser);
19113 break;
19114 }
19115 case PM_TOKEN_STRING_CONTENT: {
19116 if (current == NULL) {
19117 /* If we hit content and the current node is NULL, then this
19118 * is the first string content we've seen. In that case
19119 * we're going to create a new string node and set that to
19120 * the current. */
19121 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
19122 parser_lex(parser);
19123 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
19124 /* If we hit string content and the current node is an
19125 * interpolated string, then we need to append the string
19126 * content to the list of child nodes. */
19127 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
19128 parser_lex(parser);
19129
19130 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
19131 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19132 /* If we hit string content and the current node is a symbol
19133 * node, then we need to convert the current node into an
19134 * interpolated string and add the string content to the
19135 * list of child nodes. */
19136 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
19137 pm_token_t content = {
19138 .type = PM_TOKEN_STRING_CONTENT,
19139 .start = parser->start + cast->value_loc.start,
19140 .end = parser->start + cast->value_loc.start + cast->value_loc.length
19141 };
19142
19143 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
19144 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
19145 parser_lex(parser);
19146
19147 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
19148 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
19149 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
19150
19151 current = UP(interpolated);
19152 } else {
19153 assert(false && "unreachable");
19154 }
19155
19156 break;
19157 }
19158 case PM_TOKEN_EMBVAR: {
19159 bool start_location_set = false;
19160 if (current == NULL) {
19161 /* If we hit an embedded variable and the current node is
19162 * NULL, then this is the start of a new string. We'll set
19163 * the current node to a new interpolated string. */
19164 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
19165 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19166 /* If we hit an embedded variable and the current node is a
19167 * string node, then we'll convert the current into an
19168 * interpolated string and add the string node to the list
19169 * of parts. */
19170 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
19171
19172 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
19173 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
19174 PM_NODE_START_SET_NODE(interpolated, current);
19175 start_location_set = true;
19176 current = UP(interpolated);
19177 } else {
19178 /* If we hit an embedded variable and the current node is an
19179 * interpolated string, then we'll just add the embedded
19180 * variable. */
19181 }
19182
19183 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19184 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
19185 if (!start_location_set) {
19186 PM_NODE_START_SET_NODE(current, part);
19187 }
19188 break;
19189 }
19190 case PM_TOKEN_EMBEXPR_BEGIN: {
19191 bool start_location_set = false;
19192 if (current == NULL) {
19193 /* If we hit an embedded expression and the current node is
19194 * NULL, then this is the start of a new string. We'll set
19195 * the current node to a new interpolated string. */
19196 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
19197 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19198 /* If we hit an embedded expression and the current node is
19199 * a string node, then we'll convert the current into an
19200 * interpolated string and add the string node to the list
19201 * of parts. */
19202 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
19203
19204 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
19205 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
19206 PM_NODE_START_SET_NODE(interpolated, current);
19207 start_location_set = true;
19208 current = UP(interpolated);
19209 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
19210 /* If we hit an embedded expression and the current node is
19211 * an interpolated string, then we'll just continue on. */
19212 } else {
19213 assert(false && "unreachable");
19214 }
19215
19216 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19217 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
19218 if (!start_location_set) {
19219 PM_NODE_START_SET_NODE(current, part);
19220 }
19221 break;
19222 }
19223 default:
19224 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_UPPER_ELEMENT);
19225 parser_lex(parser);
19226 break;
19227 }
19228 }
19229
19230 /* If we have a current node, then we need to append it to the list. */
19231 if (current) {
19232 pm_array_node_elements_append(parser->arena, array, current);
19233 }
19234
19235 pm_token_t closing = parser->current;
19236 if (match1(parser, PM_TOKEN_EOF)) {
19237 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_UPPER_TERM);
19238 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19239 } else {
19240 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM);
19241 }
19242 pm_array_node_close_set(parser, array, &closing);
19243
19244 return UP(array);
19245}
19246
19251static pm_node_t *
19252parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
19253 pm_token_t opening = parser->current;
19254 pm_node_flags_t paren_flags = 0;
19255
19256 pm_node_list_t current_block_exits = { 0 };
19257 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19258
19259 parser_lex(parser);
19260 while (true) {
19261 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19262 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19263 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19264 break;
19265 }
19266 }
19267
19268 /* If this is the end of the file or we match a right parenthesis, then we
19269 * have an empty parentheses node, and we can immediately return. */
19270 if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
19271 /* A command argument group sets EXPR_ENDARG before its ')' is
19272 * consumed, even when the group is empty, so that a following '{' is
19273 * scanned as a block brace. */
19274 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT) && opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19275 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19276 }
19277
19278 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19279 pop_block_exits(parser, previous_block_exits);
19280 return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, paren_flags));
19281 }
19282
19283 /* Otherwise, we're going to parse the first statement in the list of
19284 * statements within the parentheses. */
19285 context_push(parser, PM_CONTEXT_PARENS);
19286 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));
19287 context_pop(parser);
19288
19289 /* Determine if this statement is followed by a terminator. In the case of a
19290 * single statement, this is fine. But in the case of multiple statements
19291 * it's required. */
19292 bool terminator_found = false;
19293
19294 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19295 terminator_found = true;
19296 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19297 } else if (accept1(parser, PM_TOKEN_NEWLINE)) {
19298 terminator_found = true;
19299 }
19300
19301 if (terminator_found) {
19302 while (true) {
19303 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19304 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19305 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19306 break;
19307 }
19308 }
19309 }
19310
19311 /* If we hit a right parenthesis, then we're done parsing the parentheses
19312 * node, and we can check which kind of node we should return. */
19313 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19314 if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19315 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19316 }
19317
19318 parser_lex(parser);
19319 pop_block_exits(parser, previous_block_exits);
19320
19321 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19322 /* If we have a single statement and are ending on a right
19323 * parenthesis, then we need to check if this is possibly a multiple
19324 * target node. */
19325 pm_multi_target_node_t *multi_target;
19326
19327 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) && ((pm_multi_target_node_t *) statement)->lparen_loc.length == 0) {
19328 multi_target = (pm_multi_target_node_t *) statement;
19329 } else {
19330 multi_target = pm_multi_target_node_create(parser);
19331 pm_multi_target_node_targets_append(parser, multi_target, statement);
19332 }
19333
19334 multi_target->lparen_loc = TOK2LOC(parser, &opening);
19335 multi_target->rparen_loc = TOK2LOC(parser, &parser->previous);
19336 PM_NODE_START_SET_TOKEN(parser, multi_target, &opening);
19337 PM_NODE_LENGTH_SET_TOKEN(parser, multi_target, &parser->previous);
19338
19339 pm_node_t *result;
19340 if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) {
19341 result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19342 accept1(parser, PM_TOKEN_NEWLINE);
19343 } else {
19344 result = UP(multi_target);
19345 }
19346
19347 if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
19348 /* All set, this is explicitly allowed by the parent context. */
19349 } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match2(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_COMMA)) {
19350 /* All set, we're inside a for loop and we're parsing multiple
19351 * targets. A comma continues the index target list, as in
19352 * `for (a, b), c in ...`. */
19353 } else if (flags & PM_PARSE_ACCEPTS_STATEMENT) {
19354 /* The rescue-modifier value parser promotes this target on a
19355 * following `=` or comma. Reject any other binary operator that
19356 * would otherwise consume the target list (e.g. `(a, b) + c`). */
19357 if (pm_binding_powers[parser->current.type].binary && !match1(parser, PM_TOKEN_EQUAL)) {
19358 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19359 }
19360 } else if (binding_power != PM_BINDING_POWER_STATEMENT) {
19361 /* Multi targets are not allowed when it's not a statement
19362 * level. */
19363 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19364 } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
19365 /* Multi targets must be followed by an equal sign in order to
19366 * be valid (or a right parenthesis if they are nested). */
19367 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19368 }
19369
19370 return result;
19371 }
19372
19373 /* If we have a single statement and are ending on a right parenthesis
19374 * and we didn't return a multiple assignment node, then we can return a
19375 * regular parentheses node now. */
19376 pm_statements_node_t *statements = pm_statements_node_create(parser);
19377 pm_statements_node_body_append(parser, statements, statement, true);
19378
19379 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19380 }
19381
19382 /* If we have more than one statement in the set of parentheses, then we are
19383 * going to parse all of them as a list of statements. We'll do that here.
19384 */
19385 context_push(parser, PM_CONTEXT_PARENS);
19386 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19387
19388 pm_statements_node_t *statements = pm_statements_node_create(parser);
19389 pm_statements_node_body_append(parser, statements, statement, true);
19390
19391 /* If we didn't find a terminator and we didn't find a right parenthesis,
19392 * then this is a syntax error. */
19393 if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) {
19394 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19395 }
19396
19397 /* Parse each statement within the parentheses. */
19398 while (true) {
19399 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));
19400 pm_statements_node_body_append(parser, statements, node, true);
19401
19402 /* If we're recovering from a syntax error, then we need to stop parsing
19403 * the statements now. */
19404 if (parser->recovering) {
19405 /* If this is the level of context where the recovery has happened,
19406 * then we can mark the parser as done recovering. */
19407 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false;
19408 break;
19409 }
19410
19411 /* If we couldn't parse an expression at all, then we need to bail out
19412 * of the loop. */
19413 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) break;
19414
19415 /* If we successfully parsed a statement, then we are going to need a
19416 * terminator to delimit them. */
19417 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
19418 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
19419 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break;
19420 } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19421 break;
19422 } else if (!match1(parser, PM_TOKEN_EOF)) {
19423 /* If we're at the end of the file, then we're going to add an error
19424 * after this for the ) anyway. */
19425 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19426 }
19427 }
19428
19429 context_pop(parser);
19430 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19431
19432 /* When we're parsing multi targets, we allow them to be followed by a right
19433 * parenthesis if they are at the statement level. This is only possible if
19434 * they are the final statement in a parentheses. We need to explicitly
19435 * reject that here. */
19436 {
19437 pm_node_t *statement = statements->body.nodes[statements->body.size - 1];
19438
19439 if (PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19440 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
19441 pm_multi_target_node_targets_append(parser, multi_target, statement);
19442
19443 statement = UP(multi_target);
19444 statements->body.nodes[statements->body.size - 1] = statement;
19445 }
19446
19447 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) {
19448 const uint8_t *offset = parser->start + PM_NODE_END(statement);
19449 pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset };
19450 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(statement), 0));
19451
19452 statement = UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value));
19453 statements->body.nodes[statements->body.size - 1] = statement;
19454
19455 pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED);
19456 }
19457 }
19458
19459 pop_block_exits(parser, previous_block_exits);
19460 pm_void_statements_check(parser, statements, true);
19461 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19462}
19463
19469static pm_node_t *
19470parse_splat(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19471 pm_token_t operator = parser->previous;
19472 pm_node_t *name = NULL;
19473
19474 if (token_begins_expression_p(parser->current.type)) {
19475 name = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
19476 }
19477
19478 return UP(pm_splat_node_create(parser, &operator, name));
19479}
19480
19484static PRISM_INLINE pm_node_t *
19485parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
19486 switch (parser->current.type) {
19487 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
19488 parser_lex(parser);
19489
19490 pm_array_node_t *array = pm_array_node_create(parser, &parser->previous);
19491 bool parsed_bare_hash = false;
19492
19493 while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
19494 bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
19495
19496 // Handle the case where we don't have a comma and we have a
19497 // newline followed by a right bracket.
19498 if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19499 break;
19500 }
19501
19502 // Ensure that we have a comma between elements in the array.
19503 if (array->elements.size > 0) {
19504 if (accept1(parser, PM_TOKEN_COMMA)) {
19505 // If there was a comma but we also accepts a newline,
19506 // then this is a syntax error.
19507 if (accepted_newline) {
19508 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
19509 }
19510 } else {
19511 // If there was no comma, then we need to add a syntax
19512 // error.
19513 PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_END(parser, &parser->previous), 0, PM_ERR_ARRAY_SEPARATOR, pm_token_str(parser->current.type));
19514 parser->previous.start = parser->previous.end;
19515 parser->previous.type = 0;
19516 }
19517 }
19518
19519 // If we have a right bracket immediately following a comma,
19520 // this is allowed since it's a trailing comma. In this case we
19521 // can break out of the loop.
19522 if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break;
19523
19524 pm_node_t *element;
19525
19526 if (accept1(parser, PM_TOKEN_USTAR)) {
19527 pm_token_t operator = parser->previous;
19528 pm_node_t *expression = NULL;
19529
19530 if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) {
19531 pm_parser_scope_forwarding_positionals_check(parser, &operator);
19532 } else {
19533 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));
19534 }
19535
19536 element = UP(pm_splat_node_create(parser, &operator, expression));
19537 } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) {
19538 if (parsed_bare_hash) {
19539 pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH);
19540 }
19541
19542 element = UP(pm_keyword_hash_node_create(parser));
19543 pm_static_literals_t hash_keys = { 0 };
19544
19545 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)) {
19546 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
19547 }
19548
19549 pm_static_literals_free(&hash_keys);
19550 parsed_bare_hash = true;
19551 } else {
19552 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));
19553
19554 if (pm_symbol_node_label_p(parser, element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
19555 if (parsed_bare_hash) {
19556 pm_parser_err_previous(parser, PM_ERR_EXPRESSION_BARE_HASH);
19557 }
19558
19559 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
19560 pm_static_literals_t hash_keys = { 0 };
19561 pm_hash_key_static_literals_add(parser, &hash_keys, element);
19562
19563 pm_token_t operator = { 0 };
19564 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
19565 operator = parser->previous;
19566 }
19567
19568 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));
19569 pm_node_t *assoc = UP(pm_assoc_node_create(parser, element, NTOK2PTR(operator), value));
19570 pm_keyword_hash_node_elements_append(parser->arena, hash, assoc);
19571
19572 element = UP(hash);
19573 if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19574 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
19575 }
19576
19577 pm_static_literals_free(&hash_keys);
19578 parsed_bare_hash = true;
19579 }
19580 }
19581
19582 pm_array_node_elements_append(parser->arena, array, element);
19583 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
19584 }
19585
19586 accept1(parser, PM_TOKEN_NEWLINE);
19587
19588 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19589 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
19590 parser->previous.start = parser->previous.end;
19591 parser->previous.type = 0;
19592 }
19593
19594 pm_array_node_close_set(parser, array, &parser->previous);
19595
19596 return UP(array);
19597 }
19598 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
19599 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
19600 return parse_parentheses(parser, binding_power, flags, depth);
19601 case PM_TOKEN_BRACE_LEFT_HASH: {
19602 parser_lex(parser);
19603
19604 pm_token_t opening = parser->previous;
19605 pm_hash_node_t *node = pm_hash_node_create(parser, &opening);
19606
19607 if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
19608 pm_static_literals_t hash_keys = { 0 };
19609 parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1));
19610 pm_static_literals_free(&hash_keys);
19611
19612 accept1(parser, PM_TOKEN_NEWLINE);
19613 }
19614
19615 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening);
19616 pm_hash_node_closing_loc_set(parser, node, &parser->previous);
19617
19618 return UP(node);
19619 }
19620 case PM_TOKEN_CHARACTER_LITERAL: {
19621 pm_node_t *node = UP(pm_string_node_create_current_string(
19622 parser,
19623 &(pm_token_t) {
19624 .type = PM_TOKEN_STRING_BEGIN,
19625 .start = parser->current.start,
19626 .end = parser->current.start + 1
19627 },
19628 &(pm_token_t) {
19629 .type = PM_TOKEN_STRING_CONTENT,
19630 .start = parser->current.start + 1,
19631 .end = parser->current.end
19632 },
19633 NULL
19634 ));
19635
19636 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
19637
19638 // Skip past the character literal here, since now we have handled
19639 // parser->explicit_encoding correctly.
19640 parser_lex(parser);
19641
19642 // Characters can be followed by strings in which case they are
19643 // automatically concatenated.
19644 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
19645 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
19646 }
19647
19648 return node;
19649 }
19650 case PM_TOKEN_CLASS_VARIABLE: {
19651 parser_lex(parser);
19652 pm_node_t *node = UP(pm_class_variable_read_node_create(parser, &parser->previous));
19653
19654 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19655 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19656 }
19657
19658 return node;
19659 }
19660 case PM_TOKEN_CONSTANT: {
19661 parser_lex(parser);
19662 pm_token_t constant = parser->previous;
19663
19664 // If a constant is immediately followed by parentheses, then this is in
19665 // fact a method call, not a constant read.
19666 if (
19667 match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
19668 ((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))) ||
19669 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
19670 match1(parser, PM_TOKEN_BRACE_LEFT)
19671 ) {
19672 pm_arguments_t arguments = { 0 };
19673 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
19674 return UP(pm_call_node_fcall_create(parser, &constant, &arguments));
19675 }
19676
19677 pm_node_t *node = UP(pm_constant_read_node_create(parser, &parser->previous));
19678
19679 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19680 // If we get here, then we have a comma immediately following a
19681 // constant, so we're going to parse this as a multiple assignment.
19682 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19683 }
19684
19685 return node;
19686 }
19687 case PM_TOKEN_UCOLON_COLON: {
19688 parser_lex(parser);
19689 pm_token_t delimiter = parser->previous;
19690
19691 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
19692 pm_node_t *node = UP(pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous));
19693
19694 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19695 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19696 }
19697
19698 return node;
19699 }
19700 case PM_TOKEN_UDOT_DOT:
19701 case PM_TOKEN_UDOT_DOT_DOT: {
19702 pm_token_t operator = parser->current;
19703 parser_lex(parser);
19704
19705 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));
19706
19707 // Unary .. and ... are special because these are non-associative
19708 // operators that can also be unary operators. In this case we need
19709 // to explicitly reject code that has a .. or ... that follows this
19710 // expression.
19711 if (match2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
19712 pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
19713 }
19714
19715 return UP(pm_range_node_create(parser, NULL, &operator, right));
19716 }
19717 case PM_TOKEN_FLOAT:
19718 parser_lex(parser);
19719 return UP(pm_float_node_create(parser, &parser->previous));
19720 case PM_TOKEN_FLOAT_IMAGINARY:
19721 parser_lex(parser);
19722 return UP(pm_float_node_imaginary_create(parser, &parser->previous));
19723 case PM_TOKEN_FLOAT_RATIONAL:
19724 parser_lex(parser);
19725 return UP(pm_float_node_rational_create(parser, &parser->previous));
19726 case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY:
19727 parser_lex(parser);
19728 return UP(pm_float_node_rational_imaginary_create(parser, &parser->previous));
19729 case PM_TOKEN_NUMBERED_REFERENCE: {
19730 parser_lex(parser);
19731 pm_node_t *node = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
19732
19733 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19734 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19735 }
19736
19737 return node;
19738 }
19739 case PM_TOKEN_GLOBAL_VARIABLE: {
19740 parser_lex(parser);
19741 pm_node_t *node = UP(pm_global_variable_read_node_create(parser, &parser->previous));
19742
19743 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19744 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19745 }
19746
19747 return node;
19748 }
19749 case PM_TOKEN_BACK_REFERENCE: {
19750 parser_lex(parser);
19751 pm_node_t *node = UP(pm_back_reference_read_node_create(parser, &parser->previous));
19752
19753 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19754 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19755 }
19756
19757 return node;
19758 }
19759 case PM_TOKEN_IDENTIFIER:
19760 case PM_TOKEN_METHOD_NAME: {
19761 parser_lex(parser);
19762 pm_token_t identifier = parser->previous;
19763 pm_node_t *node = parse_variable_call(parser);
19764
19765 if (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
19766 // If parse_variable_call returned with a call node, then we
19767 // know the identifier is not in the local table. In that case
19768 // we need to check if there are arguments following the
19769 // identifier.
19770 pm_call_node_t *call = (pm_call_node_t *) node;
19771 pm_arguments_t arguments = { 0 };
19772
19773 if (parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1))) {
19774 // Since we found arguments, we need to turn off the
19775 // variable call bit in the flags.
19776 pm_node_flag_unset(UP(call), PM_CALL_NODE_FLAGS_VARIABLE_CALL);
19777
19778 call->opening_loc = arguments.opening_loc;
19779 call->arguments = arguments.arguments;
19780 call->closing_loc = arguments.closing_loc;
19781 call->block = arguments.block;
19782
19783 const pm_location_t *end = pm_arguments_end(&arguments);
19784 if (end == NULL) {
19785 PM_NODE_LENGTH_SET_LOCATION(call, &call->message_loc);
19786 } else {
19787 PM_NODE_LENGTH_SET_LOCATION(call, end);
19788 }
19789 }
19790 } else {
19791 // Otherwise, we know the identifier is in the local table. This
19792 // can still be a method call if it is followed by arguments or
19793 // a block, so we need to check for that here.
19794 if (
19795 ((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))) ||
19796 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
19797 match1(parser, PM_TOKEN_BRACE_LEFT)
19798 ) {
19799 pm_arguments_t arguments = { 0 };
19800 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
19801 pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments);
19802
19803 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
19804 // If we're about to convert an 'it' implicit local
19805 // variable read into a method call, we need to remove
19806 // it from the list of implicit local variables.
19807 pm_node_unreference(parser, node);
19808 } else {
19809 // Otherwise, we're about to convert a regular local
19810 // variable read into a method call, in which case we
19811 // need to indicate that this was not a read for the
19812 // purposes of warnings.
19813 assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE));
19814
19815 if (pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &identifier), PM_TOKEN_LENGTH(&identifier))) {
19816 pm_node_unreference(parser, node);
19817 } else {
19819 pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
19820 }
19821 }
19822
19823 return UP(fcall);
19824 }
19825 }
19826
19827 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19828 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19829 }
19830
19831 return node;
19832 }
19833 case PM_TOKEN_HEREDOC_START: {
19834 // Here we have found a heredoc. We'll parse it and add it to the
19835 // list of strings.
19836 assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC);
19837 pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base;
19838
19839 size_t common_whitespace = (size_t) -1;
19840 parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace;
19841
19842 parser_lex(parser);
19843 pm_token_t opening = parser->previous;
19844
19845 pm_node_t *node;
19846 pm_node_t *part;
19847
19848 if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19849 // If we get here, then we have an empty heredoc. We'll create
19850 // an empty content token and return an empty string node.
19851 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19852 pm_token_t content = parse_strings_empty_content(parser->previous.start);
19853
19854 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19855 node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19856 } else {
19857 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19858 }
19859
19860 PM_NODE_LENGTH_SET_TOKEN(parser, node, &opening);
19861 } else if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) == NULL) {
19862 // If we get here, then we tried to find something in the
19863 // heredoc but couldn't actually parse anything, so we'll just
19864 // return a missing node.
19865 //
19866 // parse_string_part handles its own errors, so there is no need
19867 // for us to add one here.
19868 node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
19869 } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19870 // If we get here, then the part that we parsed was plain string
19871 // content and we're at the end of the heredoc, so we can return
19872 // just a string node with the heredoc opening and closing as
19873 // its opening and closing.
19874 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
19875 pm_string_node_t *cast = (pm_string_node_t *) part;
19876
19877 cast->opening_loc = TOK2LOC(parser, &opening);
19878 cast->closing_loc = TOK2LOC(parser, &parser->current);
19879 cast->base.location = cast->opening_loc;
19880
19881 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19882 assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
19883 cast->base.type = PM_X_STRING_NODE;
19884 }
19885
19886 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19887 parse_heredoc_dedent_string(parser->arena, &cast->unescaped, common_whitespace);
19888 }
19889
19890 node = UP(cast);
19891 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19892 } else {
19893 // If we get here, then we have multiple parts in the heredoc,
19894 // so we'll need to create an interpolated string node to hold
19895 // them all.
19896 pm_node_list_t parts = { 0 };
19897 pm_node_list_append(parser->arena, &parts, part);
19898
19899 while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19900 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19901 pm_node_list_append(parser->arena, &parts, part);
19902 }
19903 }
19904
19905 // Now that we have all of the parts, create the correct type of
19906 // interpolated node.
19907 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19908 pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19909 cast->parts = parts;
19910
19911 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19912 pm_interpolated_xstring_node_closing_set(parser, cast, &parser->previous);
19913
19914 cast->base.location = cast->opening_loc;
19915 node = UP(cast);
19916 } else {
19917 pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
19918
19919 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19920 pm_interpolated_string_node_closing_set(parser, cast, &parser->previous);
19921
19922 cast->base.location = cast->opening_loc;
19923 node = UP(cast);
19924 }
19925
19926 // If this is a heredoc that is indented with a ~, then we need
19927 // to dedent each line by the common leading whitespace.
19928 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19929 pm_node_list_t *nodes;
19930 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19931 nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
19932 } else {
19933 nodes = &((pm_interpolated_string_node_t *) node)->parts;
19934 }
19935
19936 parse_heredoc_dedent(parser, nodes, common_whitespace);
19937 }
19938 }
19939
19940 /* If a missing terminator left this heredoc's lex mode on the
19941 * stack, it still points at our stack-local common_whitespace.
19942 * Clear the pointer so that subsequent lexing cannot read from
19943 * this function's dead stack frame. */
19944 pm_lex_mode_t *whitespace_mode = parser->lex_modes.current;
19945 do {
19946 if (whitespace_mode->mode == PM_LEX_HEREDOC && whitespace_mode->as.heredoc.common_whitespace == &common_whitespace) {
19947 whitespace_mode->as.heredoc.common_whitespace = NULL;
19948 }
19949 whitespace_mode = whitespace_mode->prev;
19950 } while (whitespace_mode != NULL);
19951
19952 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
19953 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
19954 }
19955
19956 return node;
19957 }
19958 case PM_TOKEN_INSTANCE_VARIABLE: {
19959 parser_lex(parser);
19960 pm_node_t *node = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
19961
19962 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19963 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19964 }
19965
19966 return node;
19967 }
19968 case PM_TOKEN_INTEGER: {
19969 pm_node_flags_t base = parser->integer.base;
19970 parser_lex(parser);
19971 return UP(pm_integer_node_create(parser, base, &parser->previous));
19972 }
19973 case PM_TOKEN_INTEGER_IMAGINARY: {
19974 pm_node_flags_t base = parser->integer.base;
19975 parser_lex(parser);
19976 return UP(pm_integer_node_imaginary_create(parser, base, &parser->previous));
19977 }
19978 case PM_TOKEN_INTEGER_RATIONAL: {
19979 pm_node_flags_t base = parser->integer.base;
19980 parser_lex(parser);
19981 return UP(pm_integer_node_rational_create(parser, base, &parser->previous));
19982 }
19983 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: {
19984 pm_node_flags_t base = parser->integer.base;
19985 parser_lex(parser);
19986 return UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous));
19987 }
19988 case PM_TOKEN_KEYWORD___ENCODING__:
19989 parser_lex(parser);
19990 return UP(pm_source_encoding_node_create(parser, &parser->previous));
19991 case PM_TOKEN_KEYWORD___FILE__:
19992 parser_lex(parser);
19993 return UP(pm_source_file_node_create(parser, &parser->previous));
19994 case PM_TOKEN_KEYWORD___LINE__:
19995 parser_lex(parser);
19996 return UP(pm_source_line_node_create(parser, &parser->previous));
19997 case PM_TOKEN_KEYWORD_ALIAS: {
19998 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
19999 pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS);
20000 }
20001
20002 parser_lex(parser);
20003 pm_token_t keyword = parser->previous;
20004
20005 pm_node_t *new_name = parse_alias_argument(parser, true, (uint16_t) (depth + 1));
20006 pm_node_t *old_name = parse_alias_argument(parser, false, (uint16_t) (depth + 1));
20007
20008 switch (PM_NODE_TYPE(new_name)) {
20009 case PM_BACK_REFERENCE_READ_NODE:
20010 case PM_NUMBERED_REFERENCE_READ_NODE:
20011 case PM_GLOBAL_VARIABLE_READ_NODE: {
20012 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)) {
20013 if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) {
20014 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE);
20015 }
20016 } else if (!PM_NODE_TYPE_P(old_name, PM_ERROR_RECOVERY_NODE)) {
20017 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
20018 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
20019 }
20020
20021 return UP(pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name));
20022 }
20023 case PM_SYMBOL_NODE:
20024 case PM_INTERPOLATED_SYMBOL_NODE: {
20025 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)) {
20026 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
20027 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
20028 }
20029 }
20031 default:
20032 return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name));
20033 }
20034 }
20035 case PM_TOKEN_KEYWORD_CASE:
20036 return parse_case(parser, flags, depth);
20037 case PM_TOKEN_KEYWORD_BEGIN: {
20038 size_t opening_newline_index = token_newline_index(parser);
20039 parser_lex(parser);
20040
20041 pm_token_t begin_keyword = parser->previous;
20042 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20043
20044 pm_node_list_t current_block_exits = { 0 };
20045 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
20046 pm_statements_node_t *begin_statements = NULL;
20047
20048 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
20049 pm_accepts_block_stack_push(parser, true);
20050 begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN, (uint16_t) (depth + 1));
20051 pm_accepts_block_stack_pop(parser);
20052 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20053 }
20054
20055 pm_begin_node_t *begin_node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
20056 parse_rescues(parser, opening_newline_index, &begin_keyword, begin_node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1));
20057 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword);
20058
20059 PM_NODE_LENGTH_SET_TOKEN(parser, begin_node, &parser->previous);
20060 pm_begin_node_end_keyword_set(parser, begin_node, &parser->previous);
20061 pop_block_exits(parser, previous_block_exits);
20062 return UP(begin_node);
20063 }
20064 case PM_TOKEN_KEYWORD_BEGIN_UPCASE: {
20065 pm_node_list_t current_block_exits = { 0 };
20066 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
20067
20068 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20069 pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
20070 }
20071
20072 parser_lex(parser);
20073 pm_token_t keyword = parser->previous;
20074
20075 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_BEGIN_UPCASE_BRACE);
20076 pm_token_t opening = parser->previous;
20077 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE, (uint16_t) (depth + 1));
20078
20079 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening);
20080 pm_context_t context = parser->current_context->context;
20081 if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
20082 pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
20083 }
20084
20085 flush_block_exits(parser, previous_block_exits);
20086 return UP(pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous));
20087 }
20088 case PM_TOKEN_KEYWORD_BREAK:
20089 case PM_TOKEN_KEYWORD_NEXT:
20090 case PM_TOKEN_KEYWORD_RETURN: {
20091 parser_lex(parser);
20092
20093 pm_token_t keyword = parser->previous;
20094 pm_arguments_t arguments = { 0 };
20095
20096 if (
20097 token_begins_expression_p(parser->current.type) ||
20098 match2(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)
20099 ) {
20100 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
20101
20102 if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
20103 pm_token_t next = parser->current;
20104 parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
20105
20106 // Reject `foo && return bar`.
20107 if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) {
20108 PM_PARSER_ERR_TOKEN_FORMAT(parser, &next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(next.type));
20109 }
20110
20111 // Reject a trailing comma, e.g. `return a,`. The arguments
20112 // parser silently accepts a trailing comma only when it is
20113 // immediately followed by the EOF terminator; in every other
20114 // case (e.g. `return a,;`) it reports the dangling comma
20115 // itself. We reject the accepted case here to stay in line
20116 // with the command call argument parsing above.
20117 if (parser->previous.type == PM_TOKEN_COMMA && match1(parser, PM_TOKEN_EOF)) {
20118 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
20119 }
20120 }
20121
20122 // It's possible that we've parsed a block argument through our
20123 // call to parse_arguments. If we found one, we should mark it
20124 // as invalid and destroy it, as we don't have a place for it.
20125 if (arguments.block != NULL) {
20126 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20127 pm_node_unreference(parser, arguments.block);
20128 arguments.block = NULL;
20129 }
20130 }
20131
20132 switch (keyword.type) {
20133 case PM_TOKEN_KEYWORD_BREAK: {
20134 pm_node_t *node = UP(pm_break_node_create(parser, &keyword, arguments.arguments));
20135 if (!parser->partial_script) parse_block_exit(parser, node);
20136 return node;
20137 }
20138 case PM_TOKEN_KEYWORD_NEXT: {
20139 pm_node_t *node = UP(pm_next_node_create(parser, &keyword, arguments.arguments));
20140 if (!parser->partial_script) parse_block_exit(parser, node);
20141 return node;
20142 }
20143 case PM_TOKEN_KEYWORD_RETURN: {
20144 pm_node_t *node = UP(pm_return_node_create(parser, &keyword, arguments.arguments));
20145 parse_return(parser, node);
20146 return node;
20147 }
20148 default:
20149 assert(false && "unreachable");
20150 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20151 }
20152 }
20153 case PM_TOKEN_KEYWORD_SUPER: {
20154 parser_lex(parser);
20155
20156 pm_token_t keyword = parser->previous;
20157 pm_arguments_t arguments = { 0 };
20158 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20159
20160 if (
20161 arguments.opening_loc.length == 0 &&
20162 arguments.arguments == NULL &&
20163 ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE))
20164 ) {
20165 return UP(pm_forwarding_super_node_create(parser, &keyword, &arguments));
20166 }
20167
20168 return UP(pm_super_node_create(parser, &keyword, &arguments));
20169 }
20170 case PM_TOKEN_KEYWORD_YIELD: {
20171 parser_lex(parser);
20172
20173 pm_token_t keyword = parser->previous;
20174 pm_arguments_t arguments = { 0 };
20175 parse_arguments_list(parser, &arguments, false, flags, (uint16_t) (depth + 1));
20176
20177 // It's possible that we've parsed a block argument through our
20178 // call to parse_arguments_list. If we found one, we should mark it
20179 // as invalid and destroy it, as we don't have a place for it on the
20180 // yield node.
20181 if (arguments.block != NULL) {
20182 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20183 pm_node_unreference(parser, arguments.block);
20184 arguments.block = NULL;
20185 }
20186
20187 pm_node_t *node = UP(pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc));
20188 if (!parser->parsing_eval && !parser->partial_script) parse_yield(parser, node);
20189
20190 return node;
20191 }
20192 case PM_TOKEN_KEYWORD_CLASS:
20193 return parse_class(parser, flags, depth);
20194 case PM_TOKEN_KEYWORD_DEF:
20195 return parse_def(parser, binding_power, flags, depth);
20196 case PM_TOKEN_KEYWORD_DEFINED: {
20197 parser_lex(parser);
20198
20199 pm_token_t keyword = parser->previous;
20200 pm_token_t lparen = { 0 };
20201 pm_token_t rparen = { 0 };
20202 pm_node_t *expression;
20203
20204 context_push(parser, PM_CONTEXT_DEFINED);
20205 bool newline = accept1(parser, PM_TOKEN_NEWLINE);
20206
20207 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
20208 lparen = parser->previous;
20209
20210 if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20211 expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
20212 lparen = (pm_token_t) { 0 };
20213 } else {
20214 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));
20215
20216 if (!parser->recovering) {
20217 accept1(parser, PM_TOKEN_NEWLINE);
20218 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20219 rparen = parser->previous;
20220 }
20221 }
20222 } else {
20223 expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
20224 }
20225
20226 context_pop(parser);
20227 return UP(pm_defined_node_create(
20228 parser,
20229 NTOK2PTR(lparen),
20230 expression,
20231 NTOK2PTR(rparen),
20232 &keyword
20233 ));
20234 }
20235 case PM_TOKEN_KEYWORD_END_UPCASE: {
20236 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20237 pm_parser_err_current(parser, PM_ERR_STATEMENT_POSTEXE_END);
20238 }
20239
20240 parser_lex(parser);
20241 pm_token_t keyword = parser->previous;
20242
20243 if (context_def_p(parser)) {
20244 pm_parser_warn_token(parser, &keyword, PM_WARN_END_IN_METHOD);
20245 }
20246
20247 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_END_UPCASE_BRACE);
20248 pm_token_t opening = parser->previous;
20249 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE, (uint16_t) (depth + 1));
20250
20251 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening);
20252 return UP(pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous));
20253 }
20254 case PM_TOKEN_KEYWORD_FALSE:
20255 parser_lex(parser);
20256 return UP(pm_false_node_create(parser, &parser->previous));
20257 case PM_TOKEN_KEYWORD_FOR: {
20258 size_t opening_newline_index = token_newline_index(parser);
20259 parser_lex(parser);
20260
20261 pm_token_t for_keyword = parser->previous;
20262 pm_node_t *index;
20263
20264 context_push(parser, PM_CONTEXT_FOR_INDEX);
20265
20266 // First, parse out the first index expression.
20267 if (accept1(parser, PM_TOKEN_USTAR)) {
20268 index = parse_splat(parser, flags, depth);
20269 } else if (token_begins_expression_p(parser->current.type)) {
20270 index = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
20271 } else {
20272 pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX);
20273 index = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &for_keyword), PM_TOKEN_LENGTH(&for_keyword)));
20274 }
20275
20276 // Now, if there are multiple index expressions, parse them out.
20277 if (match1(parser, PM_TOKEN_COMMA)) {
20278 index = parse_targets(parser, index, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20279 } else {
20280 index = parse_target(parser, index, false, false);
20281 }
20282
20283 context_pop(parser);
20284 pm_do_loop_stack_push(parser, true);
20285
20286 expect1(parser, PM_TOKEN_KEYWORD_IN, PM_ERR_FOR_IN);
20287 pm_token_t in_keyword = parser->previous;
20288
20289 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));
20290 pm_do_loop_stack_pop(parser);
20291
20292 pm_token_t do_keyword = { 0 };
20293 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20294 do_keyword = parser->previous;
20295 } else {
20296 if (!match2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)) {
20297 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_str(parser->current.type));
20298 }
20299 }
20300
20301 pm_statements_node_t *statements = NULL;
20302 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20303 statements = parse_statements(parser, PM_CONTEXT_FOR, (uint16_t) (depth + 1));
20304 }
20305
20306 parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false);
20307 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword);
20308
20309 return UP(pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, NTOK2PTR(do_keyword), &parser->previous));
20310 }
20311 case PM_TOKEN_KEYWORD_IF:
20312 if (parser_end_of_line_p(parser)) {
20313 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
20314 }
20315
20316 size_t opening_newline_index = token_newline_index(parser);
20317 bool if_after_else = parser->previous.type == PM_TOKEN_KEYWORD_ELSE;
20318 parser_lex(parser);
20319
20320 return parse_conditional(parser, PM_CONTEXT_IF, opening_newline_index, if_after_else, (uint16_t) (depth + 1));
20321 case PM_TOKEN_KEYWORD_UNDEF: {
20322 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20323 pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF);
20324 }
20325
20326 parser_lex(parser);
20327 pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous);
20328 pm_node_t *name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20329
20330 if (PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20331 } else {
20332 pm_undef_node_append(parser->arena, undef, name);
20333
20334 while (match1(parser, PM_TOKEN_COMMA)) {
20335 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
20336 parser_lex(parser);
20337 name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20338
20339 if (PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20340 break;
20341 }
20342
20343 pm_undef_node_append(parser->arena, undef, name);
20344 }
20345 }
20346
20347 return UP(undef);
20348 }
20349 case PM_TOKEN_KEYWORD_NOT: {
20350 parser_lex(parser);
20351
20352 pm_token_t message = parser->previous;
20353 pm_arguments_t arguments = { 0 };
20354 pm_node_t *receiver = NULL;
20355
20356 // The `not` keyword without parentheses is only valid in contexts
20357 // where it would be parsed as an expression (i.e., at or below
20358 // the `not` binding power level). In other contexts (e.g., method
20359 // arguments, array elements, assignment right-hand sides),
20360 // parentheses are required: `not(x)`. An exception is made for
20361 // endless def bodies, where `not` is valid as both `arg` and
20362 // `command` (e.g., `def f = not 1`, `def f = not foo bar`).
20363 if (binding_power > PM_BINDING_POWER_NOT && !(flags & PM_PARSE_IN_ENDLESS_DEF) && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
20364 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) {
20365 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->previous), 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN);
20366 } else {
20367 accept1(parser, PM_TOKEN_NEWLINE);
20368 pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER);
20369 }
20370
20371 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
20372 }
20373
20374 accept1(parser, PM_TOKEN_NEWLINE);
20375
20376 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
20377 pm_token_t lparen = parser->previous;
20378
20379 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20380 receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
20381 } else {
20382 arguments.opening_loc = TOK2LOC(parser, &lparen);
20383 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));
20384
20385 if (!parser->recovering) {
20386 accept1(parser, PM_TOKEN_NEWLINE);
20387 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20388 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
20389 }
20390 }
20391 } else {
20392 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));
20393 }
20394
20395 return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
20396 }
20397 case PM_TOKEN_KEYWORD_UNLESS: {
20398 size_t opening_newline_index = token_newline_index(parser);
20399 parser_lex(parser);
20400
20401 return parse_conditional(parser, PM_CONTEXT_UNLESS, opening_newline_index, false, (uint16_t) (depth + 1));
20402 }
20403 case PM_TOKEN_KEYWORD_MODULE:
20404 return parse_module(parser, flags, depth);
20405 case PM_TOKEN_KEYWORD_NIL:
20406 parser_lex(parser);
20407 return UP(pm_nil_node_create(parser, &parser->previous));
20408 case PM_TOKEN_KEYWORD_REDO: {
20409 parser_lex(parser);
20410
20411 pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous));
20412 if (!parser->partial_script) parse_block_exit(parser, node);
20413
20414 return node;
20415 }
20416 case PM_TOKEN_KEYWORD_RETRY: {
20417 parser_lex(parser);
20418
20419 pm_node_t *node = UP(pm_retry_node_create(parser, &parser->previous));
20420 parse_retry(parser, node);
20421
20422 return node;
20423 }
20424 case PM_TOKEN_KEYWORD_SELF:
20425 parser_lex(parser);
20426 return UP(pm_self_node_create(parser, &parser->previous));
20427 case PM_TOKEN_KEYWORD_TRUE:
20428 parser_lex(parser);
20429 return UP(pm_true_node_create(parser, &parser->previous));
20430 case PM_TOKEN_KEYWORD_UNTIL: {
20431 size_t opening_newline_index = token_newline_index(parser);
20432
20433 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
20434 pm_do_loop_stack_push(parser, true);
20435
20436 parser_lex(parser);
20437 pm_token_t keyword = parser->previous;
20438 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));
20439
20440 pm_do_loop_stack_pop(parser);
20441 context_pop(parser);
20442
20443 pm_token_t do_keyword = { 0 };
20444 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20445 do_keyword = parser->previous;
20446 } else {
20447 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
20448 }
20449
20450 pm_statements_node_t *statements = NULL;
20451 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20452 pm_accepts_block_stack_push(parser, true);
20453 statements = parse_statements(parser, PM_CONTEXT_UNTIL, (uint16_t) (depth + 1));
20454 pm_accepts_block_stack_pop(parser);
20455 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20456 }
20457
20458 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
20459 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword);
20460
20461 return UP(pm_until_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0));
20462 }
20463 case PM_TOKEN_KEYWORD_WHILE: {
20464 size_t opening_newline_index = token_newline_index(parser);
20465
20466 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
20467 pm_do_loop_stack_push(parser, true);
20468
20469 parser_lex(parser);
20470 pm_token_t keyword = parser->previous;
20471 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));
20472
20473 pm_do_loop_stack_pop(parser);
20474 context_pop(parser);
20475
20476 pm_token_t do_keyword = { 0 };
20477 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20478 do_keyword = parser->previous;
20479 } else {
20480 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
20481 }
20482
20483 pm_statements_node_t *statements = NULL;
20484 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20485 pm_accepts_block_stack_push(parser, true);
20486 statements = parse_statements(parser, PM_CONTEXT_WHILE, (uint16_t) (depth + 1));
20487 pm_accepts_block_stack_pop(parser);
20488 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20489 }
20490
20491 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
20492 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword);
20493
20494 return UP(pm_while_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0));
20495 }
20496 case PM_TOKEN_PERCENT_LOWER_I: {
20497 parser_lex(parser);
20498 pm_token_t opening = parser->previous;
20499 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20500 pm_node_t *current = NULL;
20501
20502 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20503 accept1(parser, PM_TOKEN_WORDS_SEP);
20504 if (match1(parser, PM_TOKEN_STRING_END)) break;
20505
20506 // Interpolation is not possible but nested heredocs can still lead to
20507 // consecutive (disjoint) string tokens when the final newline is escaped.
20508 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20509 // Record the string node, moving to interpolation if needed.
20510 if (current == NULL) {
20511 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
20512 parser_lex(parser);
20513 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20514 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20515 parser_lex(parser);
20516 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
20517 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20518 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
20519 pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start + cast->value_loc.start, .end = parser->start + cast->value_loc.start + cast->value_loc.length };
20520 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
20521 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
20522 parser_lex(parser);
20523
20524 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20525 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
20526 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
20527
20528 // current is arena-allocated so no explicit free is needed.
20529 current = UP(interpolated);
20530 } else {
20531 assert(false && "unreachable");
20532 }
20533 }
20534
20535 if (current) {
20536 pm_array_node_elements_append(parser->arena, array, current);
20537 current = NULL;
20538 } else {
20539 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT);
20540 }
20541 }
20542
20543 pm_token_t closing = parser->current;
20544 if (match1(parser, PM_TOKEN_EOF)) {
20545 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_LOWER_TERM);
20546 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20547 } else {
20548 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM);
20549 }
20550 pm_array_node_close_set(parser, array, &closing);
20551
20552 return UP(array);
20553 }
20554 case PM_TOKEN_PERCENT_UPPER_I:
20555 return parse_symbol_array(parser, depth);
20556 case PM_TOKEN_PERCENT_LOWER_W: {
20557 parser_lex(parser);
20558 pm_token_t opening = parser->previous;
20559 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20560 pm_node_t *current = NULL;
20561
20562 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20563 accept1(parser, PM_TOKEN_WORDS_SEP);
20564 if (match1(parser, PM_TOKEN_STRING_END)) break;
20565
20566 // Interpolation is not possible but nested heredocs can still lead to
20567 // consecutive (disjoint) string tokens when the final newline is escaped.
20568 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20569 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20570
20571 // Record the string node, moving to interpolation if needed.
20572 if (current == NULL) {
20573 current = string;
20574 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20575 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
20576 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20577 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20578 pm_interpolated_string_node_append(parser, interpolated, current);
20579 pm_interpolated_string_node_append(parser, interpolated, string);
20580 current = UP(interpolated);
20581 } else {
20582 assert(false && "unreachable");
20583 }
20584 parser_lex(parser);
20585 }
20586
20587 if (current) {
20588 pm_array_node_elements_append(parser->arena, array, current);
20589 current = NULL;
20590 } else {
20591 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT);
20592 }
20593 }
20594
20595 pm_token_t closing = parser->current;
20596 if (match1(parser, PM_TOKEN_EOF)) {
20597 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_LOWER_TERM);
20598 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20599 } else {
20600 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM);
20601 }
20602
20603 pm_array_node_close_set(parser, array, &closing);
20604 return UP(array);
20605 }
20606 case PM_TOKEN_PERCENT_UPPER_W:
20607 return parse_string_array(parser, depth);
20608 case PM_TOKEN_REGEXP_BEGIN: {
20609 pm_token_t opening = parser->current;
20610 parser_lex(parser);
20611
20612 if (match1(parser, PM_TOKEN_REGEXP_END)) {
20613 // If we get here, then we have an end immediately after a start. In
20614 // that case we'll create an empty content token and return an
20615 // uninterpolated regular expression.
20616 pm_token_t content = (pm_token_t) {
20617 .type = PM_TOKEN_STRING_CONTENT,
20618 .start = parser->previous.end,
20619 .end = parser->previous.end
20620 };
20621
20622 parser_lex(parser);
20623
20624 pm_regular_expression_node_t *node = pm_regular_expression_node_create(parser, &opening, &content, &parser->previous);
20625 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
20626 return UP(node);
20627 }
20628
20630
20631 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20632 // In this case we've hit string content so we know the regular
20633 // expression at least has something in it. We'll need to check if the
20634 // following token is the end (in which case we can return a plain
20635 // regular expression) or if it's not then it has interpolation.
20636 pm_string_t unescaped = parser->current_string;
20637 pm_token_t content = parser->current;
20638 parser_lex(parser);
20639
20640 // If we hit an end, then we can create a regular expression
20641 // node without interpolation, which can be represented more
20642 // succinctly and more easily compiled.
20643 if (accept1(parser, PM_TOKEN_REGEXP_END)) {
20644 pm_regular_expression_node_t *node = (pm_regular_expression_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
20645
20646 // If we're not immediately followed by a =~, then we
20647 // parse and validate now. If it is followed by a =~,
20648 // then it will get parsed in the =~ handler where
20649 // named captures can also be extracted.
20650 if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) {
20651 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
20652 }
20653
20654 return UP(node);
20655 }
20656
20657 // If we get here, then we have interpolation so we'll need to create
20658 // a regular expression node with interpolation.
20659 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
20660
20661 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
20662 if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
20663 // This is extremely strange, but the first string part of a
20664 // regular expression will always be tagged as binary if we
20665 // are in a US-ASCII file, no matter its contents.
20666 pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING);
20667 }
20668
20669 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
20670 } else {
20671 // If the first part of the body of the regular expression is not a
20672 // string content, then we have interpolation and we need to create an
20673 // interpolated regular expression node.
20674 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
20675 }
20676
20677 // Now that we're here and we have interpolation, we'll parse all of the
20678 // parts into the list.
20679 pm_node_t *part;
20680 while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) {
20681 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
20682 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
20683 }
20684 }
20685
20686 pm_token_t closing = parser->current;
20687 if (match1(parser, PM_TOKEN_EOF)) {
20688 pm_parser_err_token(parser, &opening, PM_ERR_REGEXP_TERM);
20689 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20690 } else {
20691 expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM);
20692 }
20693
20694 pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing);
20695 return UP(interpolated);
20696 }
20697 case PM_TOKEN_XSTRING_BEGIN:
20698 case PM_TOKEN_PERCENT_LOWER_X: {
20699 parser_lex(parser);
20700 pm_token_t opening = parser->previous;
20701
20702 // When we get here, we don't know if this string is going to have
20703 // interpolation or not, even though it is allowed. Still, we want to be
20704 // able to return a string node without interpolation if we can since
20705 // it'll be faster.
20706 if (match1(parser, PM_TOKEN_STRING_END)) {
20707 // If we get here, then we have an end immediately after a start. In
20708 // that case we'll create an empty content token and return an
20709 // uninterpolated string.
20710 pm_token_t content = (pm_token_t) {
20711 .type = PM_TOKEN_STRING_CONTENT,
20712 .start = parser->previous.end,
20713 .end = parser->previous.end
20714 };
20715
20716 parser_lex(parser);
20717 return UP(pm_xstring_node_create(parser, &opening, &content, &parser->previous));
20718 }
20719
20721
20722 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20723 // In this case we've hit string content so we know the string
20724 // at least has something in it. We'll need to check if the
20725 // following token is the end (in which case we can return a
20726 // plain string) or if it's not then it has interpolation.
20727 pm_string_t unescaped = parser->current_string;
20728 pm_token_t content = parser->current;
20729 parser_lex(parser);
20730
20731 if (match1(parser, PM_TOKEN_STRING_END)) {
20732 pm_node_t *node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
20733 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
20734 parser_lex(parser);
20735 return node;
20736 }
20737
20738 // If we get here, then we have interpolation so we'll need to
20739 // create a string node with interpolation.
20740 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20741
20742 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
20743 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
20744
20745 pm_interpolated_xstring_node_append(parser->arena, node, part);
20746 } else {
20747 // If the first part of the body of the string is not a string
20748 // content, then we have interpolation and we need to create an
20749 // interpolated string node.
20750 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20751 }
20752
20753 pm_node_t *part;
20754 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20755 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
20756 pm_interpolated_xstring_node_append(parser->arena, node, part);
20757 }
20758 }
20759
20760 pm_token_t closing = parser->current;
20761 if (match1(parser, PM_TOKEN_EOF)) {
20762 pm_parser_err_token(parser, &opening, PM_ERR_XSTRING_TERM);
20763 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20764 } else {
20765 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM);
20766 }
20767 pm_interpolated_xstring_node_closing_set(parser, node, &closing);
20768
20769 return UP(node);
20770 }
20771 case PM_TOKEN_USTAR: {
20772 parser_lex(parser);
20773
20774 // * operators at the beginning of expressions are only valid in the
20775 // context of a multiple assignment. We enforce that here. We'll
20776 // still lex past it though and create a missing node place.
20777 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20778 pm_parser_err_prefix(parser, diag_id);
20779 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20780 }
20781
20782 pm_node_t *splat = parse_splat(parser, flags, depth);
20783
20784 if (match1(parser, PM_TOKEN_COMMA)) {
20785 return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20786 } else {
20787 return parse_target_validate(parser, splat, true);
20788 }
20789 }
20790 case PM_TOKEN_BANG: {
20791 if (binding_power > PM_BINDING_POWER_UNARY) {
20792 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20793 }
20794
20795 parser_lex(parser);
20796
20797 pm_token_t operator = parser->previous;
20798 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));
20799 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!");
20800
20801 pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
20802 return UP(node);
20803 }
20804 case PM_TOKEN_TILDE: {
20805 if (binding_power > PM_BINDING_POWER_UNARY) {
20806 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20807 }
20808 parser_lex(parser);
20809
20810 pm_token_t operator = parser->previous;
20811 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));
20812 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~");
20813
20814 return UP(node);
20815 }
20816 case PM_TOKEN_UMINUS: {
20817 if (binding_power > PM_BINDING_POWER_UNARY) {
20818 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20819 }
20820 parser_lex(parser);
20821
20822 pm_token_t operator = parser->previous;
20823 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));
20824 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "-@");
20825
20826 return UP(node);
20827 }
20828 case PM_TOKEN_UMINUS_NUM: {
20829 parser_lex(parser);
20830
20831 pm_token_t operator = parser->previous;
20832 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));
20833
20834 if (accept1(parser, PM_TOKEN_STAR_STAR)) {
20835 pm_token_t exponent_operator = parser->previous;
20836 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));
20837 node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0));
20838 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20839 } else {
20840 switch (PM_NODE_TYPE(node)) {
20841 case PM_INTEGER_NODE:
20842 case PM_FLOAT_NODE:
20843 case PM_RATIONAL_NODE:
20844 case PM_IMAGINARY_NODE:
20845 parse_negative_numeric(node);
20846 break;
20847 default:
20848 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20849 break;
20850 }
20851 }
20852
20853 return node;
20854 }
20855 case PM_TOKEN_MINUS_GREATER: {
20856 int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting;
20857 parser->lambda_enclosure_nesting = parser->enclosure_nesting;
20858
20859 size_t opening_newline_index = token_newline_index(parser);
20860 parser_lex(parser);
20861
20862 pm_token_t operator = parser->previous;
20863 pm_parser_scope_push(parser, false);
20864
20865 pm_block_parameters_node_t *block_parameters;
20866
20867 switch (parser->current.type) {
20868 case PM_TOKEN_PARENTHESIS_LEFT: {
20869 pm_token_t opening = parser->current;
20870 parser_lex(parser);
20871
20872 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20873 block_parameters = pm_block_parameters_node_create(parser, NULL, &opening);
20874 } else {
20875 block_parameters = parse_block_parameters(parser, false, &opening, true, true, (uint16_t) (depth + 1));
20876 }
20877
20878 accept1(parser, PM_TOKEN_NEWLINE);
20879 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20880
20881 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
20882 break;
20883 }
20884 case PM_CASE_PARAMETER: {
20885 block_parameters = parse_block_parameters(parser, false, NULL, true, false, (uint16_t) (depth + 1));
20886 break;
20887 }
20888 default: {
20889 block_parameters = NULL;
20890 break;
20891 }
20892 }
20893
20894 pm_token_t opening;
20895 pm_node_t *body = NULL;
20896
20897 if (accept1(parser, PM_TOKEN_LAMBDA_BEGIN)) {
20898 opening = parser->previous;
20899
20900 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
20901 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1)));
20902 }
20903
20904 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
20905
20906 /* Restore the enclosing lambda's nesting now that the body has
20907 * been parsed, so that the token following the closing `}` is
20908 * lexed in the enclosing context. During the body the nesting
20909 * held this lambda's own level, which every token inside the
20910 * braces sits above. This mirrors parse.y restoring
20911 * `p->lex.lpar_beg` after `lambda_body`. */
20912 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
20913 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
20914 } else {
20915 /* A `-> { }` body is delimited by `{`/`}`, whose block-accepting
20916 * frame the lexer manages. A `-> do end` body is delimited by
20917 * keywords, so push the frame here and pop it before `end`. The
20918 * push must precede consuming the `do`, which lexes the first
20919 * token of the body; this matches parse.y's CMDARG_PUSH(0)
20920 * before `lambda_body`. */
20921 pm_accepts_block_stack_push(parser, true);
20922 expect1(parser, PM_TOKEN_KEYWORD_DO_LAMBDA, PM_ERR_LAMBDA_OPEN);
20923 opening = parser->previous;
20924
20925 /* The lexer cleared the nesting when it produced the `do`. If
20926 * it was missing entirely, clear it here so that the body is
20927 * recovered the same way it would have been parsed: no token
20928 * within it sits at the beginning of a lambda. */
20929 parser->lambda_enclosure_nesting = -1;
20930
20931 if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
20932 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1)));
20933 }
20934
20935 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
20936 assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE));
20937 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)));
20938 } else {
20939 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
20940 }
20941
20942 pm_accepts_block_stack_pop(parser);
20943
20944 /* As with the brace branch above, restore the nesting before
20945 * consuming the closing `end`, which lexes the token that
20946 * follows it. */
20947 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
20948 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator);
20949 }
20950
20951 pm_constant_id_list_t locals;
20952 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
20953 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &operator, &parser->previous);
20954
20955 pm_parser_scope_pop(parser);
20956
20957 return UP(pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body));
20958 }
20959 case PM_TOKEN_UPLUS: {
20960 if (binding_power > PM_BINDING_POWER_UNARY) {
20961 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20962 }
20963 parser_lex(parser);
20964
20965 pm_token_t operator = parser->previous;
20966 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));
20967 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "+@");
20968
20969 return UP(node);
20970 }
20971 case PM_TOKEN_STRING_BEGIN:
20972 return parse_strings(parser, NULL, flags & PM_PARSE_ACCEPTS_LABEL, (uint16_t) (depth + 1));
20973 case PM_TOKEN_SYMBOL_BEGIN: {
20974 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
20975 parser_lex(parser);
20976
20977 return parse_symbol(parser, &lex_mode, PM_LEX_STATE_END, (uint16_t) (depth + 1));
20978 }
20979 default: {
20980 pm_context_t recoverable = context_recoverable(parser, &parser->current);
20981
20982 if (recoverable != PM_CONTEXT_NONE) {
20983 parser->recovering = true;
20984
20985 // If the given error is not the generic one, then we'll add it
20986 // here because it will provide more context in addition to the
20987 // recoverable error that we will also add.
20988 if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) {
20989 pm_parser_err_prefix(parser, diag_id);
20990 }
20991
20992 // If we get here, then we are assuming this token is closing a
20993 // parent context, so we'll indicate that to the user so that
20994 // they know how we behaved.
20995 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_str(parser->current.type), context_human(recoverable));
20996 } else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) {
20997 // We're going to make a special case here, because "cannot
20998 // parse expression" is pretty generic, and we know here that we
20999 // have an unexpected token.
21000 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_str(parser->current.type));
21001 } else {
21002 pm_parser_err_prefix(parser, diag_id);
21003 }
21004
21005 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
21006 }
21007 }
21008}
21009
21010static pm_node_t *
21011parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth);
21012
21020static void
21021parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
21022 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21023 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21024 parser_lex(parser);
21025 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));
21026 }
21027}
21028
21038static pm_node_t *
21039parse_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) {
21040 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));
21041
21042 // Assignments whose value is a command call (e.g., a = b c) can only
21043 // be followed by modifiers (if/unless/while/until/rescue) and not by
21044 // operators with higher binding power. If we find one, emit an error
21045 // and skip the operator and its right-hand side.
21046 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER && (pm_command_call_value_p(parser, value) || pm_block_call_p(value))) {
21047 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21048 parser_lex(parser);
21049 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));
21050 }
21051
21052 // Contradicting binding powers, the right-hand-side value of the assignment
21053 // allows the `rescue` modifier.
21054 if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21055 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21056
21057 pm_token_t rescue = parser->current;
21058 parser_lex(parser);
21059
21060 // As in parse_assignment_values, the resbody is a `stmt` (permitting a
21061 // multiple assignment / command call) when the rescued value is itself a
21062 // command call, and a plain `arg` otherwise.
21063 bool statement_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21064 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21065
21066 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21067 context_pop(parser);
21068
21069 // A pattern-match resbody is a statement, but here the rescue is nested
21070 // in an assignment value where parse_expression_terminator cannot see
21071 // it, so reject a trailing operator above the modifier level directly.
21072 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21073 parse_rescue_modifier_terminator(parser, flags, depth);
21074 }
21075
21076 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21077 }
21078
21079 return value;
21080}
21081
21086static void
21087parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) {
21088 switch (PM_NODE_TYPE(node)) {
21089 case PM_BEGIN_NODE: {
21090 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
21091 if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements);
21092 break;
21093 }
21094 case PM_LOCAL_VARIABLE_WRITE_NODE: {
21096 pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
21097 break;
21098 }
21099 case PM_PARENTHESES_NODE: {
21100 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
21101 if (cast->body != NULL) parse_assignment_value_local(parser, cast->body);
21102 break;
21103 }
21104 case PM_STATEMENTS_NODE: {
21105 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
21106 const pm_node_t *statement;
21107
21108 PM_NODE_LIST_FOREACH(&cast->body, index, statement) {
21109 parse_assignment_value_local(parser, statement);
21110 }
21111 break;
21112 }
21113 default:
21114 break;
21115 }
21116}
21117
21130static pm_node_t *
21131parse_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) {
21132 bool statement_level = (previous_binding_power == PM_BINDING_POWER_STATEMENT) || (flags & PM_PARSE_ACCEPTS_STATEMENT);
21133
21134 bool permitted = true;
21135 if (!statement_level && match1(parser, PM_TOKEN_USTAR)) permitted = false;
21136
21137 // A command call (e.g. `x = y z`) is permitted as the value when assigning
21138 // directly (carrying the caller's flag), or in any statement-level context
21139 // — which includes a rescue modifier value via the flag.
21140 uint8_t command_call_flag = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
21141 ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21142 : ((previous_binding_power < PM_BINDING_POWER_MODIFIER || statement_level) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21143
21144 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));
21145 if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
21146
21147 parse_assignment_value_local(parser, value);
21148 bool single_value = true;
21149
21150 // Block calls (command call + do block, e.g., `foo bar do end`) cannot
21151 // be followed by a comma to form a multi-value RHS because each element
21152 // of a multi-value assignment must be an `arg`, not a `block_call`.
21153 if (statement_level && !pm_block_call_p(value) && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) {
21154 single_value = false;
21155
21156 pm_array_node_t *array = pm_array_node_create(parser, NULL);
21157 pm_array_node_elements_append(parser->arena, array, value);
21158 value = UP(array);
21159
21160 while (accept1(parser, PM_TOKEN_COMMA)) {
21161 pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1));
21162
21163 pm_array_node_elements_append(parser->arena, array, element);
21164 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
21165
21166 parse_assignment_value_local(parser, element);
21167 }
21168 }
21169
21170 // Assignments whose value is a command call (e.g., a = b c) can only
21171 // be followed by modifiers (if/unless/while/until/rescue) and not by
21172 // operators with higher binding power. If we find one, emit an error
21173 // and skip the operator and its right-hand side.
21174 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))) {
21175 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21176 parser_lex(parser);
21177 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));
21178 }
21179
21180 // Contradicting binding powers, the right-hand-side value of the assignment
21181 // allows the `rescue` modifier.
21182 bool multiple_assignment = (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1));
21183 if ((single_value || multiple_assignment) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21184 bool command_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21185
21186 // A multiple assignment whose value is a command call (`x, y = foo
21187 // bar`) is a complete statement (parse.y: `mlhs '='
21188 // command_call_value`, which has no rescue), so a trailing `rescue`
21189 // modifies the whole assignment rather than the value. Leave it for the
21190 // statement-level rescue instead of binding it to the value here. For a
21191 // non-command value the rescue does bind to the value (parse.y:
21192 // `mlhs '=' mrhs_arg modifier_rescue stmt`).
21193 if (multiple_assignment && command_value) return value;
21194
21195 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21196
21197 pm_token_t rescue = parser->current;
21198 parser_lex(parser);
21199
21200 // The resbody is a `stmt` (parse.y: `command_rhs`/`mlhs '=' mrhs_arg`),
21201 // which permits a multiple assignment and a command call, when this is a
21202 // multiple assignment or the rescued value is itself a command call.
21203 // Otherwise it is a plain `arg` (parse.y: `arg_rhs`).
21204 bool statement_value = multiple_assignment || command_value;
21205 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21206
21207 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21208 context_pop(parser);
21209
21210 // A pattern-match resbody is a statement, but here the rescue is nested
21211 // in an assignment value where parse_expression_terminator cannot see
21212 // it, so reject a trailing operator above the modifier level directly.
21213 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21214 parse_rescue_modifier_terminator(parser, flags, depth);
21215 }
21216
21217 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21218 }
21219
21220 return value;
21221}
21222
21235static pm_node_t *
21236parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth) {
21237 if (statement) {
21238 pm_node_t *value;
21239 bool multiple;
21240
21241 if (match1(parser, PM_TOKEN_USTAR)) {
21242 // A leading splat can only begin a multiple assignment target list.
21243 parser_lex(parser);
21244 value = parse_splat(parser, flags, depth);
21245 multiple = true;
21246 } else {
21247 // The flag lets a single-target assignment take a multiple-value or
21248 // splat right-hand side (`b = c, d` / `b = *c`); a comma _before_ an
21249 // `=`, or a parenthesized target list (`(b, c), d = 1`), instead
21250 // promotes to a multiple assignment target list below.
21251 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));
21252 multiple = match1(parser, PM_TOKEN_COMMA) || PM_NODE_TYPE_P(value, PM_MULTI_TARGET_NODE);
21253 }
21254
21255 if (multiple) {
21256 pm_node_t *target = parse_targets_validate(parser, value, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21257
21258 // A promoted target list is only a valid rescue value as part of a
21259 // complete `targets = values`. parse_targets_validate already
21260 // reports a missing `=` for every terminator except `)` (which it
21261 // permits for an enclosing mlhs paren that does not apply here), so
21262 // reject that case.
21263 if (!match1(parser, PM_TOKEN_EQUAL)) {
21264 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
21265 return target;
21266 }
21267
21268 pm_token_t operator = parser->current;
21269 parser_lex(parser);
21270
21271 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));
21272 value = parse_write(parser, target, &operator, values);
21273 }
21274
21275 // Reject a trailing operator that cannot follow a statement resbody.
21276 // Pattern-match handlers are statements too, but for the bare statement
21277 // form they are reported by parse_expression_terminator instead (which
21278 // keeps its existing error-recovery), so they are excluded here.
21279 if (!PM_NODE_TYPE_P(value, PM_MATCH_REQUIRED_NODE) && !PM_NODE_TYPE_P(value, PM_MATCH_PREDICATE_NODE)) {
21280 parse_rescue_modifier_terminator(parser, flags, depth);
21281 }
21282
21283 return value;
21284 }
21285
21286 // Otherwise the resbody is a plain `arg` (parse.y: `arg modifier_rescue
21287 // arg`), parsed above the `and`/`or`/`not` level so those stay outside it.
21288 return parse_expression(parser, PM_BINDING_POWER_DEFINED, flags, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21289}
21290
21298static void
21299parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) {
21300 if (call_node->arguments != NULL) {
21301 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS);
21302 pm_node_unreference(parser, UP(call_node->arguments));
21303 call_node->arguments = NULL;
21304 }
21305
21306 if (call_node->block != NULL) {
21307 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK);
21308 pm_node_unreference(parser, UP(call_node->block));
21309 call_node->block = NULL;
21310 }
21311}
21312
21313static PRISM_INLINE const uint8_t *
21314pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21315 cursor++;
21316
21317 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21318 uint8_t value = escape_hexadecimal_digit(*cursor);
21319 cursor++;
21320
21321 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21322 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(*cursor));
21323 cursor++;
21324 }
21325
21326 pm_buffer_append_byte(unescaped, value);
21327 } else {
21328 pm_buffer_append_string(unescaped, "\\x", 2);
21329 }
21330
21331 return cursor;
21332}
21333
21334static PRISM_INLINE const uint8_t *
21335pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21336 uint8_t value = (uint8_t) (*cursor - '0');
21337 cursor++;
21338
21339 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21340 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21341 cursor++;
21342
21343 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21344 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21345 cursor++;
21346 }
21347 }
21348
21349 pm_buffer_append_byte(unescaped, value);
21350 return cursor;
21351}
21352
21353static PRISM_INLINE const uint8_t *
21354pm_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) {
21355 const uint8_t *start = cursor - 1;
21356 cursor++;
21357
21358 if (cursor >= end) {
21359 pm_buffer_append_string(unescaped, "\\u", 2);
21360 return cursor;
21361 }
21362
21363 if (*cursor != '{') {
21364 size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4));
21365 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21366
21367 if (!pm_buffer_append_unicode_codepoint(unescaped, value)) {
21368 pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start));
21369 }
21370
21371 return cursor + length;
21372 }
21373
21374 cursor++;
21375 for (;;) {
21376 while (cursor < end && *cursor == ' ') cursor++;
21377
21378 if (cursor >= end) break;
21379 if (*cursor == '}') {
21380 cursor++;
21381 break;
21382 }
21383
21384 size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor);
21385 if (length == 0) {
21386 break;
21387 }
21388 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21389
21390 (void) pm_buffer_append_unicode_codepoint(unescaped, value);
21391 cursor += length;
21392 }
21393
21394 return cursor;
21395}
21396
21397static void
21398pm_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) {
21399 const uint8_t *end = source + length;
21400 pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source));
21401
21402 for (;;) {
21403 if (++cursor >= end) {
21404 pm_buffer_append_byte(unescaped, '\\');
21405 return;
21406 }
21407
21408 switch (*cursor) {
21409 case 'x':
21410 cursor = pm_named_capture_escape_hex(unescaped, cursor, end);
21411 break;
21412 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
21413 cursor = pm_named_capture_escape_octal(unescaped, cursor, end);
21414 break;
21415 case 'u':
21416 cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location);
21417 break;
21418 default:
21419 pm_buffer_append_byte(unescaped, '\\');
21420 break;
21421 }
21422
21423 const uint8_t *next_cursor = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, parser->encoding);
21424 if (next_cursor == NULL) break;
21425
21426 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (next_cursor - cursor));
21427 cursor = next_cursor;
21428 }
21429
21430 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (end - cursor));
21431}
21432
21437static void
21438parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *capture, bool shared, pm_regexp_name_data_t *callback_data) {
21439 pm_call_node_t *call = callback_data->call;
21440 pm_constant_id_list_t *names = &callback_data->names;
21441
21442 const uint8_t *source = pm_string_source(capture);
21443 size_t length = pm_string_length(capture);
21444 pm_buffer_t unescaped = { 0 };
21445
21446 // First, we need to handle escapes within the name of the capture group.
21447 // This is because regular expressions have three different representations
21448 // in prism. The first is the plain source code. The second is the
21449 // representation that will be sent to the regular expression engine, which
21450 // is the value of the "unescaped" field. This is poorly named, because it
21451 // actually still contains escapes, just a subset of them that the regular
21452 // expression engine knows how to handle. The third representation is fully
21453 // unescaped, which is what we need.
21454 const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding);
21455 if (PRISM_UNLIKELY(cursor != NULL)) {
21456 pm_named_capture_escape(parser, &unescaped, source, length, cursor, shared ? NULL : &call->receiver->location);
21457 source = (const uint8_t *) pm_buffer_value(&unescaped);
21458 length = pm_buffer_length(&unescaped);
21459 }
21460
21461 const uint8_t *start;
21462 const uint8_t *end;
21463 pm_constant_id_t name;
21464
21465 // If the name of the capture group isn't a valid identifier, we do
21466 // not add it to the local table.
21467 if (!pm_slice_is_valid_local(parser, source, source + length)) {
21468 pm_buffer_cleanup(&unescaped);
21469 return;
21470 }
21471
21472 if (shared) {
21473 // If the unescaped string is a slice of the source, then we can
21474 // copy the names directly. The pointers will line up.
21475 start = source;
21476 end = source + length;
21477 name = pm_parser_constant_id_raw(parser, start, end);
21478 } else {
21479 // Otherwise, the name is a slice of the malloc-ed owned string,
21480 // in which case we need to copy it out into a new string.
21481 start = parser->start + PM_NODE_START(call->receiver);
21482 end = parser->start + PM_NODE_END(call->receiver);
21483
21484 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
21485 memcpy(memory, source, length);
21486 name = pm_parser_constant_id_owned(parser, memory, length);
21487 }
21488
21489 // Add this name to the list of constants if it is valid, not duplicated,
21490 // and not a keyword.
21491 if (name != 0 && !pm_constant_id_list_includes(names, name)) {
21492 pm_constant_id_list_append(parser->arena, names, name);
21493
21494 int depth;
21495 if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
21496 // If the local is not already a local but it is a keyword, then we
21497 // do not want to add a capture for this.
21498 if (pm_local_is_keyword((const char *) source, length)) {
21499 pm_buffer_cleanup(&unescaped);
21500 return;
21501 }
21502
21503 // If the identifier is not already a local, then we will add it to
21504 // the local table.
21505 pm_parser_local_add(parser, name, start, end, 0);
21506 }
21507
21508 // Here we lazily create the MatchWriteNode since we know we're
21509 // about to add a target.
21510 if (callback_data->match == NULL) {
21511 callback_data->match = pm_match_write_node_create(parser, call);
21512 }
21513
21514 // Next, create the local variable target and add it to the list of
21515 // targets for the match.
21516 pm_token_t token = { .type = 0, .start = start, .end = end };
21517 pm_location_t token_loc = TOK2LOC(parser, &token);
21518 pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &token_loc, name, depth == -1 ? 0 : (uint32_t) depth));
21519 pm_node_list_append(parser->arena, &callback_data->match->targets, target);
21520 }
21521
21522 pm_buffer_cleanup(&unescaped);
21523}
21524
21530static pm_node_t *
21531parse_interpolated_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call, bool extended_mode) {
21532 pm_regexp_name_data_t callback_data = {
21533 .call = call,
21534 .match = NULL,
21535 .names = { 0 },
21536 };
21537
21538 pm_regexp_parse_named_captures(parser, pm_string_source(content), pm_string_length(content), false, extended_mode, parse_regular_expression_named_capture, &callback_data);
21539
21540 if (callback_data.match != NULL) {
21541 return UP(callback_data.match);
21542 } else {
21543 return UP(call);
21544 }
21545}
21546
21547static PRISM_INLINE pm_node_t *
21548parse_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) {
21549 pm_token_t token = parser->current;
21550
21551 switch (token.type) {
21552 case PM_TOKEN_EQUAL: {
21553 switch (PM_NODE_TYPE(node)) {
21554 case PM_CALL_NODE: {
21555 // If we have no arguments to the call node and we need this
21556 // to be a target then this is either a method call or a
21557 // local variable write. This _must_ happen before the value
21558 // is parsed because it could be referenced in the value.
21559 pm_call_node_t *call_node = (pm_call_node_t *) node;
21560 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21561 pm_parser_local_add_location(parser, &call_node->message_loc, 0);
21562 }
21563 }
21565 case PM_CASE_WRITABLE: {
21566 // When we have `it = value`, we need to add `it` as a local
21567 // variable before parsing the value, in case the value
21568 // references the variable.
21569 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
21570 pm_parser_local_add_location(parser, &node->location, 0);
21571 }
21572
21573 parser_lex(parser);
21574 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));
21575
21576 if (PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) && previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
21577 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_MULTI_WRITE);
21578 }
21579
21580 return parse_write(parser, node, &token, value);
21581 }
21582 case PM_SPLAT_NODE: {
21583 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
21584 pm_multi_target_node_targets_append(parser, multi_target, node);
21585
21586 parser_lex(parser);
21587 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));
21588 return parse_write(parser, UP(multi_target), &token, value);
21589 }
21590 case PM_SOURCE_ENCODING_NODE:
21591 case PM_FALSE_NODE:
21592 case PM_SOURCE_FILE_NODE:
21593 case PM_SOURCE_LINE_NODE:
21594 case PM_NIL_NODE:
21595 case PM_SELF_NODE:
21596 case PM_TRUE_NODE: {
21597 // In these special cases, we have specific error messages
21598 // and we will replace them with local variable writes.
21599 parser_lex(parser);
21600 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
21601 return parse_unwriteable_write(parser, node, &token, value);
21602 }
21603 default:
21604 // In this case we have an = sign, but we don't know what
21605 // it's for. We need to treat it as an error. We'll mark it
21606 // as an error and skip past it.
21607 parser_lex(parser);
21608 pm_parser_err_token(parser, &token, PM_ERR_EXPRESSION_NOT_WRITABLE);
21609 return node;
21610 }
21611 }
21612 case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL: {
21613 switch (PM_NODE_TYPE(node)) {
21614 case PM_BACK_REFERENCE_READ_NODE:
21615 case PM_NUMBERED_REFERENCE_READ_NODE:
21616 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21618 case PM_GLOBAL_VARIABLE_READ_NODE: {
21619 parser_lex(parser);
21620
21621 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21622 pm_node_t *result = UP(pm_global_variable_and_write_node_create(parser, node, &token, value));
21623
21624 return result;
21625 }
21626 case PM_CLASS_VARIABLE_READ_NODE: {
21627 parser_lex(parser);
21628
21629 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21630 pm_node_t *result = UP(pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21631
21632 return result;
21633 }
21634 case PM_CONSTANT_PATH_NODE: {
21635 parser_lex(parser);
21636
21637 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21638 pm_node_t *write = UP(pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21639
21640 return parse_shareable_constant_write(parser, write);
21641 }
21642 case PM_CONSTANT_READ_NODE: {
21643 parser_lex(parser);
21644
21645 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21646 pm_node_t *write = UP(pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21647
21648 if (context_def_p(parser)) {
21649 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21650 }
21651
21652 return parse_shareable_constant_write(parser, write);
21653 }
21654 case PM_INSTANCE_VARIABLE_READ_NODE: {
21655 parser_lex(parser);
21656
21657 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21658 pm_node_t *result = UP(pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21659
21660 return result;
21661 }
21662 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21663 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21664 parser_lex(parser);
21665
21666 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21667 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0));
21668
21669 pm_node_unreference(parser, node);
21670 return result;
21671 }
21672 case PM_LOCAL_VARIABLE_READ_NODE: {
21673 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21674 PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + node->location.start);
21675 pm_node_unreference(parser, node);
21676 }
21677
21679 parser_lex(parser);
21680
21681 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21682 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21683
21684 return result;
21685 }
21686 case PM_CALL_NODE: {
21687 pm_call_node_t *cast = (pm_call_node_t *) node;
21688
21689 // If we have a vcall (a method with no arguments and no
21690 // receiver that could have been a local variable) then we
21691 // will transform it into a local variable write.
21692 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21693 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
21694 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
21695 parser_lex(parser);
21696
21697 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21698 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21699
21700 return result;
21701 }
21702
21703 // Move past the token here so that we have already added
21704 // the local variable by this point.
21705 parser_lex(parser);
21706
21707 // If there is no call operator and the message is "[]" then
21708 // this is an aref expression, and we can transform it into
21709 // an aset expression.
21710 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
21711 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21712 return UP(pm_index_and_write_node_create(parser, cast, &token, value));
21713 }
21714
21715 // If this node cannot be writable, then we have an error.
21716 if (pm_call_node_writable_p(parser, cast)) {
21717 parse_write_name(parser, &cast->name);
21718 } else {
21719 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
21720 }
21721
21722 parse_call_operator_write(parser, cast, &token);
21723 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21724 return UP(pm_call_and_write_node_create(parser, cast, &token, value));
21725 }
21726 case PM_MULTI_WRITE_NODE: {
21727 parser_lex(parser);
21728 pm_parser_err_token(parser, &token, PM_ERR_AMPAMPEQ_MULTI_ASSIGN);
21729 return node;
21730 }
21731 default:
21732 parser_lex(parser);
21733
21734 // In this case we have an &&= sign, but we don't know what it's for.
21735 // We need to treat it as an error. For now, we'll mark it as an error
21736 // and just skip right past it.
21737 pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
21738 return node;
21739 }
21740 }
21741 case PM_TOKEN_PIPE_PIPE_EQUAL: {
21742 switch (PM_NODE_TYPE(node)) {
21743 case PM_BACK_REFERENCE_READ_NODE:
21744 case PM_NUMBERED_REFERENCE_READ_NODE:
21745 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21747 case PM_GLOBAL_VARIABLE_READ_NODE: {
21748 parser_lex(parser);
21749
21750 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21751 pm_node_t *result = UP(pm_global_variable_or_write_node_create(parser, node, &token, value));
21752
21753 return result;
21754 }
21755 case PM_CLASS_VARIABLE_READ_NODE: {
21756 parser_lex(parser);
21757
21758 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21759 pm_node_t *result = UP(pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21760
21761 return result;
21762 }
21763 case PM_CONSTANT_PATH_NODE: {
21764 parser_lex(parser);
21765
21766 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21767 pm_node_t *write = UP(pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21768
21769 return parse_shareable_constant_write(parser, write);
21770 }
21771 case PM_CONSTANT_READ_NODE: {
21772 parser_lex(parser);
21773
21774 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21775 pm_node_t *write = UP(pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21776
21777 if (context_def_p(parser)) {
21778 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21779 }
21780
21781 return parse_shareable_constant_write(parser, write);
21782 }
21783 case PM_INSTANCE_VARIABLE_READ_NODE: {
21784 parser_lex(parser);
21785
21786 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21787 pm_node_t *result = UP(pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21788
21789 return result;
21790 }
21791 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21792 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21793 parser_lex(parser);
21794
21795 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21796 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0));
21797
21798 pm_node_unreference(parser, node);
21799 return result;
21800 }
21801 case PM_LOCAL_VARIABLE_READ_NODE: {
21802 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21803 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
21804 pm_node_unreference(parser, node);
21805 }
21806
21808 parser_lex(parser);
21809
21810 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21811 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21812
21813 return result;
21814 }
21815 case PM_CALL_NODE: {
21816 pm_call_node_t *cast = (pm_call_node_t *) node;
21817
21818 // If we have a vcall (a method with no arguments and no
21819 // receiver that could have been a local variable) then we
21820 // will transform it into a local variable write.
21821 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21822 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
21823 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
21824 parser_lex(parser);
21825
21826 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21827 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21828
21829 return result;
21830 }
21831
21832 // Move past the token here so that we have already added
21833 // the local variable by this point.
21834 parser_lex(parser);
21835
21836 // If there is no call operator and the message is "[]" then
21837 // this is an aref expression, and we can transform it into
21838 // an aset expression.
21839 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
21840 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21841 return UP(pm_index_or_write_node_create(parser, cast, &token, value));
21842 }
21843
21844 // If this node cannot be writable, then we have an error.
21845 if (pm_call_node_writable_p(parser, cast)) {
21846 parse_write_name(parser, &cast->name);
21847 } else {
21848 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
21849 }
21850
21851 parse_call_operator_write(parser, cast, &token);
21852 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21853 return UP(pm_call_or_write_node_create(parser, cast, &token, value));
21854 }
21855 case PM_MULTI_WRITE_NODE: {
21856 parser_lex(parser);
21857 pm_parser_err_token(parser, &token, PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN);
21858 return node;
21859 }
21860 default:
21861 parser_lex(parser);
21862
21863 // In this case we have an ||= sign, but we don't know what it's for.
21864 // We need to treat it as an error. For now, we'll mark it as an error
21865 // and just skip right past it.
21866 pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
21867 return node;
21868 }
21869 }
21870 case PM_TOKEN_AMPERSAND_EQUAL:
21871 case PM_TOKEN_CARET_EQUAL:
21872 case PM_TOKEN_GREATER_GREATER_EQUAL:
21873 case PM_TOKEN_LESS_LESS_EQUAL:
21874 case PM_TOKEN_MINUS_EQUAL:
21875 case PM_TOKEN_PERCENT_EQUAL:
21876 case PM_TOKEN_PIPE_EQUAL:
21877 case PM_TOKEN_PLUS_EQUAL:
21878 case PM_TOKEN_SLASH_EQUAL:
21879 case PM_TOKEN_STAR_EQUAL:
21880 case PM_TOKEN_STAR_STAR_EQUAL: {
21881 switch (PM_NODE_TYPE(node)) {
21882 case PM_BACK_REFERENCE_READ_NODE:
21883 case PM_NUMBERED_REFERENCE_READ_NODE:
21884 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21886 case PM_GLOBAL_VARIABLE_READ_NODE: {
21887 parser_lex(parser);
21888
21889 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21890 pm_node_t *result = UP(pm_global_variable_operator_write_node_create(parser, node, &token, value));
21891
21892 return result;
21893 }
21894 case PM_CLASS_VARIABLE_READ_NODE: {
21895 parser_lex(parser);
21896
21897 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21898 pm_node_t *result = UP(pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21899
21900 return result;
21901 }
21902 case PM_CONSTANT_PATH_NODE: {
21903 parser_lex(parser);
21904
21905 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21906 pm_node_t *write = UP(pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21907
21908 return parse_shareable_constant_write(parser, write);
21909 }
21910 case PM_CONSTANT_READ_NODE: {
21911 parser_lex(parser);
21912
21913 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21914 pm_node_t *write = UP(pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21915
21916 if (context_def_p(parser)) {
21917 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21918 }
21919
21920 return parse_shareable_constant_write(parser, write);
21921 }
21922 case PM_INSTANCE_VARIABLE_READ_NODE: {
21923 parser_lex(parser);
21924
21925 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21926 pm_node_t *result = UP(pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21927
21928 return result;
21929 }
21930 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21931 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21932 parser_lex(parser);
21933
21934 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21935 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0));
21936
21937 pm_node_unreference(parser, node);
21938 return result;
21939 }
21940 case PM_LOCAL_VARIABLE_READ_NODE: {
21941 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21942 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
21943 pm_node_unreference(parser, node);
21944 }
21945
21947 parser_lex(parser);
21948
21949 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21950 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21951
21952 return result;
21953 }
21954 case PM_CALL_NODE: {
21955 parser_lex(parser);
21956 pm_call_node_t *cast = (pm_call_node_t *) node;
21957
21958 // If we have a vcall (a method with no arguments and no
21959 // receiver that could have been a local variable) then we
21960 // will transform it into a local variable write.
21961 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21962 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
21963 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
21964 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21965 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21966
21967 return result;
21968 }
21969
21970 // If there is no call operator and the message is "[]" then
21971 // this is an aref expression, and we can transform it into
21972 // an aset expression.
21973 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
21974 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21975 return UP(pm_index_operator_write_node_create(parser, cast, &token, value));
21976 }
21977
21978 // If this node cannot be writable, then we have an error.
21979 if (pm_call_node_writable_p(parser, cast)) {
21980 parse_write_name(parser, &cast->name);
21981 } else {
21982 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
21983 }
21984
21985 parse_call_operator_write(parser, cast, &token);
21986 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21987 return UP(pm_call_operator_write_node_create(parser, cast, &token, value));
21988 }
21989 case PM_MULTI_WRITE_NODE: {
21990 parser_lex(parser);
21991 pm_parser_err_token(parser, &token, PM_ERR_OPERATOR_MULTI_ASSIGN);
21992 return node;
21993 }
21994 default:
21995 parser_lex(parser);
21996
21997 // In this case we have an operator but we don't know what it's for.
21998 // We need to treat it as an error. For now, we'll mark it as an error
21999 // and just skip right past it.
22000 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_str(parser->current.type));
22001 return node;
22002 }
22003 }
22004 case PM_TOKEN_AMPERSAND_AMPERSAND:
22005 case PM_TOKEN_KEYWORD_AND: {
22006 parser_lex(parser);
22007
22008 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));
22009 return UP(pm_and_node_create(parser, node, &token, right));
22010 }
22011 case PM_TOKEN_KEYWORD_OR:
22012 case PM_TOKEN_PIPE_PIPE: {
22013 parser_lex(parser);
22014
22015 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));
22016 return UP(pm_or_node_create(parser, node, &token, right));
22017 }
22018 case PM_TOKEN_EQUAL_TILDE: {
22019 // Note that we _must_ parse the value before adding the local
22020 // variables in order to properly mirror the behavior of Ruby. For
22021 // example,
22022 //
22023 // /(?<foo>bar)/ =~ foo
22024 //
22025 // In this case, `foo` should be a method call and not a local yet.
22026 parser_lex(parser);
22027 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));
22028
22029 // By default, we're going to create a call node and then return it.
22030 pm_call_node_t *call = pm_call_node_binary_create(parser, node, &token, argument, 0);
22031 pm_node_t *result = UP(call);
22032
22033 // If the receiver of this =~ is a regular expression node, then we
22034 // need to introduce local variables for it based on its named
22035 // capture groups.
22036 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE)) {
22037 // It's possible to have an interpolated regular expression node
22038 // that only contains strings. This is because it can be split
22039 // up by a heredoc. In this case we need to concat the unescaped
22040 // strings together and then parse them as a regular expression.
22042
22043 bool interpolated = false;
22044 size_t total_length = 0;
22045
22046 pm_node_t *part;
22047 PM_NODE_LIST_FOREACH(parts, index, part) {
22048 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
22049 total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped);
22050 } else {
22051 interpolated = true;
22052 break;
22053 }
22054 }
22055
22056 if (!interpolated && total_length > 0) {
22057 void *memory = xmalloc(total_length);
22058 if (!memory) abort();
22059
22060 uint8_t *cursor = memory;
22061 PM_NODE_LIST_FOREACH(parts, index, part) {
22062 pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
22063 size_t length = pm_string_length(unescaped);
22064
22065 memcpy(cursor, pm_string_source(unescaped), length);
22066 cursor += length;
22067 }
22068
22069 pm_string_t owned;
22070 pm_string_owned_init(&owned, (uint8_t *) memory, total_length);
22071
22072 result = parse_interpolated_regular_expression_named_captures(parser, &owned, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
22073 pm_string_cleanup(&owned);
22074 }
22075 } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
22076 // If we have a regular expression node, then we can parse
22077 // the named captures and validate encoding in one pass.
22079
22080 pm_regexp_name_data_t name_data = {
22081 .call = call,
22082 .match = NULL,
22083 .names = { 0 },
22084 };
22085
22086 pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data));
22087
22088 if (name_data.match != NULL) {
22089 result = UP(name_data.match);
22090 }
22091 }
22092
22093 return result;
22094 }
22095 case PM_TOKEN_UAMPERSAND:
22096 case PM_TOKEN_USTAR:
22097 case PM_TOKEN_USTAR_STAR:
22098 // The only times this will occur are when we are in an error state,
22099 // but we'll put them in here so that errors can propagate.
22100 case PM_TOKEN_BANG_EQUAL:
22101 case PM_TOKEN_BANG_TILDE:
22102 case PM_TOKEN_EQUAL_EQUAL:
22103 case PM_TOKEN_EQUAL_EQUAL_EQUAL:
22104 case PM_TOKEN_LESS_EQUAL_GREATER:
22105 case PM_TOKEN_CARET:
22106 case PM_TOKEN_PIPE:
22107 case PM_TOKEN_AMPERSAND:
22108 case PM_TOKEN_GREATER_GREATER:
22109 case PM_TOKEN_LESS_LESS:
22110 case PM_TOKEN_MINUS:
22111 case PM_TOKEN_PLUS:
22112 case PM_TOKEN_PERCENT:
22113 case PM_TOKEN_SLASH:
22114 case PM_TOKEN_STAR:
22115 case PM_TOKEN_STAR_STAR: {
22116 parser_lex(parser);
22117 pm_token_t operator = parser->previous;
22118 switch (PM_NODE_TYPE(node)) {
22119 case PM_RESCUE_MODIFIER_NODE: {
22121 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22122 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22123 }
22124 break;
22125 }
22126 case PM_AND_NODE: {
22127 pm_and_node_t *cast = (pm_and_node_t *) node;
22128 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22129 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22130 }
22131 break;
22132 }
22133 case PM_OR_NODE: {
22134 pm_or_node_t *cast = (pm_or_node_t *) node;
22135 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22136 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22137 }
22138 break;
22139 }
22140 default:
22141 break;
22142 }
22143
22144 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));
22145 return UP(pm_call_node_binary_create(parser, node, &token, argument, 0));
22146 }
22147 case PM_TOKEN_GREATER:
22148 case PM_TOKEN_GREATER_EQUAL:
22149 case PM_TOKEN_LESS:
22150 case PM_TOKEN_LESS_EQUAL: {
22151 if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) {
22152 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON);
22153 }
22154
22155 parser_lex(parser);
22156 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));
22157 return UP(pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON));
22158 }
22159 case PM_TOKEN_AMPERSAND_DOT:
22160 case PM_TOKEN_DOT: {
22161 parser_lex(parser);
22162 pm_token_t operator = parser->previous;
22163 pm_arguments_t arguments = { 0 };
22164
22165 // This if statement handles the foo.() syntax.
22166 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
22167 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
22168 return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments));
22169 }
22170
22171 switch (PM_NODE_TYPE(node)) {
22172 case PM_RESCUE_MODIFIER_NODE: {
22174 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22175 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22176 }
22177 break;
22178 }
22179 case PM_AND_NODE: {
22180 pm_and_node_t *cast = (pm_and_node_t *) node;
22181 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22182 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22183 }
22184 break;
22185 }
22186 case PM_OR_NODE: {
22187 pm_or_node_t *cast = (pm_or_node_t *) node;
22188 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22189 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22190 }
22191 break;
22192 }
22193 default:
22194 break;
22195 }
22196
22197 pm_token_t message;
22198
22199 switch (parser->current.type) {
22200 case PM_CASE_OPERATOR:
22201 case PM_CASE_KEYWORD:
22202 case PM_TOKEN_CONSTANT:
22203 case PM_TOKEN_IDENTIFIER:
22204 case PM_TOKEN_METHOD_NAME: {
22205 parser_lex(parser);
22206 message = parser->previous;
22207 break;
22208 }
22209 default: {
22210 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_str(parser->current.type));
22211 message = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
22212 }
22213 }
22214
22215 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22216 pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments);
22217
22218 if (
22219 (previous_binding_power == PM_BINDING_POWER_STATEMENT) &&
22220 arguments.arguments == NULL &&
22221 arguments.opening_loc.length == 0 &&
22222 match1(parser, PM_TOKEN_COMMA)
22223 ) {
22224 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22225 } else {
22226 return UP(call);
22227 }
22228 }
22229 case PM_TOKEN_DOT_DOT:
22230 case PM_TOKEN_DOT_DOT_DOT: {
22231 parser_lex(parser);
22232
22233 pm_node_t *right = NULL;
22234 if (token_begins_expression_p(parser->current.type)) {
22235 right = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22236 }
22237
22238 return UP(pm_range_node_create(parser, node, &token, right));
22239 }
22240 case PM_TOKEN_KEYWORD_IF_MODIFIER: {
22241 pm_token_t keyword = parser->current;
22242 parser_lex(parser);
22243
22244 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));
22245 return UP(pm_if_node_modifier_create(parser, node, &keyword, predicate));
22246 }
22247 case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: {
22248 pm_token_t keyword = parser->current;
22249 parser_lex(parser);
22250
22251 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));
22252 return UP(pm_unless_node_modifier_create(parser, node, &keyword, predicate));
22253 }
22254 case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: {
22255 parser_lex(parser);
22256 pm_statements_node_t *statements = pm_statements_node_create(parser);
22257 pm_statements_node_body_append(parser, statements, node, true);
22258
22259 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));
22260 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));
22261 }
22262 case PM_TOKEN_KEYWORD_WHILE_MODIFIER: {
22263 parser_lex(parser);
22264 pm_statements_node_t *statements = pm_statements_node_create(parser);
22265 pm_statements_node_body_append(parser, statements, node, true);
22266
22267 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));
22268 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));
22269 }
22270 case PM_TOKEN_QUESTION_MARK: {
22271 context_push(parser, PM_CONTEXT_TERNARY);
22272 pm_node_list_t current_block_exits = { 0 };
22273 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
22274
22275 pm_token_t qmark = parser->current;
22276 parser_lex(parser);
22277
22278 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));
22279
22280 if (parser->recovering) {
22281 // If parsing the true expression of this ternary resulted in a syntax
22282 // error that we can recover from, then we're going to put missing nodes
22283 // and tokens into the remaining places. We want to be sure to do this
22284 // before the `expect` function call to make sure it doesn't
22285 // accidentally move past a ':' token that occurs after the syntax
22286 // error.
22287 pm_token_t colon = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
22288 pm_node_t *false_expression = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &colon), PM_TOKEN_LENGTH(&colon)));
22289
22290 context_pop(parser);
22291 pop_block_exits(parser, previous_block_exits);
22292 return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression));
22293 }
22294
22295 accept1(parser, PM_TOKEN_NEWLINE);
22296 expect1(parser, PM_TOKEN_COLON, PM_ERR_TERNARY_COLON);
22297
22298 pm_token_t colon = parser->previous;
22299 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));
22300
22301 context_pop(parser);
22302 pop_block_exits(parser, previous_block_exits);
22303 return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression));
22304 }
22305 case PM_TOKEN_COLON_COLON: {
22306 parser_lex(parser);
22307 pm_token_t delimiter = parser->previous;
22308
22309 switch (parser->current.type) {
22310 case PM_TOKEN_CONSTANT: {
22311 parser_lex(parser);
22312 pm_node_t *path;
22313
22314 if (
22315 (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
22316 ((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)))
22317 ) {
22318 // If we have a constant immediately following a '::' operator, then
22319 // this can either be a constant path or a method call, depending on
22320 // what follows the constant.
22321 //
22322 // If we have parentheses, then this is a method call. That would
22323 // look like Foo::Bar().
22324 pm_token_t message = parser->previous;
22325 pm_arguments_t arguments = { 0 };
22326
22327 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22328 path = UP(pm_call_node_call_create(parser, node, &delimiter, &message, &arguments));
22329 } else {
22330 // Otherwise, this is a constant path. That would look like Foo::Bar.
22331 path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
22332 }
22333
22334 // If this is followed by a comma then it is a multiple assignment.
22335 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22336 return parse_targets_validate(parser, path, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22337 }
22338
22339 return path;
22340 }
22341 case PM_CASE_OPERATOR:
22342 case PM_CASE_KEYWORD:
22343 case PM_TOKEN_IDENTIFIER:
22344 case PM_TOKEN_METHOD_NAME: {
22345 parser_lex(parser);
22346 pm_token_t message = parser->previous;
22347
22348 // If we have an identifier following a '::' operator, then it is for
22349 // sure a method call.
22350 pm_arguments_t arguments = { 0 };
22351 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22352 pm_call_node_t *call = pm_call_node_call_create(parser, node, &delimiter, &message, &arguments);
22353
22354 // If this is followed by a comma then it is a multiple assignment.
22355 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22356 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22357 }
22358
22359 return UP(call);
22360 }
22361 case PM_TOKEN_PARENTHESIS_LEFT: {
22362 // If we have a parenthesis following a '::' operator, then it is the
22363 // method call shorthand. That would look like Foo::(bar).
22364 pm_arguments_t arguments = { 0 };
22365 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
22366
22367 return UP(pm_call_node_shorthand_create(parser, node, &delimiter, &arguments));
22368 }
22369 default: {
22370 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
22371 return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
22372 }
22373 }
22374 }
22375 case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: {
22376 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
22377 parser_lex(parser);
22378 accept1(parser, PM_TOKEN_NEWLINE);
22379
22380 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));
22381 context_pop(parser);
22382
22383 return UP(pm_rescue_modifier_node_create(parser, node, &token, value));
22384 }
22385 case PM_TOKEN_BRACKET_LEFT: {
22386 parser_lex(parser);
22387
22388 pm_arguments_t arguments = { 0 };
22389 arguments.opening_loc = TOK2LOC(parser, &parser->previous);
22390
22391 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
22392 parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
22393 expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET);
22394 }
22395
22396 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
22397
22398 // If we have a comma after the closing bracket then this is a multiple
22399 // assignment and we should parse the targets.
22400 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22401 pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments);
22402 return parse_targets_validate(parser, UP(aref), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22403 }
22404
22405 // If we're at the end of the arguments, we can now check if there is a
22406 // block node that starts with a {. If there is, then we can parse it and
22407 // add it to the arguments.
22408 pm_block_node_t *block = NULL;
22409 if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
22410 block = parse_block(parser, (uint16_t) (depth + 1));
22411 pm_arguments_validate_block(parser, &arguments, block);
22412 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
22413 block = parse_block(parser, (uint16_t) (depth + 1));
22414 }
22415
22416 if (block != NULL) {
22417 if (arguments.block != NULL) {
22418 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_AFTER_BLOCK);
22419 if (arguments.arguments == NULL) {
22420 arguments.arguments = pm_arguments_node_create(parser);
22421 }
22422 pm_arguments_node_arguments_append(parser->arena, arguments.arguments, arguments.block);
22423 }
22424
22425 arguments.block = UP(block);
22426 }
22427
22428 return UP(pm_call_node_aref_create(parser, node, &arguments));
22429 }
22430 case PM_TOKEN_KEYWORD_IN: {
22431 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22432 parser->pattern_matching_newlines = true;
22433
22434 pm_token_t operator = parser->current;
22435 parser->command_start = false;
22436 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22437 parser_lex(parser);
22438
22439 pm_constant_id_list_t captures = { 0 };
22440 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));
22441
22442 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22443
22444 return UP(pm_match_predicate_node_create(parser, node, pattern, &operator));
22445 }
22446 case PM_TOKEN_EQUAL_GREATER: {
22447 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22448 parser->pattern_matching_newlines = true;
22449
22450 pm_token_t operator = parser->current;
22451 parser->command_start = false;
22452 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22453 parser_lex(parser);
22454
22455 pm_constant_id_list_t captures = { 0 };
22456 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));
22457
22458 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22459
22460 return UP(pm_match_required_node_create(parser, node, pattern, &operator));
22461 }
22462 default:
22463 assert(false && "unreachable");
22464 return NULL;
22465 }
22466}
22467
22468#undef PM_PARSE_PATTERN_SINGLE
22469#undef PM_PARSE_PATTERN_TOP
22470#undef PM_PARSE_PATTERN_MULTI
22471
22484static bool
22485parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) {
22486 pm_binding_power_t left = pm_binding_powers[parser->current.type].left;
22487
22488 switch (PM_NODE_TYPE(node)) {
22489 case PM_MULTI_WRITE_NODE:
22490 case PM_RETURN_NODE:
22491 case PM_BREAK_NODE:
22492 case PM_NEXT_NODE:
22493 return left > PM_BINDING_POWER_MODIFIER;
22494 case PM_CLASS_VARIABLE_WRITE_NODE:
22495 case PM_CONSTANT_PATH_WRITE_NODE:
22496 case PM_CONSTANT_WRITE_NODE:
22497 case PM_GLOBAL_VARIABLE_WRITE_NODE:
22498 case PM_INSTANCE_VARIABLE_WRITE_NODE:
22499 case PM_LOCAL_VARIABLE_WRITE_NODE:
22500 return PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && left > PM_BINDING_POWER_MODIFIER;
22501 case PM_CALL_NODE: {
22502 // Calls with an implicit array on the right-hand side are
22503 // statements and can only be followed by modifiers.
22504 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)) {
22505 return left > PM_BINDING_POWER_MODIFIER;
22506 }
22507
22508 // Command-style calls (including block commands like
22509 // `foo bar do end`) can only be followed by composition
22510 // (and/or) and modifier (if/unless/etc.) operators.
22511 if (pm_command_call_value_p(parser, node)) {
22512 return left > PM_BINDING_POWER_COMPOSITION;
22513 }
22514
22515 // A block call (command with do-block, or any call chained
22516 // from one) can only be followed by call chaining (., ::,
22517 // &.), composition (and/or), and modifier operators.
22518 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22519 }
22520 case PM_SUPER_NODE:
22521 case PM_YIELD_NODE:
22522 // Command-style super/yield (without parens) can only be followed
22523 // by composition and modifier operators.
22524 if (pm_command_call_value_p(parser, node)) {
22525 return left > PM_BINDING_POWER_COMPOSITION;
22526 }
22527
22528 /* A super carrying a do-block is a block call, so it may also be
22529 * followed by call chaining (`.`, `::`, `&.`). */
22530 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22531 case PM_DEF_NODE:
22532 // An endless method whose body is a command-style call (e.g.,
22533 // `def f = foo bar`) is a command assignment and can only be
22534 // followed by modifiers.
22535 return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(parser, node);
22536 case PM_RESCUE_MODIFIER_NODE:
22537 // A rescue modifier whose handler is a pattern match (=> or in)
22538 // produces a statement and cannot be followed by operators above
22539 // the modifier level.
22540 if (left > PM_BINDING_POWER_MODIFIER) {
22542 pm_node_t *rescue_expression = cast->rescue_expression;
22543 return PM_NODE_TYPE_P(rescue_expression, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(rescue_expression, PM_MATCH_PREDICATE_NODE);
22544 }
22545 return false;
22546 default:
22547 return false;
22548 }
22549}
22550
22559static pm_node_t *
22560parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
22561 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
22562 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
22563 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
22564 }
22565
22566 pm_node_t *node = parse_expression_prefix(parser, binding_power, flags, diag_id, depth);
22567
22568 // Some prefix nodes are statements and can only be followed by modifiers
22569 // (if/unless/while/until/rescue) or nothing at all. We check these cheaply
22570 // here before entering the infix loop.
22571 switch (PM_NODE_TYPE(node)) {
22572 case PM_ERROR_RECOVERY_NODE:
22573 return node;
22574 case PM_PRE_EXECUTION_NODE:
22575 return node;
22576 case PM_POST_EXECUTION_NODE:
22577 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
22578 case PM_ALIAS_METHOD_NODE:
22579 case PM_UNDEF_NODE:
22580 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
22581 return node;
22582 }
22583 break;
22584 case PM_CALL_NODE:
22585 case PM_SUPER_NODE:
22586 case PM_YIELD_NODE:
22587 case PM_DEF_NODE:
22588 if (parse_expression_terminator(parser, node)) {
22589 return node;
22590 }
22591 break;
22592 case PM_SYMBOL_NODE:
22593 if (pm_symbol_node_label_p(parser, node)) {
22594 return node;
22595 }
22596 break;
22597 default:
22598 break;
22599 }
22600
22601 // Look and see if the next token can be parsed as an infix operator. If it
22602 // can, then we'll parse it using parse_expression_infix.
22603 pm_binding_powers_t current_binding_powers;
22604 pm_token_type_t current_token_type;
22605
22606 while (
22607 current_token_type = parser->current.type,
22608 current_binding_powers = pm_binding_powers[current_token_type],
22609 binding_power <= current_binding_powers.left &&
22610 current_binding_powers.binary
22611 ) {
22612 node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, flags, (uint16_t) (depth + 1));
22613 if (parse_expression_terminator(parser, node)) return node;
22614
22615 // If the operator is nonassoc and we should not be able to parse the
22616 // upcoming infix operator, break.
22617 if (current_binding_powers.nonassoc) {
22618 // If we are about to parse another non-associative operator at the
22619 // same precedence as the one we just parsed, then we need to add an
22620 // error. This covers chaining the same operator (`1 == 2 == 3`) as
22621 // well as different operators that share a precedence, since they
22622 // are equally non-associative with one another (`1 == 2 != 3`,
22623 // `1...2..3`).
22624 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22625 if (next_binding_powers.nonassoc && next_binding_powers.left == current_binding_powers.left) {
22626 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));
22627 break;
22628 }
22629
22630 // If this is an endless range, then we need to reject a couple of
22631 // additional operators because it violates the normal operator
22632 // precedence rules. Those patterns are:
22633 //
22634 // 1.. & 2
22635 // 1.. * 2
22636 //
22637 if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) {
22638 if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) {
22639 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));
22640 break;
22641 }
22642
22643 if (PM_BINDING_POWER_TERM <= next_binding_powers.left) {
22644 break;
22645 }
22646 } else if (current_binding_powers.left <= next_binding_powers.left) {
22647 break;
22648 }
22649 }
22650
22651 if (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) {
22652 // A command-style method call is only accepted on method chains.
22653 // Thus, we check whether the parsed node can continue method chains.
22654 // The method chain can continue if the parsed node is one of the following five kinds:
22655 // (1) index access: foo[1]
22656 // (2) attribute access: foo.bar
22657 // (3) method call with parenthesis: foo.bar(1)
22658 // (4) method call with a block: foo.bar do end
22659 // (5) constant path: foo::Bar
22660 switch (node->type) {
22661 case PM_CALL_NODE: {
22662 pm_call_node_t *cast = (pm_call_node_t *)node;
22663 if (
22664 // (1) foo[1]
22665 !(
22666 cast->call_operator_loc.length == 0 &&
22667 cast->message_loc.length > 0 &&
22668 parser->start[cast->message_loc.start] == '[' &&
22669 parser->start[cast->message_loc.start + cast->message_loc.length - 1] == ']'
22670 ) &&
22671 // (2) foo.bar
22672 !(
22673 cast->call_operator_loc.length > 0 &&
22674 cast->arguments == NULL &&
22675 cast->block == NULL &&
22676 cast->opening_loc.length == 0
22677 ) &&
22678 // (3) foo.bar(1)
22679 !(
22680 cast->call_operator_loc.length > 0 &&
22681 cast->opening_loc.length > 0
22682 ) &&
22683 // (4) foo.bar do end
22684 !(
22685 cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
22686 )
22687 ) {
22688 flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
22689 }
22690 break;
22691 }
22692 // (5) foo::Bar
22693 case PM_CONSTANT_PATH_NODE:
22694 break;
22695 default:
22696 flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
22697 break;
22698 }
22699 }
22700
22701 if (context_terminator(parser->current_context->context, &parser->current)) {
22702 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22703 if (
22704 !next_binding_powers.binary ||
22705 binding_power > next_binding_powers.left ||
22706 (PM_NODE_TYPE_P(node, PM_CALL_NODE) && pm_call_node_command_p((pm_call_node_t *) node))
22707 ) {
22708 return node;
22709 }
22710 }
22711 }
22712
22713 return node;
22714}
22715
22720static pm_statements_node_t *
22721wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
22722 if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) {
22723 if (statements == NULL) {
22724 statements = pm_statements_node_create(parser);
22725 }
22726
22727 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22728 pm_arguments_node_arguments_append(
22729 parser->arena,
22730 arguments,
22731 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)))
22732 );
22733
22734 pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create(
22735 parser,
22736 arguments,
22737 pm_parser_constant_id_constant(parser, "print", 5)
22738 )), true);
22739 }
22740
22741 if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) {
22742 if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) {
22743 if (statements == NULL) {
22744 statements = pm_statements_node_create(parser);
22745 }
22746
22747 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22748 pm_arguments_node_arguments_append(
22749 parser->arena,
22750 arguments,
22751 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)))
22752 );
22753
22754 pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2));
22755 pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, UP(receiver), "split", arguments);
22756
22757 pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create(
22758 parser,
22759 pm_parser_constant_id_constant(parser, "$F", 2),
22760 UP(call)
22761 );
22762
22763 pm_statements_node_body_prepend(parser->arena, statements, UP(write));
22764 }
22765
22766 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22767 pm_arguments_node_arguments_append(
22768 parser->arena,
22769 arguments,
22770 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)))
22771 );
22772
22773 if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) {
22774 pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser);
22775 pm_keyword_hash_node_elements_append(parser->arena, keywords, UP(pm_assoc_node_create(
22776 parser,
22777 UP(pm_symbol_node_synthesized_create(parser, "chomp")),
22778 NULL,
22779 UP(pm_true_node_synthesized_create(parser))
22780 )));
22781
22782 pm_arguments_node_arguments_append(parser->arena, arguments, UP(keywords));
22783 pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS);
22784 }
22785
22786 pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
22787 pm_statements_node_body_append(parser, wrapped_statements, UP(pm_while_node_synthesized_create(
22788 parser,
22789 UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))),
22790 statements
22791 )), true);
22792
22793 statements = wrapped_statements;
22794 }
22795
22796 return statements;
22797}
22798
22802static pm_node_t *
22803parse_program(pm_parser_t *parser) {
22804 // If the current scope is NULL, then we want to push a new top level scope.
22805 // The current scope could exist in the event that we are parsing an eval
22806 // and the user has passed into scopes that already exist.
22807 if (parser->current_scope == NULL) {
22808 pm_parser_scope_push(parser, true);
22809 }
22810
22811 pm_node_list_t current_block_exits = { 0 };
22812 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
22813
22814 parser_lex(parser);
22815 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN, 0);
22816
22817 if (statements != NULL && !parser->parsing_eval) {
22818 // If we have statements, then the top-level statement should be
22819 // explicitly checked as well. We have to do this here because
22820 // everywhere else we check all but the last statement.
22821 assert(statements->body.size > 0);
22822 pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]);
22823 }
22824
22825 pm_constant_id_list_t locals;
22826 pm_locals_order(parser, &parser->current_scope->locals, &locals, true);
22827 pm_parser_scope_pop(parser);
22828
22829 // At the top level, see if we need to wrap the statements in a program
22830 // node with a while loop based on the options.
22831 if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
22832 statements = wrap_statements(parser, statements);
22833 } else {
22834 flush_block_exits(parser, previous_block_exits);
22835 }
22836
22837 // If this is an empty file, then we're still going to parse all of the
22838 // statements in order to gather up all of the comments and such. Here we'll
22839 // correct the location information.
22840 if (statements == NULL) {
22841 statements = pm_statements_node_create(parser);
22842 statements->base.location = (pm_location_t) { 0 };
22843 }
22844
22845 return UP(pm_program_node_create(parser, &locals, statements));
22846}
22847
22848/******************************************************************************/
22849/* External functions */
22850/******************************************************************************/
22851
22861static const char *
22862pm_strnstr(const char *big, const char *little, size_t big_length) {
22863 size_t little_length = strlen(little);
22864
22865 for (const char *max = big + big_length - little_length; big <= max; big++) {
22866 if (*big == *little && memcmp(big, little, little_length) == 0) return big;
22867 }
22868
22869 return NULL;
22870}
22871
22872#ifdef _WIN32
22873#define pm_parser_warn_shebang_carriage_return(parser, start, length) ((void) 0)
22874#else
22880static void
22881pm_parser_warn_shebang_carriage_return(pm_parser_t *parser, const uint8_t *start, size_t length) {
22882 if (length > 2 && start[length - 2] == '\r' && start[length - 1] == '\n') {
22883 pm_parser_warn(parser, U32(start - parser->start), U32(length), PM_WARN_SHEBANG_CARRIAGE_RETURN);
22884 }
22885}
22886#endif
22887
22892static void
22893pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const char *engine, size_t length) {
22894 const char *switches = pm_strnstr(engine, " -", length);
22895 if (switches == NULL) return;
22896
22897 pm_options_t next_options = *options;
22898 options->shebang_callback(
22899 &next_options,
22900 (const uint8_t *) (switches + 1),
22901 length - ((size_t) (switches - engine)) - 1,
22902 options->shebang_callback_data
22903 );
22904
22905 size_t encoding_length;
22906 if ((encoding_length = pm_string_length(&next_options.encoding)) > 0) {
22907 const uint8_t *encoding_source = pm_string_source(&next_options.encoding);
22908 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
22909 }
22910
22911 parser->command_line = next_options.command_line;
22912 parser->frozen_string_literal = next_options.frozen_string_literal;
22913}
22914
22918void
22919pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) {
22920 assert(arena != NULL);
22921 assert(source != NULL);
22922
22923 *parser = (pm_parser_t) {
22924 .arena = arena,
22925 .metadata_arena = { 0 },
22926 .node_id = 0,
22927 .lex_state = PM_LEX_STATE_BEG,
22928 .enclosure_nesting = 0,
22929 .lambda_enclosure_nesting = -1,
22930 .brace_nesting = 0,
22931 .do_loop_stack = 0,
22932 .accepts_block_stack = 0,
22933 .lex_modes = {
22934 .index = 0,
22935 .stack = {{ .mode = PM_LEX_DEFAULT }},
22936 .current = &parser->lex_modes.stack[0],
22937 },
22938 .start = source,
22939 .end = source + size,
22940 .previous = { .type = PM_TOKEN_EOF, .start = source, .end = source },
22941 .current = { .type = PM_TOKEN_EOF, .start = source, .end = source },
22942 .next_start = NULL,
22943 .heredoc_end = NULL,
22944 .data_loc = { 0 },
22945 .comment_list = { 0 },
22946 .magic_comment_list = { 0 },
22947 .warning_list = { 0 },
22948 .error_list = { 0 },
22949 .current_scope = NULL,
22950 .current_context = NULL,
22951 .encoding = PM_ENCODING_UTF_8_ENTRY,
22952 .encoding_changed_callback = NULL,
22953 .encoding_comment_start = source,
22954 .lex_callback = { 0 },
22955 .filepath = { 0 },
22956 .constant_pool = { 0 },
22957 .line_offsets = { 0 },
22958 .integer = { 0 },
22959 .current_string = PM_STRING_EMPTY,
22960 .start_line = 1,
22961 .explicit_encoding = NULL,
22962 .command_line = 0,
22963 .parsing_eval = false,
22964 .partial_script = false,
22965 .command_start = true,
22966 .recovering = false,
22967 .continuable = true,
22968 .encoding_locked = false,
22969 .encoding_changed = false,
22970 .pattern_matching_newlines = false,
22971 .in_keyword_arg = false,
22972 .current_block_exits = NULL,
22973 .semantic_token_seen = false,
22974 .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET,
22975 .warn_mismatched_indentation = true
22976 };
22977
22978 /* Pre-size the arenas based on input size to reduce the number of block
22979 * allocations (and the kernel page zeroing they trigger). The ratios were
22980 * measured empirically: AST arena ~3.3x input, metadata arena ~1.1x input.
22981 * The reserve call is a no-op when the capacity is at or below the default
22982 * arena block size, so small inputs don't waste an extra allocation. */
22983 if (size <= SIZE_MAX / 4) pm_arena_reserve(arena, size * 4);
22984 if (size <= SIZE_MAX / 5 * 4) pm_arena_reserve(&parser->metadata_arena, size + size / 4);
22985
22986 /* Initialize the constant pool. Measured across 1532 Ruby stdlib files, the
22987 * bytes/constant ratio has a median of ~56 and a 90th percentile of ~135.
22988 * We use 120 as a balance between over-allocation waste and resize
22989 * frequency. Resizes are cheap with arena allocation, so we lean toward
22990 * under-estimating. */
22991 uint32_t constant_size = ((uint32_t) size) / 120;
22992 pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size < 4 ? 4 : constant_size);
22993
22994 /* Initialize the line offset list. Similar to the constant pool, we are
22995 * going to estimate the number of newlines that we will need based on the
22996 * size of the input. */
22997 size_t newline_size = size / 22;
22998 pm_line_offset_list_init(&parser->metadata_arena, &parser->line_offsets, newline_size < 4 ? 4 : newline_size);
22999
23000 // If options were provided to this parse, establish them here.
23001 if (options != NULL) {
23002 // filepath option
23003 parser->filepath = options->filepath;
23004
23005 // line option
23006 parser->start_line = options->line;
23007
23008 // encoding option
23009 size_t encoding_length = pm_string_length(&options->encoding);
23010 if (encoding_length > 0) {
23011 const uint8_t *encoding_source = pm_string_source(&options->encoding);
23012 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
23013 }
23014
23015 // encoding_locked option
23016 parser->encoding_locked = options->encoding_locked;
23017
23018 // frozen_string_literal option
23019 parser->frozen_string_literal = options->frozen_string_literal;
23020
23021 // command_line option
23022 parser->command_line = options->command_line;
23023
23024 // version option
23025 parser->version = options->version;
23026
23027 // partial_script
23028 parser->partial_script = options->partial_script;
23029
23030 // scopes option
23031 parser->parsing_eval = options->scopes_count > 0;
23032 if (parser->parsing_eval) parser->warn_mismatched_indentation = false;
23033
23034 for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
23035 const pm_options_scope_t *scope = pm_options_scope(options, scope_index);
23036 pm_parser_scope_push(parser, scope_index == 0);
23037
23038 // Scopes given from the outside are not allowed to have numbered
23039 // parameters.
23040 parser->current_scope->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED;
23041
23042 for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
23043 const pm_string_t *local = pm_options_scope_local(scope, local_index);
23044
23045 const uint8_t *source = pm_string_source(local);
23046 size_t length = pm_string_length(local);
23047
23048 uint8_t *allocated = (uint8_t *) pm_arena_alloc(&parser->metadata_arena, length, 1);
23049 memcpy(allocated, source, length);
23050 pm_parser_local_add_owned(parser, allocated, length);
23051 }
23052 }
23053 }
23054
23055 // Now that we have established the user-provided options, check if
23056 // a version was given and parse as the latest version otherwise.
23057 if (parser->version == PM_OPTIONS_VERSION_UNSET) {
23058 parser->version = PM_OPTIONS_VERSION_LATEST;
23059 }
23060
23061 pm_accepts_block_stack_push(parser, true);
23062
23063 // Skip past the UTF-8 BOM if it exists.
23064 if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) {
23065 parser->current.end += 3;
23066 parser->encoding_comment_start += 3;
23067
23068 if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
23069 parser->encoding = PM_ENCODING_UTF_8_ENTRY;
23070 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
23071 }
23072 }
23073
23074 // If the -x command line flag is set, or the first shebang of the file does
23075 // not include "ruby", then we'll search for a shebang that does include
23076 // "ruby" and start parsing from there.
23077 bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
23078
23079 // If the first two bytes of the source are a shebang, then we will do a bit
23080 // of extra processing.
23081 //
23082 // First, we'll indicate that the encoding comment is at the end of the
23083 // shebang. This means that when a shebang is present the encoding comment
23084 // can begin on the second line.
23085 //
23086 // Second, we will check if the shebang includes "ruby". If it does, then we
23087 // we will start parsing from there. We will also potentially warning the
23088 // user if there is a carriage return at the end of the shebang. We will
23089 // also potentially call the shebang callback if this is the main script to
23090 // allow the caller to parse the shebang and find any command-line options.
23091 // If the shebang does not include "ruby" and this is the main script being
23092 // parsed, then we will start searching the file for a shebang that does
23093 // contain "ruby" as if -x were passed on the command line.
23094 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
23095 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end);
23096
23097 if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') {
23098 const char *engine;
23099
23100 if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) {
23101 if (newline != NULL) {
23102 parser->encoding_comment_start = newline + 1;
23103
23104 if (options == NULL || options->main_script) {
23105 pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1);
23106 }
23107 }
23108
23109 if (options != NULL && options->main_script && options->shebang_callback != NULL) {
23110 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start)));
23111 }
23112
23113 search_shebang = false;
23114 } else if (options != NULL && options->main_script && !parser->parsing_eval) {
23115 search_shebang = true;
23116 }
23117 }
23118
23119 // Here we're going to find the first shebang that includes "ruby" and start
23120 // parsing from there.
23121 if (search_shebang) {
23122 // If a shebang that includes "ruby" is not found, then we're going to a
23123 // a load error to the list of errors on the parser.
23124 bool found_shebang = false;
23125
23126 // This is going to point to the start of each line as we check it.
23127 // We'll maintain a moving window looking at each line at they come.
23128 const uint8_t *cursor = parser->start;
23129
23130 // The newline pointer points to the end of the current line that we're
23131 // considering. If it is NULL, then we're at the end of the file.
23132 const uint8_t *newline = next_newline(cursor, parser->end - cursor);
23133
23134 while (newline != NULL) {
23135 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
23136
23137 cursor = newline + 1;
23138 newline = next_newline(cursor, parser->end - cursor);
23139
23140 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - cursor);
23141 if (length > 2 && cursor[0] == '#' && cursor[1] == '!') {
23142 const char *engine;
23143 if ((engine = pm_strnstr((const char *) cursor, "ruby", length)) != NULL) {
23144 found_shebang = true;
23145
23146 if (newline != NULL) {
23147 pm_parser_warn_shebang_carriage_return(parser, cursor, length + 1);
23148 parser->encoding_comment_start = newline + 1;
23149 }
23150
23151 if (options != NULL && options->shebang_callback != NULL) {
23152 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) cursor)));
23153 }
23154
23155 break;
23156 }
23157 }
23158 }
23159
23160 if (found_shebang) {
23161 parser->previous = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23162 parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23163 } else {
23164 pm_parser_err(parser, 0, 0, PM_ERR_SCRIPT_NOT_FOUND);
23165 pm_line_offset_list_clear(&parser->line_offsets);
23166 }
23167 }
23168
23169 // The encoding comment can start after any amount of inline whitespace, so
23170 // here we'll advance it to the first non-inline-whitespace character so
23171 // that it is ready for future comparisons.
23172 parser->encoding_comment_start += pm_strspn_inline_whitespace(parser->encoding_comment_start, parser->end - parser->encoding_comment_start);
23173}
23174
23183pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) {
23184 pm_parser_t *parser = (pm_parser_t *) xmalloc(sizeof(pm_parser_t));
23185 if (parser == NULL) abort();
23186
23187 pm_parser_init(arena, parser, source, size, options);
23188 return parser;
23189}
23190
23194void
23195pm_parser_cleanup(pm_parser_t *parser) {
23196 pm_string_cleanup(&parser->filepath);
23197 pm_arena_cleanup(&parser->metadata_arena);
23198
23199 while (parser->current_scope != NULL) {
23200 // Normally, popping the scope doesn't free the locals since it is
23201 // assumed that ownership has transferred to the AST. However if we have
23202 // scopes while we're freeing the parser, it's likely they came from
23203 // eval scopes and we need to free them explicitly here.
23204 pm_parser_scope_pop(parser);
23205 }
23206
23207 while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
23208 lex_mode_pop(parser);
23209 }
23210}
23211
23215void
23217 pm_parser_cleanup(parser);
23218 xfree_sized(parser, sizeof(pm_parser_t));
23219}
23220
23226static bool
23227pm_parse_err_is_fatal(pm_diagnostic_id_t diag_id) {
23228 switch (diag_id) {
23229 case PM_ERR_ARRAY_EXPRESSION_AFTER_STAR:
23230 case PM_ERR_BEGIN_UPCASE_BRACE:
23231 case PM_ERR_CLASS_VARIABLE_BARE:
23232 case PM_ERR_END_UPCASE_BRACE:
23233 case PM_ERR_ESCAPE_INVALID_HEXADECIMAL:
23234 case PM_ERR_ESCAPE_INVALID_UNICODE_LIST:
23235 case PM_ERR_ESCAPE_INVALID_UNICODE_SHORT:
23236 case PM_ERR_EXPRESSION_NOT_WRITABLE:
23237 case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF:
23238 case PM_ERR_FLOAT_PARSE:
23239 case PM_ERR_GLOBAL_VARIABLE_BARE:
23240 case PM_ERR_HASH_KEY:
23241 case PM_ERR_HEREDOC_IDENTIFIER:
23242 case PM_ERR_INSTANCE_VARIABLE_BARE:
23243 case PM_ERR_INVALID_BLOCK_EXIT:
23244 case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT:
23245 case PM_ERR_INVALID_FLOAT_EXPONENT:
23246 case PM_ERR_INVALID_NUMBER_BINARY:
23247 case PM_ERR_INVALID_NUMBER_DECIMAL:
23248 case PM_ERR_INVALID_NUMBER_HEXADECIMAL:
23249 case PM_ERR_INVALID_NUMBER_OCTAL:
23250 case PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING:
23251 case PM_ERR_NO_LOCAL_VARIABLE:
23252 case PM_ERR_PARAMETER_ORDER:
23253 case PM_ERR_STATEMENT_UNDEF:
23254 case PM_ERR_VOID_EXPRESSION:
23255 return true;
23256 default:
23257 return false;
23258 }
23259}
23260
23294static void
23295pm_parse_continuable(pm_parser_t *parser) {
23296 // If there are no errors then there is nothing to continue.
23297 if (parser->error_list.size == 0) {
23298 parser->continuable = false;
23299 return;
23300 }
23301
23302 if (!parser->continuable) return;
23303
23304 size_t source_length = (size_t) (parser->end - parser->start);
23305
23306 // First pass: check if there are any non-stray, non-fatal errors.
23307 bool has_non_stray_error = false;
23308 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23309 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)) {
23310 has_non_stray_error = true;
23311 break;
23312 }
23313 }
23314
23315 // Second pass: check each error. We track the minimum source position
23316 // among non-stray, non-fatal errors seen so far in list order, which
23317 // lets us detect cascade stray tokens.
23318 size_t non_stray_min_start = SIZE_MAX;
23319
23320 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23321 size_t error_start = (size_t) error->location.start;
23322 size_t error_end = error_start + (size_t) error->location.length;
23323 bool at_eof = error_end >= source_length;
23324
23325 // Fatal errors are non-continuable unless they occur at EOF.
23326 if (pm_parse_err_is_fatal(error->diag_id) && !at_eof) {
23327 parser->continuable = false;
23328 return;
23329 }
23330
23331 // Track non-stray, non-fatal error positions in list order.
23332 if (error->diag_id != PM_ERR_UNEXPECTED_TOKEN_IGNORE &&
23333 error->diag_id != PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT) {
23334 if (error_start < non_stray_min_start) non_stray_min_start = error_start;
23335 continue;
23336 }
23337
23338 // This is a stray token. Determine if it is a cascade effect
23339 // of a preceding error or genuinely stray.
23340
23341 // Rule (a): a non-stray error was seen earlier in the list at a
23342 // strictly earlier position — this stray is a cascade effect.
23343 if (non_stray_min_start < error_start) continue;
23344
23345 // Rule (b): this stray is at EOF with valid code before it.
23346 // Single-byte stray tokens at EOF (like `\` for line continuation)
23347 // are likely truncated tokens. Multi-byte stray tokens (like the
23348 // keyword `end`) need additional evidence that they are cascade
23349 // effects (i.e. non-stray errors exist elsewhere).
23350 if (at_eof && error_start > 0) {
23351 // Exception: closing delimiters at EOF are genuinely stray.
23352 if (error->location.length == 1) {
23353 const uint8_t *byte = parser->start + error_start;
23354 if (*byte == ')' || *byte == ']' || *byte == '}') {
23355 parser->continuable = false;
23356 return;
23357 }
23358
23359 // Single-byte non-delimiter stray at EOF: cascade.
23360 continue;
23361 }
23362
23363 // Multi-byte stray at EOF: cascade only if there are
23364 // non-stray errors (evidence of a preceding parse failure).
23365 if (has_non_stray_error) continue;
23366 }
23367
23368 // Rule (c): a stray `=` at the start of a line could be the
23369 // beginning of an embedded document (`=begin`). The remaining
23370 // bytes after `=` parse as an identifier, so the error is not
23371 // at EOF, but the construct is genuinely incomplete.
23372 if (error->location.length == 1) {
23373 const uint8_t *byte = parser->start + error_start;
23374 if (*byte == '=' && (error_start == 0 || *(byte - 1) == '\n')) continue;
23375 }
23376
23377 // This stray token is genuinely non-continuable.
23378 parser->continuable = false;
23379 return;
23380 }
23381}
23382
23386pm_node_t *
23388 pm_node_t *node = parse_program(parser);
23389 pm_parse_continuable(parser);
23390 return node;
23391}
23392
23399pm_node_t *
23400pm_parse_stream(pm_parser_t **parser, pm_arena_t *arena, pm_source_t *source, const pm_options_t *options) {
23401 bool eof = pm_source_stream_read(source);
23402
23403 pm_parser_t *tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23404 pm_node_t *node = pm_parse(tmp);
23405
23406 while (!eof && tmp->error_list.size > 0) {
23407 eof = pm_source_stream_read(source);
23408
23409 pm_parser_free(tmp);
23410 pm_arena_cleanup(arena);
23411
23412 tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23413 node = pm_parse(tmp);
23414 }
23415
23416 *parser = tmp;
23417 return node;
23418}
23419
23420#undef PM_CASE_KEYWORD
23421#undef PM_CASE_OPERATOR
23422#undef PM_CASE_WRITABLE
23423#undef PM_STRING_EMPTY
23424
23425// We optionally support serializing to a binary string. For systems that don't
23426// want or need this functionality, it can be turned off with the
23427// PRISM_EXCLUDE_SERIALIZATION define.
23428#ifndef PRISM_EXCLUDE_SERIALIZATION
23429
23430static PRISM_INLINE void
23431pm_serialize_header(pm_buffer_t *buffer) {
23432 pm_buffer_append_string(buffer, "PRISM", 5);
23433 pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR);
23434 pm_buffer_append_byte(buffer, PRISM_VERSION_MINOR);
23435 pm_buffer_append_byte(buffer, PRISM_VERSION_PATCH);
23436 pm_buffer_append_byte(buffer, PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS ? 1 : 0);
23437}
23438
23442void
23443pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
23444 pm_serialize_header(buffer);
23445 pm_serialize_content(parser, node, buffer);
23446 pm_buffer_append_byte(buffer, '\0');
23447}
23448
23453void
23454pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23455 pm_options_t options = { 0 };
23456 pm_options_read(&options, data);
23457
23458 pm_arena_t arena = { 0 };
23459 pm_parser_t parser;
23460 pm_parser_init(&arena, &parser, source, size, &options);
23461
23462 pm_node_t *node = pm_parse(&parser);
23463
23464 pm_serialize_header(buffer);
23465 pm_serialize_content(&parser, node, buffer);
23466 pm_buffer_append_byte(buffer, '\0');
23467
23468 pm_parser_cleanup(&parser);
23469 pm_arena_cleanup(&arena);
23470 pm_options_cleanup(&options);
23471}
23472
23477void
23478pm_serialize_parse_stream(pm_buffer_t *buffer, pm_source_t *source, const char *data) {
23479 pm_arena_t arena = { 0 };
23480 pm_parser_t *parser;
23481 pm_options_t options = { 0 };
23482 pm_options_read(&options, data);
23483
23484 pm_node_t *node = pm_parse_stream(&parser, &arena, source, &options);
23485 pm_serialize_header(buffer);
23486 pm_serialize_content(parser, node, buffer);
23487 pm_buffer_append_byte(buffer, '\0');
23488
23489 pm_parser_free(parser);
23490 pm_arena_cleanup(&arena);
23491 pm_options_cleanup(&options);
23492}
23493
23502int8_t
23503pm_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) {
23504 pm_options_t options = { 0 };
23505 pm_options_read(&options, data);
23506
23507 pm_arena_t arena = { 0 };
23508 pm_parser_t parser;
23509 pm_parser_init(&arena, &parser, source, size, &options);
23510
23511 pm_parse(&parser);
23512
23513 int8_t result = -1;
23514 if (parser.error_list.size > 0) {
23515 const char *encoding_name = parser.encoding->name;
23516 pm_buffer_append_string(buffer, encoding_name, strlen(encoding_name));
23517 pm_buffer_append_byte(buffer, '\0');
23518
23519 result = (int8_t) pm_errors_format(&parser, buffer, format_type);
23520 }
23521
23522 pm_parser_cleanup(&parser);
23523 pm_arena_cleanup(&arena);
23524 pm_options_cleanup(&options);
23525
23526 return result;
23527}
23528
23532void
23533pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23534 pm_options_t options = { 0 };
23535 pm_options_read(&options, data);
23536
23537 pm_arena_t arena = { 0 };
23538 pm_parser_t parser;
23539 pm_parser_init(&arena, &parser, source, size, &options);
23540
23541 pm_parse(&parser);
23542 pm_serialize_header(buffer);
23543 pm_serialize_encoding(parser.encoding, buffer);
23544 pm_buffer_append_varsint(buffer, parser.start_line);
23545 pm_serialize_line_offset_list(&parser.line_offsets, buffer);
23546 pm_serialize_comment_list(&parser.comment_list, buffer);
23547
23548 pm_parser_cleanup(&parser);
23549 pm_arena_cleanup(&arena);
23550 pm_options_cleanup(&options);
23551}
23552
23553#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_EXPORTED_FUNCTION PRISM_NODISCARD 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:23183
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:23216
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:23387
#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:1309
PM_NODE_ALIGNAS struct pm_node * left
AndNode::left.
Definition ast.h:1324
PM_NODE_ALIGNAS struct pm_node * right
AndNode::right.
Definition ast.h:1337
ArgumentsNode.
Definition ast.h:1369
pm_node_t base
The embedded base node.
Definition ast.h:1371
struct pm_node_list arguments
ArgumentsNode::arguments.
Definition ast.h:1381
This is a special out parameter to the parse_arguments_list function that includes opening and closin...
Definition prism.c:1774
pm_node_t * block
The optional block attached to the call.
Definition prism.c:1785
bool has_forwarding
The flag indicating whether this arguments list has forwarding argument.
Definition prism.c:1788
pm_location_t opening_loc
The optional location of the opening parenthesis or bracket.
Definition prism.c:1776
pm_arguments_node_t * arguments
The lazily-allocated optional arguments node.
Definition prism.c:1779
pm_location_t closing_loc
The optional location of the closing parenthesis or bracket.
Definition prism.c:1782
ArrayNode.
Definition ast.h:1399
struct pm_node_list elements
ArrayNode::elements.
Definition ast.h:1408
ArrayPatternNode.
Definition ast.h:1459
PM_NODE_ALIGNAS struct pm_node * constant
ArrayPatternNode::constant.
Definition ast.h:1477
pm_location_t opening_loc
ArrayPatternNode::opening_loc.
Definition ast.h:1517
pm_location_t closing_loc
ArrayPatternNode::closing_loc.
Definition ast.h:1527
AssocNode.
Definition ast.h:1542
PM_NODE_ALIGNAS struct pm_node * value
AssocNode::value.
Definition ast.h:1573
PM_NODE_ALIGNAS struct pm_node * key
AssocNode::key.
Definition ast.h:1560
AssocSplatNode.
Definition ast.h:1598
BeginNode.
Definition ast.h:1665
PM_NODE_ALIGNAS struct pm_else_node * else_clause
BeginNode::else_clause.
Definition ast.h:1707
PM_NODE_ALIGNAS struct pm_ensure_node * ensure_clause
BeginNode::ensure_clause.
Definition ast.h:1717
PM_NODE_ALIGNAS struct pm_statements_node * statements
BeginNode::statements.
Definition ast.h:1687
PM_NODE_ALIGNAS struct pm_rescue_node * rescue_clause
BeginNode::rescue_clause.
Definition ast.h:1697
pm_node_t base
The embedded base node.
Definition ast.h:1667
This struct represents a set of binding powers used for a given token.
Definition prism.c:12615
bool binary
Whether or not this token can be used as a binary operator.
Definition prism.c:12623
pm_binding_power_t left
The left binding power.
Definition prism.c:12617
bool nonassoc
Whether or not this token can be used as non-associative binary operator.
Definition prism.c:12629
pm_binding_power_t right
The right binding power.
Definition prism.c:12620
BlockLocalVariableNode.
Definition ast.h:1782
BlockNode.
Definition ast.h:1809
BlockParametersNode.
Definition ast.h:1937
CallNode.
Definition ast.h:2161
pm_location_t opening_loc
CallNode::opening_loc.
Definition ast.h:2222
pm_location_t closing_loc
CallNode::closing_loc.
Definition ast.h:2242
pm_constant_id_t name
CallNode::name.
Definition ast.h:2202
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
CallNode::arguments.
Definition ast.h:2232
pm_location_t equal_loc
CallNode::equal_loc.
Definition ast.h:2255
pm_location_t call_operator_loc
CallNode::call_operator_loc.
Definition ast.h:2192
pm_location_t message_loc
CallNode::message_loc.
Definition ast.h:2212
PM_NODE_ALIGNAS struct pm_node * block
CallNode::block.
Definition ast.h:2265
PM_NODE_ALIGNAS struct pm_node * receiver
CallNode::receiver.
Definition ast.h:2179
CaseMatchNode.
Definition ast.h:2596
struct pm_node_list conditions
CaseMatchNode::conditions.
Definition ast.h:2618
PM_NODE_ALIGNAS struct pm_else_node * else_clause
CaseMatchNode::else_clause.
Definition ast.h:2628
CaseNode.
Definition ast.h:2665
PM_NODE_ALIGNAS struct pm_else_node * else_clause
CaseNode::else_clause.
Definition ast.h:2697
struct pm_node_list conditions
CaseNode::conditions.
Definition ast.h:2687
ClassVariableReadNode.
Definition ast.h:2954
ClassVariableTargetNode.
Definition ast.h:2982
ClassVariableWriteNode.
Definition ast.h:3004
A list of constant IDs.
size_t size
The number of constant ids in the list.
ConstantPathNode.
Definition ast.h:3213
ConstantPathTargetNode.
Definition ast.h:3348
ConstantReadNode.
Definition ast.h:3441
ConstantTargetNode.
Definition ast.h:3469
ConstantWriteNode.
Definition ast.h:3491
DefNode.
Definition ast.h:3553
pm_location_t equal_loc
DefNode::equal_loc.
Definition ast.h:3610
PM_NODE_ALIGNAS struct pm_node * body
DefNode::body.
Definition ast.h:3580
ElseNode.
Definition ast.h:3667
PM_NODE_ALIGNAS struct pm_statements_node * statements
ElseNode::statements.
Definition ast.h:3679
EnsureNode.
Definition ast.h:3762
PM_NODE_ALIGNAS struct pm_statements_node * statements
EnsureNode::statements.
Definition ast.h:3774
FindPatternNode.
Definition ast.h:3841
pm_location_t opening_loc
FindPatternNode::opening_loc.
Definition ast.h:3905
PM_NODE_ALIGNAS struct pm_node * constant
FindPatternNode::constant.
Definition ast.h:3853
pm_location_t closing_loc
FindPatternNode::closing_loc.
Definition ast.h:3918
FlipFlopNode.
Definition ast.h:3936
FloatNode.
Definition ast.h:3968
double value
FloatNode::value.
Definition ast.h:3977
pm_node_t base
The embedded base node.
Definition ast.h:3970
ForwardingParameterNode.
Definition ast.h:4101
GlobalVariableReadNode.
Definition ast.h:4274
GlobalVariableTargetNode.
Definition ast.h:4302
GlobalVariableWriteNode.
Definition ast.h:4324
HashNode.
Definition ast.h:4385
struct pm_node_list elements
HashNode::elements.
Definition ast.h:4410
HashPatternNode.
Definition ast.h:4444
PM_NODE_ALIGNAS struct pm_node * constant
HashPatternNode::constant.
Definition ast.h:4459
pm_location_t opening_loc
HashPatternNode::opening_loc.
Definition ast.h:4498
pm_location_t closing_loc
HashPatternNode::closing_loc.
Definition ast.h:4511
IfNode.
Definition ast.h:4532
PM_NODE_ALIGNAS struct pm_statements_node * statements
IfNode::statements.
Definition ast.h:4591
PM_NODE_ALIGNAS struct pm_node * subsequent
IfNode::subsequent.
Definition ast.h:4610
ImaginaryNode.
Definition ast.h:4637
InNode.
Definition ast.h:4713
PM_NODE_ALIGNAS struct pm_statements_node * statements
InNode::statements.
Definition ast.h:4725
InstanceVariableReadNode.
Definition ast.h:5116
InstanceVariableTargetNode.
Definition ast.h:5144
InstanceVariableWriteNode.
Definition ast.h:5166
IntegerNode.
Definition ast.h:5233
pm_integer_t value
IntegerNode::value.
Definition ast.h:5242
pm_node_t base
The embedded base node.
Definition ast.h:5235
bool negative
Whether or not the integer is negative.
Definition integer.h:38
InterpolatedMatchLastLineNode.
Definition ast.h:5270
InterpolatedRegularExpressionNode.
Definition ast.h:5315
InterpolatedStringNode.
Definition ast.h:5351
pm_node_t base
The embedded base node.
Definition ast.h:5353
pm_location_t opening_loc
InterpolatedStringNode::opening_loc.
Definition ast.h:5358
InterpolatedSymbolNode.
Definition ast.h:5383
InterpolatedXStringNode.
Definition ast.h:5415
pm_location_t opening_loc
InterpolatedXStringNode::opening_loc.
Definition ast.h:5422
pm_node_t base
The embedded base node.
Definition ast.h:5417
struct pm_node_list parts
InterpolatedXStringNode::parts.
Definition ast.h:5427
KeywordHashNode.
Definition ast.h:5484
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:5720
uint32_t depth
LocalVariableReadNode::depth.
Definition ast.h:5750
pm_constant_id_t name
LocalVariableReadNode::name.
Definition ast.h:5737
LocalVariableTargetNode.
Definition ast.h:5768
LocalVariableWriteNode.
Definition ast.h:5795
uint32_t depth
LocalVariableWriteNode::depth.
Definition ast.h:5821
pm_constant_id_t name
LocalVariableWriteNode::name.
Definition ast.h:5808
This struct represents a slice in the source code, defined by an offset and a length.
Definition ast.h:572
uint32_t start
The offset of the location from the start of the source.
Definition ast.h:574
uint32_t length
The length of the location.
Definition ast.h:577
MatchLastLineNode.
Definition ast.h:5886
struct pm_node_list targets
MatchWriteNode::targets.
Definition ast.h:6053
MultiTargetNode.
Definition ast.h:6120
pm_location_t lparen_loc
MultiTargetNode::lparen_loc.
Definition ast.h:6177
struct pm_node_list lefts
MultiTargetNode::lefts.
Definition ast.h:6137
pm_location_t rparen_loc
MultiTargetNode::rparen_loc.
Definition ast.h:6187
MultiWriteNode.
Definition ast.h:6202
A list of nodes in the source, most often used for lists of children.
Definition ast.h:585
size_t size
The number of nodes in the list.
Definition ast.h:587
struct pm_node ** nodes
The nodes in the list.
Definition ast.h:593
This is the base structure that represents a node in the syntax tree.
Definition ast.h:1083
pm_node_type_t type
This represents the type of the node.
Definition ast.h:1088
pm_location_t location
This is the location of the node in the source.
Definition ast.h:1106
OptionalParameterNode.
Definition ast.h:6496
OrNode.
Definition ast.h:6533
PM_NODE_ALIGNAS struct pm_node * right
OrNode::right.
Definition ast.h:6561
PM_NODE_ALIGNAS struct pm_node * left
OrNode::left.
Definition ast.h:6548
ParametersNode.
Definition ast.h:6587
PM_NODE_ALIGNAS struct pm_node * block
ParametersNode::block.
Definition ast.h:6624
PM_NODE_ALIGNAS struct pm_node * rest
ParametersNode::rest.
Definition ast.h:6604
PM_NODE_ALIGNAS struct pm_node * keyword_rest
ParametersNode::keyword_rest.
Definition ast.h:6619
ParenthesesNode.
Definition ast.h:6642
PM_NODE_ALIGNAS struct pm_node * body
ParenthesesNode::body.
Definition ast.h:6649
RangeNode.
Definition ast.h:6872
PM_NODE_ALIGNAS struct pm_node * right
RangeNode::right.
Definition ast.h:6901
PM_NODE_ALIGNAS struct pm_node * left
RangeNode::left.
Definition ast.h:6887
RationalNode.
Definition ast.h:6929
pm_node_t base
The embedded base node.
Definition ast.h:6931
pm_integer_t numerator
RationalNode::numerator.
Definition ast.h:6940
In order to properly set a regular expression's encoding and to validate the byte sequence for the un...
Definition prism.c:9847
pm_buffer_t regexp_buffer
The buffer holding the regexp source.
Definition prism.c:9852
pm_token_buffer_t base
The embedded base buffer.
Definition prism.c:9849
RegularExpressionNode.
Definition ast.h:6994
RequiredParameterNode.
Definition ast.h:7066
RescueModifierNode.
Definition ast.h:7088
PM_NODE_ALIGNAS struct pm_node * rescue_expression
RescueModifierNode::rescue_expression.
Definition ast.h:7105
RescueNode.
Definition ast.h:7125
PM_NODE_ALIGNAS struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7162
pm_location_t then_keyword_loc
RescueNode::then_keyword_loc.
Definition ast.h:7152
SplatNode.
Definition ast.h:7415
PM_NODE_ALIGNAS struct pm_node * expression
SplatNode::expression.
Definition ast.h:7427
StatementsNode.
Definition ast.h:7442
struct pm_node_list body
StatementsNode::body.
Definition ast.h:7449
pm_node_t base
The embedded base node.
Definition ast.h:7444
StringNode.
Definition ast.h:7476
pm_node_t base
The embedded base node.
Definition ast.h:7478
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7498
pm_location_t content_loc
StringNode::content_loc.
Definition ast.h:7488
pm_location_t closing_loc
StringNode::closing_loc.
Definition ast.h:7493
pm_location_t opening_loc
StringNode::opening_loc.
Definition ast.h:7483
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:7518
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
SuperNode::arguments.
Definition ast.h:7537
pm_location_t lparen_loc
SuperNode::lparen_loc.
Definition ast.h:7530
PM_NODE_ALIGNAS struct pm_node * block
SuperNode::block.
Definition ast.h:7547
SymbolNode.
Definition ast.h:7570
pm_location_t value_loc
SymbolNode::value_loc.
Definition ast.h:7582
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7592
When we're lexing certain types (strings, symbols, lists, etc.) we have string content associated wit...
Definition prism.c:9821
pm_buffer_t buffer
The buffer that we're using to keep track of the string content.
Definition prism.c:9826
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:9832
This struct represents a token in the Ruby source.
Definition ast.h:544
const uint8_t * end
A pointer to the end location of the token in the source.
Definition ast.h:552
const uint8_t * start
A pointer to the start location of the token in the source.
Definition ast.h:549
pm_token_type_t type
The type of the token.
Definition ast.h:546
UndefNode.
Definition ast.h:7624
UnlessNode.
Definition ast.h:7654
PM_NODE_ALIGNAS struct pm_statements_node * statements
UnlessNode::statements.
Definition ast.h:7703
PM_NODE_ALIGNAS struct pm_else_node * else_clause
UnlessNode::else_clause.
Definition ast.h:7713
WhenNode.
Definition ast.h:7788
PM_NODE_ALIGNAS struct pm_statements_node * statements
WhenNode::statements.
Definition ast.h:7810
XStringNode.
Definition ast.h:7877
YieldNode.
Definition ast.h:7914
pm_location_t lparen_loc
YieldNode::lparen_loc.
Definition ast.h:7926
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
YieldNode::arguments.
Definition ast.h:7931
#define PRISM_UNUSED
GCC will warn if you specify a function or parameter that is unused at runtime.
Definition unused.h:13