Ruby 4.1.0dev (2026-09-29 revision 5fe7d61b2050ab8e130316fee458320cba1c0cfe)
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->content_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)->content_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
17638static bool
17639parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
17640 pm_parser_t *parser = (pm_parser_t *) data;
17641
17642 switch (PM_NODE_TYPE(node)) {
17643 case PM_LOCAL_VARIABLE_TARGET_NODE:
17644 // Underscore-prefixed names are not captures, see
17645 // parse_pattern_capture.
17646 if (peek_at(parser, parser->start + PM_NODE_START(node)) != '_') {
17647 pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
17648 }
17649 return false;
17650 case PM_ARRAY_PATTERN_NODE:
17651 case PM_ASSOC_NODE:
17652 case PM_ASSOC_SPLAT_NODE:
17653 case PM_CAPTURE_PATTERN_NODE:
17654 case PM_FIND_PATTERN_NODE:
17655 case PM_HASH_PATTERN_NODE:
17656 case PM_IMPLICIT_NODE:
17657 case PM_PARENTHESES_NODE:
17658 case PM_SPLAT_NODE:
17659 return true;
17660 default:
17661 return false;
17662 }
17663}
17664
17670static void
17671parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) {
17672 pm_visit_node(node, parse_pattern_alternation_error_each, parser);
17673}
17674
17679static pm_node_t *
17680parse_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) {
17681 pm_node_t *node = first_node;
17682 bool alternation = false;
17683
17684 while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) {
17685 if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) {
17686 parse_pattern_alternation_error(parser, node);
17687 }
17688
17689 switch (parser->current.type) {
17690 case PM_TOKEN_IDENTIFIER:
17691 case PM_TOKEN_BRACKET_LEFT_ARRAY:
17692 case PM_TOKEN_BRACE_LEFT_HASH:
17693 case PM_TOKEN_CARET:
17694 case PM_TOKEN_CONSTANT:
17695 case PM_TOKEN_UCOLON_COLON:
17696 case PM_TOKEN_UDOT_DOT:
17697 case PM_TOKEN_UDOT_DOT_DOT:
17698 case PM_CASE_PRIMITIVE: {
17699 if (!alternation) {
17700 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17701 } else {
17702 pm_token_t operator = parser->previous;
17703 pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1));
17704
17705 if (captures->size) parse_pattern_alternation_error(parser, right);
17706 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17707 }
17708
17709 break;
17710 }
17711 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
17712 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
17713 pm_token_t operator = parser->previous;
17714 pm_token_t opening = parser->current;
17715 parser_lex(parser);
17716
17717 pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17718 accept1(parser, PM_TOKEN_NEWLINE);
17719 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17720 pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0));
17721
17722 if (!alternation) {
17723 node = right;
17724 } else {
17725 if (captures->size) parse_pattern_alternation_error(parser, right);
17726 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17727 }
17728
17729 break;
17730 }
17731 default: {
17732 pm_parser_err_current(parser, diag_id);
17733 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17734
17735 if (!alternation) {
17736 node = right;
17737 } else {
17738 if (captures->size) parse_pattern_alternation_error(parser, right);
17739 node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous));
17740 }
17741
17742 break;
17743 }
17744 }
17745 }
17746
17747 // If we have an =>, then we are assigning this pattern to a variable.
17748 // In this case we should create an assignment node.
17749 while (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
17750 pm_token_t operator = parser->previous;
17751 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET);
17752
17753 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17754 int depth;
17755
17756 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17757 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17758 }
17759
17760 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17761 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17762 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17763 parser,
17764 &previous_loc,
17765 constant_id,
17766 (uint32_t) (depth == -1 ? 0 : depth)
17767 );
17768
17769 node = UP(pm_capture_pattern_node_create(parser, node, target, &operator));
17770 }
17771
17772 return node;
17773}
17774
17778static pm_node_t *
17779parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
17780 pm_node_t *node = NULL;
17781
17782 bool leading_rest = false;
17783 bool trailing_rest = false;
17784
17785 switch (parser->current.type) {
17786 case PM_TOKEN_LABEL: {
17787 parser_lex(parser);
17788 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17789 node = UP(parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)));
17790
17791 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17792 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17793 }
17794
17795 return node;
17796 }
17797 case PM_TOKEN_USTAR_STAR: {
17798 node = parse_pattern_keyword_rest(parser, captures);
17799 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17800
17801 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17802 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17803 }
17804
17805 return node;
17806 }
17807 case PM_TOKEN_STRING_BEGIN: {
17808 // We need special handling for string beginnings because they could
17809 // be dynamic symbols leading to hash patterns.
17810 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17811
17812 if (pm_symbol_node_label_p(parser, node)) {
17813 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17814
17815 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17816 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17817 }
17818
17819 return node;
17820 }
17821
17822 node = parse_pattern_primitives(parser, captures, node, diag_id, (uint16_t) (depth + 1));
17823 break;
17824 }
17825 case PM_TOKEN_USTAR: {
17826 if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) {
17827 parser_lex(parser);
17828 node = UP(parse_pattern_rest(parser, captures));
17829 leading_rest = true;
17830 break;
17831 }
17832 }
17834 default:
17835 node = parse_pattern_primitives(parser, captures, NULL, diag_id, (uint16_t) (depth + 1));
17836 break;
17837 }
17838
17839 // If we got a dynamic label symbol, then we need to treat it like the
17840 // beginning of a hash pattern.
17841 if (pm_symbol_node_label_p(parser, node)) {
17842 return UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17843 }
17844
17845 if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) {
17846 // If we have a comma, then we are now parsing either an array pattern
17847 // or a find pattern. We need to parse all of the patterns, put them
17848 // into a big list, and then determine which type of node we have.
17849 pm_node_list_t nodes = { 0 };
17850 pm_node_list_append(parser->arena, &nodes, node);
17851
17852 // Gather up all of the patterns into the list.
17853 while (accept1(parser, PM_TOKEN_COMMA)) {
17854 /*
17855 * A trailing comma ends the pattern when the next token cannot
17856 * begin another pattern element, leaving the token for the
17857 * enclosing context to accept or reject.
17858 */
17859 if (!token_begins_pattern_p(parser->current.type)) {
17860 // A trailing comma forms an implicit rest pattern (`[a,]` is
17861 // `[a, *]`). If a rest pattern has already been parsed, then
17862 // this is a second rest, which is not allowed (e.g. `[a, *b,]`
17863 // or `x => a, *b,`).
17864 if (trailing_rest) {
17865 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
17866 }
17867
17868 node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
17869 pm_node_list_append(parser->arena, &nodes, node);
17870 trailing_rest = true;
17871 break;
17872 }
17873
17874 if (accept1(parser, PM_TOKEN_USTAR)) {
17875 node = UP(parse_pattern_rest(parser, captures));
17876
17877 // If we have already parsed a splat pattern, then this is an
17878 // error. We will continue to parse the rest of the patterns,
17879 // but we will indicate it as an error.
17880 if (trailing_rest) {
17881 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
17882 }
17883
17884 trailing_rest = true;
17885 } else {
17886 node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
17887 }
17888
17889 pm_node_list_append(parser->arena, &nodes, node);
17890 }
17891
17892 // If the first pattern and the last pattern are rest patterns, then we
17893 // will call this a find pattern, regardless of how many rest patterns
17894 // are in between because we know we already added the appropriate
17895 // errors. Otherwise we will create an array pattern.
17896 if (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) {
17897 node = UP(pm_find_pattern_node_create(parser, &nodes));
17898
17899 if (nodes.size == 2) {
17900 pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER);
17901 }
17902 } else {
17903 node = UP(pm_array_pattern_node_node_list_create(parser, &nodes));
17904
17905 if (leading_rest && trailing_rest) {
17906 pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS);
17907 }
17908 }
17909
17910 // nodes.nodes is arena-allocated; no explicit free needed.
17911 } else if (leading_rest) {
17912 // Otherwise, if we parsed a single splat pattern, then we know we have
17913 // an array pattern, so we can go ahead and create that node.
17914 node = UP(pm_array_pattern_node_rest_create(parser, node));
17915 }
17916
17917 return node;
17918}
17919
17925static PRISM_INLINE void
17926parse_negative_numeric(pm_node_t *node) {
17927 switch (PM_NODE_TYPE(node)) {
17928 case PM_INTEGER_NODE: {
17929 pm_integer_node_t *cast = (pm_integer_node_t *) node;
17930 cast->base.location.start--;
17931 cast->base.location.length++;
17932 cast->value.negative = true;
17933 break;
17934 }
17935 case PM_FLOAT_NODE: {
17936 pm_float_node_t *cast = (pm_float_node_t *) node;
17937 cast->base.location.start--;
17938 cast->base.location.length++;
17939 cast->value = -cast->value;
17940 break;
17941 }
17942 case PM_RATIONAL_NODE: {
17943 pm_rational_node_t *cast = (pm_rational_node_t *) node;
17944 cast->base.location.start--;
17945 cast->base.location.length++;
17946 cast->numerator.negative = true;
17947 break;
17948 }
17949 case PM_IMAGINARY_NODE:
17950 node->location.start--;
17951 node->location.length++;
17952 parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric);
17953 break;
17954 default:
17955 assert(false && "unreachable");
17956 break;
17957 }
17958}
17959
17965static void
17966pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
17967 switch (diag_id) {
17968 case PM_ERR_HASH_KEY: {
17969 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, pm_token_str(parser->previous.type));
17970 break;
17971 }
17972 case PM_ERR_HASH_VALUE:
17973 case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: {
17974 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
17975 break;
17976 }
17977 case PM_ERR_UNARY_RECEIVER: {
17978 const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_str(parser->current.type));
17979 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, human, parser->previous.start[0]);
17980 break;
17981 }
17982 case PM_ERR_UNARY_DISALLOWED:
17983 case PM_ERR_EXPECT_ARGUMENT: {
17984 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
17985 break;
17986 }
17987 default:
17988 pm_parser_err_previous(parser, diag_id);
17989 break;
17990 }
17991}
17992
17996static void
17997parse_retry(pm_parser_t *parser, const pm_node_t *node) {
17998#define CONTEXT_NONE 0
17999#define CONTEXT_THROUGH_ENSURE 1
18000#define CONTEXT_THROUGH_ELSE 2
18001
18002 pm_context_node_t *context_node = parser->current_context;
18003 int context = CONTEXT_NONE;
18004
18005 while (context_node != NULL) {
18006 switch (context_node->context) {
18007 case PM_CONTEXT_BEGIN_RESCUE:
18008 case PM_CONTEXT_BLOCK_RESCUE:
18009 case PM_CONTEXT_CLASS_RESCUE:
18010 case PM_CONTEXT_DEF_RESCUE:
18011 case PM_CONTEXT_LAMBDA_RESCUE:
18012 case PM_CONTEXT_MODULE_RESCUE:
18013 case PM_CONTEXT_SCLASS_RESCUE:
18014 case PM_CONTEXT_DEFINED:
18015 case PM_CONTEXT_RESCUE_MODIFIER:
18016 // These are the good cases. We're allowed to have a retry here.
18017 return;
18018 case PM_CONTEXT_CLASS:
18019 case PM_CONTEXT_DEF:
18020 case PM_CONTEXT_DEF_PARAMS:
18021 case PM_CONTEXT_MAIN:
18022 case PM_CONTEXT_MODULE:
18023 case PM_CONTEXT_PREEXE:
18024 case PM_CONTEXT_SCLASS:
18025 // These are the bad cases. We're not allowed to have a retry in
18026 // these contexts.
18027 if (context == CONTEXT_NONE) {
18028 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE);
18029 } else if (context == CONTEXT_THROUGH_ENSURE) {
18030 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE);
18031 } else if (context == CONTEXT_THROUGH_ELSE) {
18032 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE);
18033 }
18034 return;
18035 case PM_CONTEXT_BEGIN_ELSE:
18036 case PM_CONTEXT_BLOCK_ELSE:
18037 case PM_CONTEXT_CLASS_ELSE:
18038 case PM_CONTEXT_DEF_ELSE:
18039 case PM_CONTEXT_LAMBDA_ELSE:
18040 case PM_CONTEXT_MODULE_ELSE:
18041 case PM_CONTEXT_SCLASS_ELSE:
18042 // These are also bad cases, but with a more specific error
18043 // message indicating the else.
18044 context = CONTEXT_THROUGH_ELSE;
18045 break;
18046 case PM_CONTEXT_BEGIN_ENSURE:
18047 case PM_CONTEXT_BLOCK_ENSURE:
18048 case PM_CONTEXT_CLASS_ENSURE:
18049 case PM_CONTEXT_DEF_ENSURE:
18050 case PM_CONTEXT_LAMBDA_ENSURE:
18051 case PM_CONTEXT_MODULE_ENSURE:
18052 case PM_CONTEXT_SCLASS_ENSURE:
18053 // These are also bad cases, but with a more specific error
18054 // message indicating the ensure.
18055 context = CONTEXT_THROUGH_ENSURE;
18056 break;
18057 case PM_CONTEXT_NONE:
18058 // This case should never happen.
18059 assert(false && "unreachable");
18060 break;
18061 case PM_CONTEXT_BEGIN:
18062 case PM_CONTEXT_BLOCK_BRACES:
18063 case PM_CONTEXT_BLOCK_KEYWORDS:
18064 case PM_CONTEXT_BLOCK_PARAMETERS:
18065 case PM_CONTEXT_CASE_IN:
18066 case PM_CONTEXT_CASE_WHEN:
18067 case PM_CONTEXT_DEFAULT_PARAMS:
18068 case PM_CONTEXT_ELSE:
18069 case PM_CONTEXT_ELSIF:
18070 case PM_CONTEXT_EMBEXPR:
18071 case PM_CONTEXT_FOR_INDEX:
18072 case PM_CONTEXT_FOR:
18073 case PM_CONTEXT_IF:
18074 case PM_CONTEXT_LAMBDA_BRACES:
18075 case PM_CONTEXT_LAMBDA_DO_END:
18076 case PM_CONTEXT_LOOP_PREDICATE:
18077 case PM_CONTEXT_MULTI_TARGET:
18078 case PM_CONTEXT_PARENS:
18079 case PM_CONTEXT_POSTEXE:
18080 case PM_CONTEXT_PREDICATE:
18081 case PM_CONTEXT_TERNARY:
18082 case PM_CONTEXT_UNLESS:
18083 case PM_CONTEXT_UNTIL:
18084 case PM_CONTEXT_WHILE:
18085 // In these contexts we should continue walking up the list of
18086 // contexts.
18087 break;
18088 }
18089
18090 context_node = context_node->prev;
18091 }
18092
18093#undef CONTEXT_NONE
18094#undef CONTEXT_ENSURE
18095#undef CONTEXT_ELSE
18096}
18097
18101static void
18102parse_yield(pm_parser_t *parser, const pm_node_t *node) {
18103 pm_context_node_t *context_node = parser->current_context;
18104
18105 while (context_node != NULL) {
18106 switch (context_node->context) {
18107 case PM_CONTEXT_DEF:
18108 case PM_CONTEXT_DEF_PARAMS:
18109 case PM_CONTEXT_DEFINED:
18110 case PM_CONTEXT_DEF_ENSURE:
18111 case PM_CONTEXT_DEF_RESCUE:
18112 case PM_CONTEXT_DEF_ELSE:
18113 // These are the good cases. We're allowed to have a block exit
18114 // in these contexts.
18115 return;
18116 case PM_CONTEXT_CLASS:
18117 case PM_CONTEXT_CLASS_ENSURE:
18118 case PM_CONTEXT_CLASS_RESCUE:
18119 case PM_CONTEXT_CLASS_ELSE:
18120 case PM_CONTEXT_MAIN:
18121 case PM_CONTEXT_MODULE:
18122 case PM_CONTEXT_MODULE_ENSURE:
18123 case PM_CONTEXT_MODULE_RESCUE:
18124 case PM_CONTEXT_MODULE_ELSE:
18125 case PM_CONTEXT_SCLASS:
18126 case PM_CONTEXT_SCLASS_RESCUE:
18127 case PM_CONTEXT_SCLASS_ENSURE:
18128 case PM_CONTEXT_SCLASS_ELSE:
18129 // These are the bad cases. We're not allowed to have a retry in
18130 // these contexts.
18131 pm_parser_err_node(parser, node, PM_ERR_INVALID_YIELD);
18132 return;
18133 case PM_CONTEXT_NONE:
18134 // This case should never happen.
18135 assert(false && "unreachable");
18136 break;
18137 case PM_CONTEXT_BEGIN:
18138 case PM_CONTEXT_BEGIN_ELSE:
18139 case PM_CONTEXT_BEGIN_ENSURE:
18140 case PM_CONTEXT_BEGIN_RESCUE:
18141 case PM_CONTEXT_BLOCK_BRACES:
18142 case PM_CONTEXT_BLOCK_KEYWORDS:
18143 case PM_CONTEXT_BLOCK_ELSE:
18144 case PM_CONTEXT_BLOCK_ENSURE:
18145 case PM_CONTEXT_BLOCK_PARAMETERS:
18146 case PM_CONTEXT_BLOCK_RESCUE:
18147 case PM_CONTEXT_CASE_IN:
18148 case PM_CONTEXT_CASE_WHEN:
18149 case PM_CONTEXT_DEFAULT_PARAMS:
18150 case PM_CONTEXT_ELSE:
18151 case PM_CONTEXT_ELSIF:
18152 case PM_CONTEXT_EMBEXPR:
18153 case PM_CONTEXT_FOR_INDEX:
18154 case PM_CONTEXT_FOR:
18155 case PM_CONTEXT_IF:
18156 case PM_CONTEXT_LAMBDA_BRACES:
18157 case PM_CONTEXT_LAMBDA_DO_END:
18158 case PM_CONTEXT_LAMBDA_ELSE:
18159 case PM_CONTEXT_LAMBDA_ENSURE:
18160 case PM_CONTEXT_LAMBDA_RESCUE:
18161 case PM_CONTEXT_LOOP_PREDICATE:
18162 case PM_CONTEXT_MULTI_TARGET:
18163 case PM_CONTEXT_PARENS:
18164 case PM_CONTEXT_POSTEXE:
18165 case PM_CONTEXT_PREDICATE:
18166 case PM_CONTEXT_PREEXE:
18167 case PM_CONTEXT_RESCUE_MODIFIER:
18168 case PM_CONTEXT_TERNARY:
18169 case PM_CONTEXT_UNLESS:
18170 case PM_CONTEXT_UNTIL:
18171 case PM_CONTEXT_WHILE:
18172 // In these contexts we should continue walking up the list of
18173 // contexts.
18174 break;
18175 }
18176
18177 context_node = context_node->prev;
18178 }
18179}
18180
18185static pm_node_t *
18186parse_case(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18187 size_t opening_newline_index = token_newline_index(parser);
18188 parser_lex(parser);
18189
18190 pm_token_t case_keyword = parser->previous;
18191 pm_node_t *predicate = NULL;
18192
18193 pm_node_list_t current_block_exits = { 0 };
18194 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18195
18196 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18197 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18198 predicate = NULL;
18199 } else if (match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_END)) {
18200 predicate = NULL;
18201 } else if (!token_begins_expression_p(parser->current.type)) {
18202 predicate = NULL;
18203 } else {
18204 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));
18205 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18206 }
18207
18208 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
18209 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18210 parser_lex(parser);
18211 pop_block_exits(parser, previous_block_exits);
18212 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18213 return UP(pm_case_node_create(parser, &case_keyword, predicate, &parser->previous));
18214 }
18215
18216 /* At this point we can create a case node, though we don't yet know if it
18217 * is a case-in or case-when node. */
18218 pm_node_t *node;
18219
18220 if (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18221 pm_case_node_t *case_node = pm_case_node_create(parser, &case_keyword, predicate, NULL);
18222 pm_static_literals_t literals = { 0 };
18223
18224 /* At this point we've seen a when keyword, so we know this is a
18225 * case-when node. We will continue to parse the when nodes until we hit
18226 * the end of the list. */
18227 while (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18228 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18229 parser_lex(parser);
18230
18231 pm_token_t when_keyword = parser->previous;
18232 pm_when_node_t *when_node = pm_when_node_create(parser, &when_keyword);
18233
18234 do {
18235 if (accept1(parser, PM_TOKEN_USTAR)) {
18236 pm_token_t operator = parser->previous;
18237 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));
18238
18239 pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression);
18240 pm_when_node_conditions_append(parser->arena, when_node, UP(splat_node));
18241
18242 if (PM_NODE_TYPE_P(expression, PM_ERROR_RECOVERY_NODE)) break;
18243 } else {
18244 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));
18245 pm_when_node_conditions_append(parser->arena, when_node, condition);
18246
18247 /* If we found a missing node, then this is a syntax error
18248 * and we should stop looping. */
18249 if (PM_NODE_TYPE_P(condition, PM_ERROR_RECOVERY_NODE)) break;
18250
18251 /* If this is a string node, then we need to mark it as
18252 * frozen because when clause strings are frozen. */
18253 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
18254 pm_node_flag_set(condition, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
18255 } else if (PM_NODE_TYPE_P(condition, PM_SOURCE_FILE_NODE) && parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
18256 pm_node_flag_set(condition, PM_NODE_FLAG_STATIC_LITERAL);
18257 }
18258
18259 pm_when_clause_static_literals_add(parser, &literals, condition);
18260 }
18261 } while (accept1(parser, PM_TOKEN_COMMA));
18262
18263 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18264 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18265 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18266 }
18267 } else {
18268 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER);
18269 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18270 }
18271
18272 if (!match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18273 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_CASE_WHEN, (uint16_t) (depth + 1));
18274 if (statements != NULL) {
18275 pm_when_node_statements_set(when_node, statements);
18276 }
18277 }
18278
18279 pm_case_node_condition_append(parser->arena, case_node, UP(when_node));
18280 }
18281
18282 /* If we didn't parse any conditions (in or when) then we need to
18283 * indicate that we have an error. */
18284 if (case_node->conditions.size == 0) {
18285 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18286 }
18287
18288 pm_static_literals_free(&literals);
18289 node = UP(case_node);
18290 } else {
18291 pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate);
18292
18293 /* If this is a case-match node (i.e., it is a pattern matching case
18294 * statement) then we must have a predicate. */
18295 if (predicate == NULL) {
18296 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MATCH_MISSING_PREDICATE);
18297 }
18298
18299 /* At this point we expect that we're parsing a case-in node. We will
18300 * continue to parse the in nodes until we hit the end of the list. */
18301 while (match1(parser, PM_TOKEN_KEYWORD_IN)) {
18302 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18303
18304 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
18305 parser->pattern_matching_newlines = true;
18306
18307 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
18308 parser->command_start = false;
18309 parser_lex(parser);
18310
18311 pm_token_t in_keyword = parser->previous;
18312
18313 pm_constant_id_list_t captures = { 0 };
18314 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));
18315
18316 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
18317
18318 /* Since we're in the top-level of the case-in node we need to
18319 * check for guard clauses in the form of `if` or `unless`
18320 * statements. */
18321 if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) {
18322 pm_token_t keyword = parser->previous;
18323 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));
18324 pattern = UP(pm_if_node_modifier_create(parser, pattern, &keyword, predicate));
18325 } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) {
18326 pm_token_t keyword = parser->previous;
18327 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));
18328 pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate));
18329 }
18330
18331 /* Now we need to check for the terminator of the in node's pattern.
18332 * It can be a newline or semicolon optionally followed by a `then`
18333 * keyword. */
18334 pm_token_t then_keyword = { 0 };
18335 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18336 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18337 then_keyword = parser->previous;
18338 }
18339 } else {
18340 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_IN_DELIMITER);
18341 then_keyword = parser->previous;
18342 }
18343
18344 /* Now we can actually parse the statements associated with the in
18345 * node. */
18346 pm_statements_node_t *statements;
18347 if (match3(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18348 statements = NULL;
18349 } else {
18350 statements = parse_statements(parser, PM_CONTEXT_CASE_IN, (uint16_t) (depth + 1));
18351 }
18352
18353 /* Now that we have the full pattern and statements, we can create
18354 * the node and attach it to the case node. */
18355 pm_node_t *condition = UP(pm_in_node_create(parser, pattern, statements, &in_keyword, NTOK2PTR(then_keyword)));
18356 pm_case_match_node_condition_append(parser->arena, case_node, condition);
18357 }
18358
18359 /* If we didn't parse any conditions (in or when) then we need to
18360 * indicate that we have an error. */
18361 if (case_node->conditions.size == 0) {
18362 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18363 }
18364
18365 node = UP(case_node);
18366 }
18367
18368 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18369 if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) {
18370 pm_token_t else_keyword = parser->previous;
18371 pm_else_node_t *else_node;
18372
18373 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
18374 else_node = pm_else_node_create(parser, &else_keyword, parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1)), &parser->current);
18375 } else {
18376 else_node = pm_else_node_create(parser, &else_keyword, NULL, &parser->current);
18377 }
18378
18379 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18380 pm_case_node_else_clause_set((pm_case_node_t *) node, else_node);
18381 } else {
18382 pm_case_match_node_else_clause_set((pm_case_match_node_t *) node, else_node);
18383 }
18384 }
18385
18386 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18387 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword);
18388
18389 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18390 pm_case_node_end_keyword_loc_set(parser, (pm_case_node_t *) node, &parser->previous);
18391 } else {
18392 pm_case_match_node_end_keyword_loc_set(parser, (pm_case_match_node_t *) node, &parser->previous);
18393 }
18394
18395 pop_block_exits(parser, previous_block_exits);
18396 return node;
18397}
18398
18403static pm_node_t *
18404parse_class(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18405 size_t opening_newline_index = token_newline_index(parser);
18406 parser_lex(parser);
18407
18408 pm_token_t class_keyword = parser->previous;
18409 pm_do_loop_stack_push(parser, false);
18410
18411 pm_node_list_t current_block_exits = { 0 };
18412 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18413
18414 if (accept1(parser, PM_TOKEN_LESS_LESS)) {
18415 pm_token_t operator = parser->previous;
18416 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));
18417
18418 pm_parser_scope_push(parser, true);
18419 if (!match2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18420 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_str(parser->current.type));
18421 }
18422
18423 pm_node_t *statements = NULL;
18424 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18425 pm_accepts_block_stack_push(parser, true);
18426 statements = UP(parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1)));
18427 pm_accepts_block_stack_pop(parser);
18428 }
18429
18430 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18431 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18432 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)));
18433 } else {
18434 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18435 }
18436
18437 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18438
18439 pm_constant_id_list_t locals;
18440 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18441
18442 pm_parser_scope_pop(parser);
18443 pm_do_loop_stack_pop(parser);
18444
18445 flush_block_exits(parser, previous_block_exits);
18446 return UP(pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous));
18447 }
18448
18449 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));
18450 pm_token_t name = parser->previous;
18451 if (name.type != PM_TOKEN_CONSTANT) {
18452 pm_parser_err_token(parser, &name, PM_ERR_CLASS_NAME);
18453 }
18454
18455 pm_token_t inheritance_operator = { 0 };
18456 pm_node_t *superclass;
18457
18458 if (match1(parser, PM_TOKEN_LESS)) {
18459 inheritance_operator = parser->current;
18460 lex_state_set(parser, PM_LEX_STATE_BEG);
18461
18462 parser->command_start = true;
18463 parser_lex(parser);
18464
18465 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));
18466 } else {
18467 superclass = NULL;
18468 }
18469
18470 pm_parser_scope_push(parser, true);
18471
18472 if (inheritance_operator.start != NULL) {
18473 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CLASS_UNEXPECTED_END);
18474 } else {
18475 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18476 }
18477 pm_node_t *statements = NULL;
18478
18479 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18480 pm_accepts_block_stack_push(parser, true);
18481 statements = UP(parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1)));
18482 pm_accepts_block_stack_pop(parser);
18483 }
18484
18485 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18486 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18487 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)));
18488 } else {
18489 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18490 }
18491
18492 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18493
18494 if (context_def_p(parser)) {
18495 pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD);
18496 }
18497
18498 pm_constant_id_list_t locals;
18499 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18500
18501 pm_parser_scope_pop(parser);
18502 pm_do_loop_stack_pop(parser);
18503
18504 if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) {
18505 pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME);
18506 if (!PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18507 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18508 }
18509 }
18510
18511 pop_block_exits(parser, previous_block_exits);
18512 return UP(pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, NTOK2PTR(inheritance_operator), superclass, statements, &parser->previous));
18513}
18514
18518static pm_node_t *
18519parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
18520 pm_node_list_t current_block_exits = { 0 };
18521 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18522
18523 pm_token_t def_keyword = parser->current;
18524 size_t opening_newline_index = token_newline_index(parser);
18525
18526 pm_node_t *receiver = NULL;
18527 pm_token_t operator = { 0 };
18528 pm_token_t name;
18529
18530 /* This context is necessary for lexing `...` in a bare params correctly. It
18531 * must be pushed before lexing the first param, so it is here. */
18532 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18533 parser_lex(parser);
18534
18535 /* This will be false if the method name is not a valid identifier but could
18536 * be followed by an operator. */
18537 bool valid_name = true;
18538
18539 switch (parser->current.type) {
18540 case PM_CASE_OPERATOR:
18541 pm_parser_scope_push(parser, true);
18542 lex_state_set(parser, PM_LEX_STATE_ENDFN);
18543 parser_lex(parser);
18544
18545 name = parser->previous;
18546 break;
18547 case PM_TOKEN_IDENTIFIER: {
18548 parser_lex(parser);
18549
18550 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18551 receiver = parse_variable_call(parser);
18552
18553 pm_parser_scope_push(parser, true);
18554 lex_state_set(parser, PM_LEX_STATE_FNAME);
18555 parser_lex(parser);
18556
18557 operator = parser->previous;
18558 name = parse_method_definition_name(parser);
18559 } else {
18560 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
18561 pm_parser_scope_push(parser, true);
18562
18563 name = parser->previous;
18564 }
18565
18566 break;
18567 }
18568 case PM_TOKEN_INSTANCE_VARIABLE:
18569 case PM_TOKEN_CLASS_VARIABLE:
18570 case PM_TOKEN_GLOBAL_VARIABLE:
18571 valid_name = false;
18573 case PM_TOKEN_CONSTANT:
18574 case PM_TOKEN_KEYWORD_NIL:
18575 case PM_TOKEN_KEYWORD_SELF:
18576 case PM_TOKEN_KEYWORD_TRUE:
18577 case PM_TOKEN_KEYWORD_FALSE:
18578 case PM_TOKEN_KEYWORD___FILE__:
18579 case PM_TOKEN_KEYWORD___LINE__:
18580 case PM_TOKEN_KEYWORD___ENCODING__: {
18581 pm_parser_scope_push(parser, true);
18582 parser_lex(parser);
18583
18584 pm_token_t identifier = parser->previous;
18585
18586 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18587 lex_state_set(parser, PM_LEX_STATE_FNAME);
18588 parser_lex(parser);
18589 operator = parser->previous;
18590
18591 switch (identifier.type) {
18592 case PM_TOKEN_CONSTANT:
18593 receiver = UP(pm_constant_read_node_create(parser, &identifier));
18594 break;
18595 case PM_TOKEN_INSTANCE_VARIABLE:
18596 receiver = UP(pm_instance_variable_read_node_create(parser, &identifier));
18597 break;
18598 case PM_TOKEN_CLASS_VARIABLE:
18599 receiver = UP(pm_class_variable_read_node_create(parser, &identifier));
18600 break;
18601 case PM_TOKEN_GLOBAL_VARIABLE:
18602 receiver = UP(pm_global_variable_read_node_create(parser, &identifier));
18603 break;
18604 case PM_TOKEN_KEYWORD_NIL:
18605 receiver = UP(pm_nil_node_create(parser, &identifier));
18606 break;
18607 case PM_TOKEN_KEYWORD_SELF:
18608 receiver = UP(pm_self_node_create(parser, &identifier));
18609 break;
18610 case PM_TOKEN_KEYWORD_TRUE:
18611 receiver = UP(pm_true_node_create(parser, &identifier));
18612 break;
18613 case PM_TOKEN_KEYWORD_FALSE:
18614 receiver = UP(pm_false_node_create(parser, &identifier));
18615 break;
18616 case PM_TOKEN_KEYWORD___FILE__:
18617 receiver = UP(pm_source_file_node_create(parser, &identifier));
18618 break;
18619 case PM_TOKEN_KEYWORD___LINE__:
18620 receiver = UP(pm_source_line_node_create(parser, &identifier));
18621 break;
18622 case PM_TOKEN_KEYWORD___ENCODING__:
18623 receiver = UP(pm_source_encoding_node_create(parser, &identifier));
18624 break;
18625 default:
18626 break;
18627 }
18628
18629 name = parse_method_definition_name(parser);
18630 } else {
18631 if (!valid_name) {
18632 PM_PARSER_ERR_TOKEN_FORMAT(parser, &identifier, PM_ERR_DEF_NAME, pm_token_str(identifier.type));
18633 }
18634
18635 name = identifier;
18636 }
18637 break;
18638 }
18639 case PM_TOKEN_PARENTHESIS_LEFT: {
18640 /* The current context is `PM_CONTEXT_DEF_PARAMS`, however the inner
18641 * expression of this parenthesis should not be processed under this
18642 * context. Thus, the context is popped here. */
18643 context_pop(parser);
18644 parser_lex(parser);
18645
18646 pm_token_t lparen = parser->previous;
18647 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));
18648
18649 accept1(parser, PM_TOKEN_NEWLINE);
18650 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
18651 pm_token_t rparen = parser->previous;
18652
18653 lex_state_set(parser, PM_LEX_STATE_FNAME);
18654 expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM);
18655
18656 operator = parser->previous;
18657 receiver = UP(pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0));
18658
18659 /* To push `PM_CONTEXT_DEF_PARAMS` again is for the same reason as
18660 * described the above. */
18661 pm_parser_scope_push(parser, true);
18662 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18663 name = parse_method_definition_name(parser);
18664 break;
18665 }
18666 default:
18667 pm_parser_scope_push(parser, true);
18668 name = parse_method_definition_name(parser);
18669 break;
18670 }
18671
18672 pm_token_t lparen = { 0 };
18673 pm_token_t rparen = { 0 };
18674 pm_parameters_node_t *params;
18675
18676 bool accept_endless_def = true;
18677 switch (parser->current.type) {
18678 case PM_TOKEN_PARENTHESIS_LEFT: {
18679 parser_lex(parser);
18680 lparen = parser->previous;
18681
18682 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18683 params = NULL;
18684 } else {
18685 /* https://bugs.ruby-lang.org/issues/19107 */
18686 bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1;
18687 params = parse_parameters(
18688 parser,
18689 PM_BINDING_POWER_DEFINED,
18690 true,
18691 allow_trailing_comma,
18692 true,
18693 true,
18694 false,
18695 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18696 (uint16_t) (depth + 1)
18697 );
18698 }
18699
18700 lex_state_set(parser, PM_LEX_STATE_BEG);
18701 parser->command_start = true;
18702
18703 context_pop(parser);
18704 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18705 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_str(parser->current.type));
18706 parser->previous.start = parser->previous.end;
18707 parser->previous.type = 0;
18708 }
18709
18710 rparen = parser->previous;
18711 break;
18712 }
18713 case PM_CASE_PARAMETER: {
18714 /* If we're about to lex a label, we need to add the label state to
18715 * make sure the next newline is ignored. */
18716 if (parser->current.type == PM_TOKEN_LABEL) {
18717 lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL);
18718 }
18719
18720 params = parse_parameters(
18721 parser,
18722 PM_BINDING_POWER_DEFINED,
18723 false,
18724 false,
18725 true,
18726 true,
18727 false,
18728 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18729 (uint16_t) (depth + 1)
18730 );
18731
18732 /* Reject `def * = 1` and similar. We have to specifically check for
18733 * them because they create ambiguity with optional arguments. */
18734 accept_endless_def = false;
18735
18736 context_pop(parser);
18737 break;
18738 }
18739 default: {
18740 params = NULL;
18741 context_pop(parser);
18742 break;
18743 }
18744 }
18745
18746 pm_node_t *statements = NULL;
18747 pm_token_t equal = { 0 };
18748 pm_token_t end_keyword = { 0 };
18749
18750 if (accept1(parser, PM_TOKEN_EQUAL)) {
18751 if (token_is_setter_name(&name)) {
18752 pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER);
18753 }
18754 if (!accept_endless_def) {
18755 pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
18756 }
18757 if (
18758 parser->current_context->context == PM_CONTEXT_DEFAULT_PARAMS &&
18759 parser->current_context->prev->context == PM_CONTEXT_BLOCK_PARAMETERS
18760 ) {
18761 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");
18762 }
18763 equal = parser->previous;
18764
18765 context_push(parser, PM_CONTEXT_DEF);
18766 pm_do_loop_stack_push(parser, false);
18767 statements = UP(pm_statements_node_create(parser));
18768
18769 uint8_t allow_flags;
18770 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
18771 allow_flags = flags & PM_PARSE_ACCEPTS_COMMAND_CALL;
18772 } else {
18773 /* Allow `def foo = puts "Hello"` but not
18774 * `private def foo = puts "Hello"` */
18775 allow_flags = (binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
18776 }
18777
18778 /* Inside a def body, we push true onto the accepts_block_stack so that
18779 * `do` is lexed as PM_TOKEN_KEYWORD_DO (which can only start a block
18780 * for primary-level constructs, not commands). During command argument
18781 * parsing, the stack is pushed to false, causing `do` to be lexed as
18782 * PM_TOKEN_KEYWORD_DO_BLOCK, which is not consumed inside the endless
18783 * def body and instead left for the outer context. A method definition
18784 * opens a fresh context all the way through its rescue modifier, so
18785 * this frame spans the rescue modifier value as well: the `do` in
18786 * `baz def f = a rescue z do end` lexes as a plain keyword that
18787 * attaches to `z` rather than to `baz`. */
18788 pm_accepts_block_stack_push(parser, true);
18789 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));
18790
18791 /* If an unconsumed PM_TOKEN_KEYWORD_DO follows the body, it is an error
18792 * (e.g., `def f = 1 do end`). PM_TOKEN_KEYWORD_DO_BLOCK is
18793 * intentionally not caught here — it should bubble up to the outer
18794 * context (e.g., `private def f = puts "Hello" do end` where the block
18795 * attaches to `private`). */
18796 if (accept1(parser, PM_TOKEN_KEYWORD_DO)) {
18797 pm_block_node_t *block = parse_block(parser, (uint16_t) (depth + 1));
18798 pm_parser_err_node(parser, UP(block), PM_ERR_DEF_ENDLESS_DO_BLOCK);
18799 }
18800
18801 /* Any number of rescue modifiers chain onto the body within the method
18802 * definition itself, associating to the left: `def f = a rescue b
18803 * rescue c` defines a method whose body is `(a rescue b) rescue c`,
18804 * rather than a rescue modifier guarding the definition. */
18805 while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
18806 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
18807
18808 pm_token_t rescue_keyword = parser->previous;
18809
18810 /* In the Ruby grammar, the rescue value of an endless method
18811 * command excludes and/or and in/=>. */
18812 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));
18813 context_pop(parser);
18814
18815 statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value));
18816 }
18817
18818 pm_accepts_block_stack_pop(parser);
18819
18820 /* A nested endless def whose body is a command call (e.g.,
18821 * `def f = def g = foo bar`) is a command assignment and cannot appear
18822 * as a def body. */
18823 if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(parser, statement)) {
18824 PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
18825 }
18826
18827 pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false);
18828 pm_do_loop_stack_pop(parser);
18829 context_pop(parser);
18830 } else {
18831 if (lparen.start == NULL) {
18832 lex_state_set(parser, PM_LEX_STATE_BEG);
18833 parser->command_start = true;
18834 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_DEF_PARAMS_TERM);
18835 } else {
18836 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18837 }
18838
18839 pm_accepts_block_stack_push(parser, true);
18840 pm_do_loop_stack_push(parser, false);
18841
18842 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18843 pm_accepts_block_stack_push(parser, true);
18844 statements = UP(parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1)));
18845 pm_accepts_block_stack_pop(parser);
18846 }
18847
18848 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18849 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18850 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)));
18851 } else {
18852 parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false);
18853 }
18854
18855 pm_accepts_block_stack_pop(parser);
18856 pm_do_loop_stack_pop(parser);
18857
18858 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword);
18859 end_keyword = parser->previous;
18860 }
18861
18862 pm_constant_id_list_t locals;
18863 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18864 pm_parser_scope_pop(parser);
18865
18866 /* If the final character is `@` as is the case when defining methods to
18867 * override the unary operators, we should ignore the @ in the same way we
18868 * do for symbols. */
18869 pm_constant_id_t name_id = pm_parser_constant_id_raw(parser, name.start, parse_operator_symbol_name(&name));
18870
18871 flush_block_exits(parser, previous_block_exits);
18872
18873 return UP(pm_def_node_create(
18874 parser,
18875 name_id,
18876 &name,
18877 receiver,
18878 params,
18879 statements,
18880 &locals,
18881 &def_keyword,
18882 NTOK2PTR(operator),
18883 NTOK2PTR(lparen),
18884 NTOK2PTR(rparen),
18885 NTOK2PTR(equal),
18886 NTOK2PTR(end_keyword)
18887 ));
18888}
18889
18893static pm_node_t *
18894parse_module(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18895 pm_node_list_t current_block_exits = { 0 };
18896 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18897
18898 size_t opening_newline_index = token_newline_index(parser);
18899 parser_lex(parser);
18900 pm_token_t module_keyword = parser->previous;
18901
18902 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));
18903 pm_token_t name;
18904
18905 /* If we can recover from a syntax error that occurred while parsing the
18906 * name of the module, then we'll handle that here. */
18907 if (PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18908 pop_block_exits(parser, previous_block_exits);
18909
18910 pm_token_t missing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
18911 return UP(pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing));
18912 }
18913
18914 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
18915 pm_token_t double_colon = parser->previous;
18916
18917 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
18918 constant_path = UP(pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous));
18919 }
18920
18921 /* Here we retrieve the name of the module. If it wasn't a constant, then
18922 * it's possible that `module foo` was passed, which is a syntax error. We
18923 * handle that here as well. */
18924 name = parser->previous;
18925 if (name.type != PM_TOKEN_CONSTANT) {
18926 pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME);
18927 }
18928
18929 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)) {
18930 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18931 }
18932
18933 pm_parser_scope_push(parser, true);
18934 accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE);
18935 pm_node_t *statements = NULL;
18936
18937 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18938 pm_accepts_block_stack_push(parser, true);
18939 statements = UP(parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1)));
18940 pm_accepts_block_stack_pop(parser);
18941 }
18942
18943 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18944 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18945 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)));
18946 } else {
18947 parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false);
18948 }
18949
18950 pm_constant_id_list_t locals;
18951 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18952
18953 pm_parser_scope_pop(parser);
18954 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword);
18955
18956 if (context_def_p(parser)) {
18957 pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
18958 }
18959
18960 pop_block_exits(parser, previous_block_exits);
18961
18962 return UP(pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous));
18963}
18964
18968static pm_node_t *
18969parse_string_array(pm_parser_t *parser, uint16_t depth) {
18970 parser_lex(parser);
18971 pm_token_t opening = parser->previous;
18972 pm_array_node_t *array = pm_array_node_create(parser, &opening);
18973
18974 /* This is the current node that we are parsing that will be added to the
18975 * list of elements. */
18976 pm_node_t *current = NULL;
18977
18978 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
18979 switch (parser->current.type) {
18980 case PM_TOKEN_WORDS_SEP: {
18981 /* Reset the explicit encoding if we hit a separator since each
18982 * element can have its own encoding. */
18983 parser->explicit_encoding = NULL;
18984
18985 if (current == NULL) {
18986 /* If we hit a separator before we have any content, then we
18987 * don't need to do anything. */
18988 } else {
18989 /* If we hit a separator after we've hit content, then we
18990 * need to append that content to the list and reset the
18991 * current node. */
18992 pm_array_node_elements_append(parser->arena, array, current);
18993 current = NULL;
18994 }
18995
18996 parser_lex(parser);
18997 break;
18998 }
18999 case PM_TOKEN_STRING_CONTENT: {
19000 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
19001 pm_node_flag_set(string, parse_unescaped_encoding(parser, parser->explicit_encoding));
19002 parser_lex(parser);
19003
19004 if (current == NULL) {
19005 /* If we hit content and the current node is NULL, then this
19006 * is the first string content we've seen. In that case
19007 * we're going to create a new string node and set that to
19008 * the current. */
19009 current = string;
19010 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
19011 /* If we hit string content and the current node is an
19012 * interpolated string, then we need to append the string
19013 * content to the list of child nodes. */
19014 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
19015 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19016 /* If we hit string content and the current node is a string
19017 * node, then we need to convert the current node into an
19018 * interpolated string and add the string content to the
19019 * list of child nodes. */
19020 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
19021 pm_interpolated_string_node_append(parser, interpolated, current);
19022 pm_interpolated_string_node_append(parser, interpolated, string);
19023 current = UP(interpolated);
19024 } else {
19025 assert(false && "unreachable");
19026 }
19027
19028 break;
19029 }
19030 case PM_TOKEN_EMBVAR: {
19031 if (current == NULL) {
19032 /* If we hit an embedded variable and the current node is
19033 * NULL, then this is the start of a new string. We'll set
19034 * the current node to a new interpolated string. */
19035 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
19036 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19037 /* If we hit an embedded variable and the current node is a
19038 * string node, then we'll convert the current into an
19039 * interpolated string and add the string node to the list
19040 * of parts. */
19041 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
19042 pm_interpolated_string_node_append(parser, interpolated, current);
19043 current = UP(interpolated);
19044 } else {
19045 /* If we hit an embedded variable and the current node is an
19046 * interpolated string, then we'll just add the embedded
19047 * variable. */
19048 }
19049
19050 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19051 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
19052 break;
19053 }
19054 case PM_TOKEN_EMBEXPR_BEGIN: {
19055 if (current == NULL) {
19056 /* If we hit an embedded expression and the current node is
19057 * NULL, then this is the start of a new string. We'll set
19058 * the current node to a new interpolated string. */
19059 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
19060 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19061 /* If we hit an embedded expression and the current node is
19062 * a string node, then we'll convert the current into an
19063 * interpolated string and add the string node to the list
19064 * of parts. */
19065 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
19066 pm_interpolated_string_node_append(parser, interpolated, current);
19067 current = UP(interpolated);
19068 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
19069 /* If we hit an embedded expression and the current node is
19070 * an interpolated string, then we'll just continue on. */
19071 } else {
19072 assert(false && "unreachable");
19073 }
19074
19075 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19076 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
19077 break;
19078 }
19079 default:
19080 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_UPPER_ELEMENT);
19081 parser_lex(parser);
19082 break;
19083 }
19084 }
19085
19086 /* If we have a current node, then we need to append it to the list. */
19087 if (current) {
19088 pm_array_node_elements_append(parser->arena, array, current);
19089 }
19090
19091 pm_token_t closing = parser->current;
19092 if (match1(parser, PM_TOKEN_EOF)) {
19093 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_UPPER_TERM);
19094 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19095 } else {
19096 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM);
19097 }
19098
19099 pm_array_node_close_set(parser, array, &closing);
19100 return UP(array);
19101}
19102
19106static pm_node_t *
19107parse_symbol_array(pm_parser_t *parser, uint16_t depth) {
19108 parser_lex(parser);
19109 pm_token_t opening = parser->previous;
19110 pm_array_node_t *array = pm_array_node_create(parser, &opening);
19111
19112 /* This is the current node that we are parsing that will be added to the
19113 * list of elements. */
19114 pm_node_t *current = NULL;
19115
19116 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
19117 switch (parser->current.type) {
19118 case PM_TOKEN_WORDS_SEP: {
19119 /* Reset the explicit encoding if we hit a separator since each
19120 * element can have its own encoding. */
19121 parser->explicit_encoding = NULL;
19122
19123 if (current == NULL) {
19124 /* If we hit a separator before we have any content, then we
19125 * don't need to do anything. */
19126 } else {
19127 /* If we hit a separator after we've hit content, then we
19128 * need to append that content to the list and reset the
19129 * current node. */
19130 pm_array_node_elements_append(parser->arena, array, current);
19131 current = NULL;
19132 }
19133
19134 parser_lex(parser);
19135 break;
19136 }
19137 case PM_TOKEN_STRING_CONTENT: {
19138 if (current == NULL) {
19139 /* If we hit content and the current node is NULL, then this
19140 * is the first string content we've seen. In that case
19141 * we're going to create a new string node and set that to
19142 * the current. */
19143 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
19144 parser_lex(parser);
19145 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
19146 /* If we hit string content and the current node is an
19147 * interpolated string, then we need to append the string
19148 * content to the list of child nodes. */
19149 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
19150 parser_lex(parser);
19151
19152 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
19153 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19154 /* If we hit string content and the current node is a symbol
19155 * node, then we need to convert the current node into an
19156 * interpolated string and add the string content to the
19157 * list of child nodes. */
19158 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
19159 pm_token_t content = {
19160 .type = PM_TOKEN_STRING_CONTENT,
19161 .start = parser->start + cast->content_loc.start,
19162 .end = parser->start + cast->content_loc.start + cast->content_loc.length
19163 };
19164
19165 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
19166 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
19167 parser_lex(parser);
19168
19169 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
19170 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
19171 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
19172
19173 current = UP(interpolated);
19174 } else {
19175 assert(false && "unreachable");
19176 }
19177
19178 break;
19179 }
19180 case PM_TOKEN_EMBVAR: {
19181 bool start_location_set = false;
19182 if (current == NULL) {
19183 /* If we hit an embedded variable and the current node is
19184 * NULL, then this is the start of a new string. We'll set
19185 * the current node to a new interpolated string. */
19186 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
19187 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19188 /* If we hit an embedded variable and the current node is a
19189 * string node, then we'll convert the current into an
19190 * interpolated string and add the string node to the list
19191 * of parts. */
19192 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
19193
19194 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
19195 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
19196 PM_NODE_START_SET_NODE(interpolated, current);
19197 start_location_set = true;
19198 current = UP(interpolated);
19199 } else {
19200 /* If we hit an embedded variable and the current node is an
19201 * interpolated string, then we'll just add the embedded
19202 * variable. */
19203 }
19204
19205 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19206 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
19207 if (!start_location_set) {
19208 PM_NODE_START_SET_NODE(current, part);
19209 }
19210 break;
19211 }
19212 case PM_TOKEN_EMBEXPR_BEGIN: {
19213 bool start_location_set = false;
19214 if (current == NULL) {
19215 /* If we hit an embedded expression and the current node is
19216 * NULL, then this is the start of a new string. We'll set
19217 * the current node to a new interpolated string. */
19218 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
19219 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19220 /* If we hit an embedded expression and the current node is
19221 * a string node, then we'll convert the current into an
19222 * interpolated string and add the string node to the list
19223 * of parts. */
19224 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
19225
19226 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
19227 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
19228 PM_NODE_START_SET_NODE(interpolated, current);
19229 start_location_set = true;
19230 current = UP(interpolated);
19231 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
19232 /* If we hit an embedded expression and the current node is
19233 * an interpolated string, then we'll just continue on. */
19234 } else {
19235 assert(false && "unreachable");
19236 }
19237
19238 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19239 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
19240 if (!start_location_set) {
19241 PM_NODE_START_SET_NODE(current, part);
19242 }
19243 break;
19244 }
19245 default:
19246 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_UPPER_ELEMENT);
19247 parser_lex(parser);
19248 break;
19249 }
19250 }
19251
19252 /* If we have a current node, then we need to append it to the list. */
19253 if (current) {
19254 pm_array_node_elements_append(parser->arena, array, current);
19255 }
19256
19257 pm_token_t closing = parser->current;
19258 if (match1(parser, PM_TOKEN_EOF)) {
19259 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_UPPER_TERM);
19260 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19261 } else {
19262 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM);
19263 }
19264 pm_array_node_close_set(parser, array, &closing);
19265
19266 return UP(array);
19267}
19268
19273static pm_node_t *
19274parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
19275 pm_token_t opening = parser->current;
19276 pm_node_flags_t paren_flags = 0;
19277
19278 pm_node_list_t current_block_exits = { 0 };
19279 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19280
19281 parser_lex(parser);
19282 while (true) {
19283 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19284 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19285 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19286 break;
19287 }
19288 }
19289
19290 /* If this is the end of the file or we match a right parenthesis, then we
19291 * have an empty parentheses node, and we can immediately return. */
19292 if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
19293 /* A command argument group sets EXPR_ENDARG before its ')' is
19294 * consumed, even when the group is empty, so that a following '{' is
19295 * scanned as a block brace. */
19296 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT) && opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19297 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19298 }
19299
19300 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19301 pop_block_exits(parser, previous_block_exits);
19302 return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, paren_flags));
19303 }
19304
19305 /* Otherwise, we're going to parse the first statement in the list of
19306 * statements within the parentheses. */
19307 context_push(parser, PM_CONTEXT_PARENS);
19308 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));
19309 context_pop(parser);
19310
19311 /* Determine if this statement is followed by a terminator. In the case of a
19312 * single statement, this is fine. But in the case of multiple statements
19313 * it's required. */
19314 bool terminator_found = false;
19315
19316 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19317 terminator_found = true;
19318 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19319 } else if (accept1(parser, PM_TOKEN_NEWLINE)) {
19320 terminator_found = true;
19321 }
19322
19323 if (terminator_found) {
19324 while (true) {
19325 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19326 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19327 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19328 break;
19329 }
19330 }
19331 }
19332
19333 /* If we hit a right parenthesis, then we're done parsing the parentheses
19334 * node, and we can check which kind of node we should return. */
19335 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19336 if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19337 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19338 }
19339
19340 parser_lex(parser);
19341 pop_block_exits(parser, previous_block_exits);
19342
19343 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19344 /* If we have a single statement and are ending on a right
19345 * parenthesis, then we need to check if this is possibly a multiple
19346 * target node. */
19347 pm_multi_target_node_t *multi_target;
19348
19349 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) && ((pm_multi_target_node_t *) statement)->lparen_loc.length == 0) {
19350 multi_target = (pm_multi_target_node_t *) statement;
19351 } else {
19352 multi_target = pm_multi_target_node_create(parser);
19353 pm_multi_target_node_targets_append(parser, multi_target, statement);
19354 }
19355
19356 multi_target->lparen_loc = TOK2LOC(parser, &opening);
19357 multi_target->rparen_loc = TOK2LOC(parser, &parser->previous);
19358 PM_NODE_START_SET_TOKEN(parser, multi_target, &opening);
19359 PM_NODE_LENGTH_SET_TOKEN(parser, multi_target, &parser->previous);
19360
19361 pm_node_t *result;
19362 if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) {
19363 result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19364 accept1(parser, PM_TOKEN_NEWLINE);
19365 } else {
19366 result = UP(multi_target);
19367 }
19368
19369 if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
19370 /* All set, this is explicitly allowed by the parent context. */
19371 } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match2(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_COMMA)) {
19372 /* All set, we're inside a for loop and we're parsing multiple
19373 * targets. A comma continues the index target list, as in
19374 * `for (a, b), c in ...`. */
19375 } else if (flags & PM_PARSE_ACCEPTS_STATEMENT) {
19376 /* The rescue-modifier value parser promotes this target on a
19377 * following `=` or comma. Reject any other binary operator that
19378 * would otherwise consume the target list (e.g. `(a, b) + c`). */
19379 if (pm_binding_powers[parser->current.type].binary && !match1(parser, PM_TOKEN_EQUAL)) {
19380 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19381 }
19382 } else if (binding_power != PM_BINDING_POWER_STATEMENT) {
19383 /* Multi targets are not allowed when it's not a statement
19384 * level. */
19385 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19386 } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
19387 /* Multi targets must be followed by an equal sign in order to
19388 * be valid (or a right parenthesis if they are nested). */
19389 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19390 }
19391
19392 return result;
19393 }
19394
19395 /* If we have a single statement and are ending on a right parenthesis
19396 * and we didn't return a multiple assignment node, then we can return a
19397 * regular parentheses node now. */
19398 pm_statements_node_t *statements = pm_statements_node_create(parser);
19399 pm_statements_node_body_append(parser, statements, statement, true);
19400
19401 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19402 }
19403
19404 /* If we have more than one statement in the set of parentheses, then we are
19405 * going to parse all of them as a list of statements. We'll do that here.
19406 */
19407 context_push(parser, PM_CONTEXT_PARENS);
19408 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19409
19410 pm_statements_node_t *statements = pm_statements_node_create(parser);
19411 pm_statements_node_body_append(parser, statements, statement, true);
19412
19413 /* If we didn't find a terminator and we didn't find a right parenthesis,
19414 * then this is a syntax error. */
19415 if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) {
19416 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19417 }
19418
19419 /* Parse each statement within the parentheses. */
19420 while (true) {
19421 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));
19422 pm_statements_node_body_append(parser, statements, node, true);
19423
19424 /* If we're recovering from a syntax error, then we need to stop parsing
19425 * the statements now. */
19426 if (parser->recovering) {
19427 /* If this is the level of context where the recovery has happened,
19428 * then we can mark the parser as done recovering. */
19429 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false;
19430 break;
19431 }
19432
19433 /* If we couldn't parse an expression at all, then we need to bail out
19434 * of the loop. */
19435 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) break;
19436
19437 /* If we successfully parsed a statement, then we are going to need a
19438 * terminator to delimit them. */
19439 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
19440 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
19441 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break;
19442 } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19443 break;
19444 } else if (!match1(parser, PM_TOKEN_EOF)) {
19445 /* If we're at the end of the file, then we're going to add an error
19446 * after this for the ) anyway. */
19447 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19448 }
19449 }
19450
19451 context_pop(parser);
19452 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19453
19454 /* When we're parsing multi targets, we allow them to be followed by a right
19455 * parenthesis if they are at the statement level. This is only possible if
19456 * they are the final statement in a parentheses. We need to explicitly
19457 * reject that here. */
19458 {
19459 pm_node_t *statement = statements->body.nodes[statements->body.size - 1];
19460
19461 if (PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19462 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
19463 pm_multi_target_node_targets_append(parser, multi_target, statement);
19464
19465 statement = UP(multi_target);
19466 statements->body.nodes[statements->body.size - 1] = statement;
19467 }
19468
19469 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) {
19470 const uint8_t *offset = parser->start + PM_NODE_END(statement);
19471 pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset };
19472 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(statement), 0));
19473
19474 statement = UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value));
19475 statements->body.nodes[statements->body.size - 1] = statement;
19476
19477 pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED);
19478 }
19479 }
19480
19481 pop_block_exits(parser, previous_block_exits);
19482 pm_void_statements_check(parser, statements, true);
19483 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19484}
19485
19491static pm_node_t *
19492parse_splat(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19493 pm_token_t operator = parser->previous;
19494 pm_node_t *name = NULL;
19495
19496 if (token_begins_expression_p(parser->current.type)) {
19497 name = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
19498 }
19499
19500 return UP(pm_splat_node_create(parser, &operator, name));
19501}
19502
19506static PRISM_INLINE pm_node_t *
19507parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
19508 switch (parser->current.type) {
19509 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
19510 parser_lex(parser);
19511
19512 pm_array_node_t *array = pm_array_node_create(parser, &parser->previous);
19513 bool parsed_bare_hash = false;
19514
19515 while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
19516 bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
19517
19518 // Handle the case where we don't have a comma and we have a
19519 // newline followed by a right bracket.
19520 if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19521 break;
19522 }
19523
19524 // Ensure that we have a comma between elements in the array.
19525 if (array->elements.size > 0) {
19526 if (accept1(parser, PM_TOKEN_COMMA)) {
19527 // If there was a comma but we also accepts a newline,
19528 // then this is a syntax error.
19529 if (accepted_newline) {
19530 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
19531 }
19532 } else {
19533 // If there was no comma, then we need to add a syntax
19534 // error.
19535 PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_END(parser, &parser->previous), 0, PM_ERR_ARRAY_SEPARATOR, pm_token_str(parser->current.type));
19536 parser->previous.start = parser->previous.end;
19537 parser->previous.type = 0;
19538 }
19539 }
19540
19541 // If we have a right bracket immediately following a comma,
19542 // this is allowed since it's a trailing comma. In this case we
19543 // can break out of the loop.
19544 if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break;
19545
19546 pm_node_t *element;
19547
19548 if (accept1(parser, PM_TOKEN_USTAR)) {
19549 pm_token_t operator = parser->previous;
19550 pm_node_t *expression = NULL;
19551
19552 if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) {
19553 pm_parser_scope_forwarding_positionals_check(parser, &operator);
19554 } else {
19555 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));
19556 }
19557
19558 element = UP(pm_splat_node_create(parser, &operator, expression));
19559 } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) {
19560 if (parsed_bare_hash) {
19561 pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH);
19562 }
19563
19564 element = UP(pm_keyword_hash_node_create(parser));
19565 pm_static_literals_t hash_keys = { 0 };
19566
19567 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)) {
19568 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
19569 }
19570
19571 pm_static_literals_free(&hash_keys);
19572 parsed_bare_hash = true;
19573 } else {
19574 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));
19575
19576 if (pm_symbol_node_label_p(parser, element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
19577 if (parsed_bare_hash) {
19578 pm_parser_err_previous(parser, PM_ERR_EXPRESSION_BARE_HASH);
19579 }
19580
19581 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
19582 pm_static_literals_t hash_keys = { 0 };
19583 pm_hash_key_static_literals_add(parser, &hash_keys, element);
19584
19585 pm_token_t operator = { 0 };
19586 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
19587 operator = parser->previous;
19588 }
19589
19590 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));
19591 pm_node_t *assoc = UP(pm_assoc_node_create(parser, element, NTOK2PTR(operator), value));
19592 pm_keyword_hash_node_elements_append(parser->arena, hash, assoc);
19593
19594 element = UP(hash);
19595 if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19596 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
19597 }
19598
19599 pm_static_literals_free(&hash_keys);
19600 parsed_bare_hash = true;
19601 }
19602 }
19603
19604 pm_array_node_elements_append(parser->arena, array, element);
19605 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
19606 }
19607
19608 accept1(parser, PM_TOKEN_NEWLINE);
19609
19610 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19611 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
19612 parser->previous.start = parser->previous.end;
19613 parser->previous.type = 0;
19614 }
19615
19616 pm_array_node_close_set(parser, array, &parser->previous);
19617
19618 return UP(array);
19619 }
19620 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
19621 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
19622 return parse_parentheses(parser, binding_power, flags, depth);
19623 case PM_TOKEN_BRACE_LEFT_HASH: {
19624 parser_lex(parser);
19625
19626 pm_token_t opening = parser->previous;
19627 pm_hash_node_t *node = pm_hash_node_create(parser, &opening);
19628
19629 if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
19630 pm_static_literals_t hash_keys = { 0 };
19631 parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1));
19632 pm_static_literals_free(&hash_keys);
19633
19634 accept1(parser, PM_TOKEN_NEWLINE);
19635 }
19636
19637 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening);
19638 pm_hash_node_closing_loc_set(parser, node, &parser->previous);
19639
19640 return UP(node);
19641 }
19642 case PM_TOKEN_CHARACTER_LITERAL: {
19643 pm_node_t *node = UP(pm_string_node_create_current_string(
19644 parser,
19645 &(pm_token_t) {
19646 .type = PM_TOKEN_STRING_BEGIN,
19647 .start = parser->current.start,
19648 .end = parser->current.start + 1
19649 },
19650 &(pm_token_t) {
19651 .type = PM_TOKEN_STRING_CONTENT,
19652 .start = parser->current.start + 1,
19653 .end = parser->current.end
19654 },
19655 NULL
19656 ));
19657
19658 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
19659
19660 // Skip past the character literal here, since now we have handled
19661 // parser->explicit_encoding correctly.
19662 parser_lex(parser);
19663
19664 // Characters can be followed by strings in which case they are
19665 // automatically concatenated.
19666 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
19667 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
19668 }
19669
19670 return node;
19671 }
19672 case PM_TOKEN_CLASS_VARIABLE: {
19673 parser_lex(parser);
19674 pm_node_t *node = UP(pm_class_variable_read_node_create(parser, &parser->previous));
19675
19676 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19677 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19678 }
19679
19680 return node;
19681 }
19682 case PM_TOKEN_CONSTANT: {
19683 parser_lex(parser);
19684 pm_token_t constant = parser->previous;
19685
19686 // If a constant is immediately followed by parentheses, then this is in
19687 // fact a method call, not a constant read.
19688 if (
19689 match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
19690 ((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))) ||
19691 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
19692 match1(parser, PM_TOKEN_BRACE_LEFT)
19693 ) {
19694 pm_arguments_t arguments = { 0 };
19695 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
19696 return UP(pm_call_node_fcall_create(parser, &constant, &arguments));
19697 }
19698
19699 pm_node_t *node = UP(pm_constant_read_node_create(parser, &parser->previous));
19700
19701 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19702 // If we get here, then we have a comma immediately following a
19703 // constant, so we're going to parse this as a multiple assignment.
19704 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19705 }
19706
19707 return node;
19708 }
19709 case PM_TOKEN_UCOLON_COLON: {
19710 parser_lex(parser);
19711 pm_token_t delimiter = parser->previous;
19712
19713 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
19714 pm_node_t *node = UP(pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous));
19715
19716 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19717 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19718 }
19719
19720 return node;
19721 }
19722 case PM_TOKEN_UDOT_DOT:
19723 case PM_TOKEN_UDOT_DOT_DOT: {
19724 pm_token_t operator = parser->current;
19725 parser_lex(parser);
19726
19727 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));
19728
19729 // Unary .. and ... are special because these are non-associative
19730 // operators that can also be unary operators. In this case we need
19731 // to explicitly reject code that has a .. or ... that follows this
19732 // expression.
19733 if (match2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
19734 pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
19735 }
19736
19737 return UP(pm_range_node_create(parser, NULL, &operator, right));
19738 }
19739 case PM_TOKEN_FLOAT:
19740 parser_lex(parser);
19741 return UP(pm_float_node_create(parser, &parser->previous));
19742 case PM_TOKEN_FLOAT_IMAGINARY:
19743 parser_lex(parser);
19744 return UP(pm_float_node_imaginary_create(parser, &parser->previous));
19745 case PM_TOKEN_FLOAT_RATIONAL:
19746 parser_lex(parser);
19747 return UP(pm_float_node_rational_create(parser, &parser->previous));
19748 case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY:
19749 parser_lex(parser);
19750 return UP(pm_float_node_rational_imaginary_create(parser, &parser->previous));
19751 case PM_TOKEN_NUMBERED_REFERENCE: {
19752 parser_lex(parser);
19753 pm_node_t *node = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
19754
19755 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19756 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19757 }
19758
19759 return node;
19760 }
19761 case PM_TOKEN_GLOBAL_VARIABLE: {
19762 parser_lex(parser);
19763 pm_node_t *node = UP(pm_global_variable_read_node_create(parser, &parser->previous));
19764
19765 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19766 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19767 }
19768
19769 return node;
19770 }
19771 case PM_TOKEN_BACK_REFERENCE: {
19772 parser_lex(parser);
19773 pm_node_t *node = UP(pm_back_reference_read_node_create(parser, &parser->previous));
19774
19775 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19776 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19777 }
19778
19779 return node;
19780 }
19781 case PM_TOKEN_IDENTIFIER:
19782 case PM_TOKEN_METHOD_NAME: {
19783 parser_lex(parser);
19784 pm_token_t identifier = parser->previous;
19785 pm_node_t *node = parse_variable_call(parser);
19786
19787 if (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
19788 // If parse_variable_call returned with a call node, then we
19789 // know the identifier is not in the local table. In that case
19790 // we need to check if there are arguments following the
19791 // identifier.
19792 pm_call_node_t *call = (pm_call_node_t *) node;
19793 pm_arguments_t arguments = { 0 };
19794
19795 if (parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1))) {
19796 // Since we found arguments, we need to turn off the
19797 // variable call bit in the flags.
19798 pm_node_flag_unset(UP(call), PM_CALL_NODE_FLAGS_VARIABLE_CALL);
19799
19800 call->opening_loc = arguments.opening_loc;
19801 call->arguments = arguments.arguments;
19802 call->closing_loc = arguments.closing_loc;
19803 call->block = arguments.block;
19804
19805 const pm_location_t *end = pm_arguments_end(&arguments);
19806 if (end == NULL) {
19807 PM_NODE_LENGTH_SET_LOCATION(call, &call->message_loc);
19808 } else {
19809 PM_NODE_LENGTH_SET_LOCATION(call, end);
19810 }
19811 }
19812 } else {
19813 // Otherwise, we know the identifier is in the local table. This
19814 // can still be a method call if it is followed by arguments or
19815 // a block, so we need to check for that here.
19816 if (
19817 ((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))) ||
19818 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
19819 match1(parser, PM_TOKEN_BRACE_LEFT)
19820 ) {
19821 pm_arguments_t arguments = { 0 };
19822 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
19823 pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments);
19824
19825 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
19826 // If we're about to convert an 'it' implicit local
19827 // variable read into a method call, we need to remove
19828 // it from the list of implicit local variables.
19829 pm_node_unreference(parser, node);
19830 } else {
19831 // Otherwise, we're about to convert a regular local
19832 // variable read into a method call, in which case we
19833 // need to indicate that this was not a read for the
19834 // purposes of warnings.
19835 assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE));
19836
19837 if (pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &identifier), PM_TOKEN_LENGTH(&identifier))) {
19838 pm_node_unreference(parser, node);
19839 } else {
19841 pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
19842 }
19843 }
19844
19845 return UP(fcall);
19846 }
19847 }
19848
19849 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19850 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19851 }
19852
19853 return node;
19854 }
19855 case PM_TOKEN_HEREDOC_START: {
19856 // Here we have found a heredoc. We'll parse it and add it to the
19857 // list of strings.
19858 assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC);
19859 pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base;
19860
19861 size_t common_whitespace = (size_t) -1;
19862 parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace;
19863
19864 parser_lex(parser);
19865 pm_token_t opening = parser->previous;
19866
19867 pm_node_t *node;
19868 pm_node_t *part;
19869
19870 if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19871 // If we get here, then we have an empty heredoc. We'll create
19872 // an empty content token and return an empty string node.
19873 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19874 pm_token_t content = parse_strings_empty_content(parser->previous.start);
19875
19876 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19877 node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19878 } else {
19879 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19880 }
19881
19882 PM_NODE_LENGTH_SET_TOKEN(parser, node, &opening);
19883 } else if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) == NULL) {
19884 // If we get here, then we tried to find something in the
19885 // heredoc but couldn't actually parse anything, so we'll just
19886 // return a missing node.
19887 //
19888 // parse_string_part handles its own errors, so there is no need
19889 // for us to add one here.
19890 node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
19891 } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19892 // If we get here, then the part that we parsed was plain string
19893 // content and we're at the end of the heredoc, so we can return
19894 // just a string node with the heredoc opening and closing as
19895 // its opening and closing.
19896 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
19897 pm_string_node_t *cast = (pm_string_node_t *) part;
19898
19899 cast->opening_loc = TOK2LOC(parser, &opening);
19900 cast->closing_loc = TOK2LOC(parser, &parser->current);
19901 cast->base.location = cast->opening_loc;
19902
19903 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19904 assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
19905 cast->base.type = PM_X_STRING_NODE;
19906 }
19907
19908 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19909 parse_heredoc_dedent_string(parser->arena, &cast->unescaped, common_whitespace);
19910 }
19911
19912 node = UP(cast);
19913 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19914 } else {
19915 // If we get here, then we have multiple parts in the heredoc,
19916 // so we'll need to create an interpolated string node to hold
19917 // them all.
19918 pm_node_list_t parts = { 0 };
19919 pm_node_list_append(parser->arena, &parts, part);
19920
19921 while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19922 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19923 pm_node_list_append(parser->arena, &parts, part);
19924 }
19925 }
19926
19927 // Now that we have all of the parts, create the correct type of
19928 // interpolated node.
19929 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19930 pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19931 cast->parts = parts;
19932
19933 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19934 pm_interpolated_xstring_node_closing_set(parser, cast, &parser->previous);
19935
19936 cast->base.location = cast->opening_loc;
19937 node = UP(cast);
19938 } else {
19939 pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
19940
19941 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19942 pm_interpolated_string_node_closing_set(parser, cast, &parser->previous);
19943
19944 cast->base.location = cast->opening_loc;
19945 node = UP(cast);
19946 }
19947
19948 // If this is a heredoc that is indented with a ~, then we need
19949 // to dedent each line by the common leading whitespace.
19950 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19951 pm_node_list_t *nodes;
19952 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19953 nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
19954 } else {
19955 nodes = &((pm_interpolated_string_node_t *) node)->parts;
19956 }
19957
19958 parse_heredoc_dedent(parser, nodes, common_whitespace);
19959 }
19960 }
19961
19962 /* If a missing terminator left this heredoc's lex mode on the
19963 * stack, it still points at our stack-local common_whitespace.
19964 * Clear the pointer so that subsequent lexing cannot read from
19965 * this function's dead stack frame. */
19966 pm_lex_mode_t *whitespace_mode = parser->lex_modes.current;
19967 do {
19968 if (whitespace_mode->mode == PM_LEX_HEREDOC && whitespace_mode->as.heredoc.common_whitespace == &common_whitespace) {
19969 whitespace_mode->as.heredoc.common_whitespace = NULL;
19970 }
19971 whitespace_mode = whitespace_mode->prev;
19972 } while (whitespace_mode != NULL);
19973
19974 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
19975 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
19976 }
19977
19978 return node;
19979 }
19980 case PM_TOKEN_INSTANCE_VARIABLE: {
19981 parser_lex(parser);
19982 pm_node_t *node = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
19983
19984 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19985 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19986 }
19987
19988 return node;
19989 }
19990 case PM_TOKEN_INTEGER: {
19991 pm_node_flags_t base = parser->integer.base;
19992 parser_lex(parser);
19993 return UP(pm_integer_node_create(parser, base, &parser->previous));
19994 }
19995 case PM_TOKEN_INTEGER_IMAGINARY: {
19996 pm_node_flags_t base = parser->integer.base;
19997 parser_lex(parser);
19998 return UP(pm_integer_node_imaginary_create(parser, base, &parser->previous));
19999 }
20000 case PM_TOKEN_INTEGER_RATIONAL: {
20001 pm_node_flags_t base = parser->integer.base;
20002 parser_lex(parser);
20003 return UP(pm_integer_node_rational_create(parser, base, &parser->previous));
20004 }
20005 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: {
20006 pm_node_flags_t base = parser->integer.base;
20007 parser_lex(parser);
20008 return UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous));
20009 }
20010 case PM_TOKEN_KEYWORD___ENCODING__:
20011 parser_lex(parser);
20012 return UP(pm_source_encoding_node_create(parser, &parser->previous));
20013 case PM_TOKEN_KEYWORD___FILE__:
20014 parser_lex(parser);
20015 return UP(pm_source_file_node_create(parser, &parser->previous));
20016 case PM_TOKEN_KEYWORD___LINE__:
20017 parser_lex(parser);
20018 return UP(pm_source_line_node_create(parser, &parser->previous));
20019 case PM_TOKEN_KEYWORD_ALIAS: {
20020 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20021 pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS);
20022 }
20023
20024 parser_lex(parser);
20025 pm_token_t keyword = parser->previous;
20026
20027 pm_node_t *new_name = parse_alias_argument(parser, true, (uint16_t) (depth + 1));
20028 pm_node_t *old_name = parse_alias_argument(parser, false, (uint16_t) (depth + 1));
20029
20030 switch (PM_NODE_TYPE(new_name)) {
20031 case PM_BACK_REFERENCE_READ_NODE:
20032 case PM_NUMBERED_REFERENCE_READ_NODE:
20033 case PM_GLOBAL_VARIABLE_READ_NODE: {
20034 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)) {
20035 if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) {
20036 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE);
20037 }
20038 } else if (!PM_NODE_TYPE_P(old_name, PM_ERROR_RECOVERY_NODE)) {
20039 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
20040 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
20041 }
20042
20043 return UP(pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name));
20044 }
20045 case PM_SYMBOL_NODE:
20046 case PM_INTERPOLATED_SYMBOL_NODE: {
20047 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)) {
20048 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
20049 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
20050 }
20051 }
20053 default:
20054 return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name));
20055 }
20056 }
20057 case PM_TOKEN_KEYWORD_CASE:
20058 return parse_case(parser, flags, depth);
20059 case PM_TOKEN_KEYWORD_BEGIN: {
20060 size_t opening_newline_index = token_newline_index(parser);
20061 parser_lex(parser);
20062
20063 pm_token_t begin_keyword = parser->previous;
20064 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20065
20066 pm_node_list_t current_block_exits = { 0 };
20067 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
20068 pm_statements_node_t *begin_statements = NULL;
20069
20070 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
20071 pm_accepts_block_stack_push(parser, true);
20072 begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN, (uint16_t) (depth + 1));
20073 pm_accepts_block_stack_pop(parser);
20074 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20075 }
20076
20077 pm_begin_node_t *begin_node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
20078 parse_rescues(parser, opening_newline_index, &begin_keyword, begin_node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1));
20079 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword);
20080
20081 PM_NODE_LENGTH_SET_TOKEN(parser, begin_node, &parser->previous);
20082 pm_begin_node_end_keyword_set(parser, begin_node, &parser->previous);
20083 pop_block_exits(parser, previous_block_exits);
20084 return UP(begin_node);
20085 }
20086 case PM_TOKEN_KEYWORD_BEGIN_UPCASE: {
20087 pm_node_list_t current_block_exits = { 0 };
20088 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
20089
20090 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20091 pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
20092 }
20093
20094 parser_lex(parser);
20095 pm_token_t keyword = parser->previous;
20096
20097 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_BEGIN_UPCASE_BRACE);
20098 pm_token_t opening = parser->previous;
20099 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE, (uint16_t) (depth + 1));
20100
20101 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening);
20102 pm_context_t context = parser->current_context->context;
20103 if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
20104 pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
20105 }
20106
20107 flush_block_exits(parser, previous_block_exits);
20108 return UP(pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous));
20109 }
20110 case PM_TOKEN_KEYWORD_BREAK:
20111 case PM_TOKEN_KEYWORD_NEXT:
20112 case PM_TOKEN_KEYWORD_RETURN: {
20113 parser_lex(parser);
20114
20115 pm_token_t keyword = parser->previous;
20116 pm_arguments_t arguments = { 0 };
20117
20118 if (
20119 token_begins_expression_p(parser->current.type) ||
20120 match2(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)
20121 ) {
20122 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
20123
20124 if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
20125 pm_token_t next = parser->current;
20126 parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
20127
20128 // Reject `foo && return bar`.
20129 if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) {
20130 PM_PARSER_ERR_TOKEN_FORMAT(parser, &next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(next.type));
20131 }
20132
20133 // Reject a trailing comma, e.g. `return a,`. The arguments
20134 // parser silently accepts a trailing comma only when it is
20135 // immediately followed by the EOF terminator; in every other
20136 // case (e.g. `return a,;`) it reports the dangling comma
20137 // itself. We reject the accepted case here to stay in line
20138 // with the command call argument parsing above.
20139 if (parser->previous.type == PM_TOKEN_COMMA && match1(parser, PM_TOKEN_EOF)) {
20140 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
20141 }
20142 }
20143
20144 // It's possible that we've parsed a block argument through our
20145 // call to parse_arguments. If we found one, we should mark it
20146 // as invalid and destroy it, as we don't have a place for it.
20147 if (arguments.block != NULL) {
20148 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20149 pm_node_unreference(parser, arguments.block);
20150 arguments.block = NULL;
20151 }
20152 }
20153
20154 switch (keyword.type) {
20155 case PM_TOKEN_KEYWORD_BREAK: {
20156 pm_node_t *node = UP(pm_break_node_create(parser, &keyword, arguments.arguments));
20157 if (!parser->partial_script) parse_block_exit(parser, node);
20158 return node;
20159 }
20160 case PM_TOKEN_KEYWORD_NEXT: {
20161 pm_node_t *node = UP(pm_next_node_create(parser, &keyword, arguments.arguments));
20162 if (!parser->partial_script) parse_block_exit(parser, node);
20163 return node;
20164 }
20165 case PM_TOKEN_KEYWORD_RETURN: {
20166 pm_node_t *node = UP(pm_return_node_create(parser, &keyword, arguments.arguments));
20167 parse_return(parser, node);
20168 return node;
20169 }
20170 default:
20171 assert(false && "unreachable");
20172 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20173 }
20174 }
20175 case PM_TOKEN_KEYWORD_SUPER: {
20176 parser_lex(parser);
20177
20178 pm_token_t keyword = parser->previous;
20179 pm_arguments_t arguments = { 0 };
20180 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20181
20182 if (
20183 arguments.opening_loc.length == 0 &&
20184 arguments.arguments == NULL &&
20185 ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE))
20186 ) {
20187 return UP(pm_forwarding_super_node_create(parser, &keyword, &arguments));
20188 }
20189
20190 return UP(pm_super_node_create(parser, &keyword, &arguments));
20191 }
20192 case PM_TOKEN_KEYWORD_YIELD: {
20193 parser_lex(parser);
20194
20195 pm_token_t keyword = parser->previous;
20196 pm_arguments_t arguments = { 0 };
20197 parse_arguments_list(parser, &arguments, false, flags, (uint16_t) (depth + 1));
20198
20199 // It's possible that we've parsed a block argument through our
20200 // call to parse_arguments_list. If we found one, we should mark it
20201 // as invalid and destroy it, as we don't have a place for it on the
20202 // yield node.
20203 if (arguments.block != NULL) {
20204 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20205 pm_node_unreference(parser, arguments.block);
20206 arguments.block = NULL;
20207 }
20208
20209 pm_node_t *node = UP(pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc));
20210 if (!parser->parsing_eval && !parser->partial_script) parse_yield(parser, node);
20211
20212 return node;
20213 }
20214 case PM_TOKEN_KEYWORD_CLASS:
20215 return parse_class(parser, flags, depth);
20216 case PM_TOKEN_KEYWORD_DEF:
20217 return parse_def(parser, binding_power, flags, depth);
20218 case PM_TOKEN_KEYWORD_DEFINED: {
20219 parser_lex(parser);
20220
20221 pm_token_t keyword = parser->previous;
20222 pm_token_t lparen = { 0 };
20223 pm_token_t rparen = { 0 };
20224 pm_node_t *expression;
20225
20226 context_push(parser, PM_CONTEXT_DEFINED);
20227 bool newline = accept1(parser, PM_TOKEN_NEWLINE);
20228
20229 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
20230 lparen = parser->previous;
20231
20232 if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20233 expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
20234 lparen = (pm_token_t) { 0 };
20235 } else {
20236 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));
20237
20238 if (!parser->recovering) {
20239 accept1(parser, PM_TOKEN_NEWLINE);
20240 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20241 rparen = parser->previous;
20242 }
20243 }
20244 } else {
20245 expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
20246 }
20247
20248 context_pop(parser);
20249 return UP(pm_defined_node_create(
20250 parser,
20251 NTOK2PTR(lparen),
20252 expression,
20253 NTOK2PTR(rparen),
20254 &keyword
20255 ));
20256 }
20257 case PM_TOKEN_KEYWORD_END_UPCASE: {
20258 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20259 pm_parser_err_current(parser, PM_ERR_STATEMENT_POSTEXE_END);
20260 }
20261
20262 parser_lex(parser);
20263 pm_token_t keyword = parser->previous;
20264
20265 if (context_def_p(parser)) {
20266 pm_parser_warn_token(parser, &keyword, PM_WARN_END_IN_METHOD);
20267 }
20268
20269 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_END_UPCASE_BRACE);
20270 pm_token_t opening = parser->previous;
20271 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE, (uint16_t) (depth + 1));
20272
20273 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening);
20274 return UP(pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous));
20275 }
20276 case PM_TOKEN_KEYWORD_FALSE:
20277 parser_lex(parser);
20278 return UP(pm_false_node_create(parser, &parser->previous));
20279 case PM_TOKEN_KEYWORD_FOR: {
20280 size_t opening_newline_index = token_newline_index(parser);
20281 parser_lex(parser);
20282
20283 pm_token_t for_keyword = parser->previous;
20284 pm_node_t *index;
20285
20286 context_push(parser, PM_CONTEXT_FOR_INDEX);
20287
20288 // First, parse out the first index expression.
20289 if (accept1(parser, PM_TOKEN_USTAR)) {
20290 index = parse_splat(parser, flags, depth);
20291 } else if (token_begins_expression_p(parser->current.type)) {
20292 index = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
20293 } else {
20294 pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX);
20295 index = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &for_keyword), PM_TOKEN_LENGTH(&for_keyword)));
20296 }
20297
20298 // Now, if there are multiple index expressions, parse them out.
20299 if (match1(parser, PM_TOKEN_COMMA)) {
20300 index = parse_targets(parser, index, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20301 } else {
20302 index = parse_target(parser, index, false, false);
20303 }
20304
20305 context_pop(parser);
20306 pm_do_loop_stack_push(parser, true);
20307
20308 expect1(parser, PM_TOKEN_KEYWORD_IN, PM_ERR_FOR_IN);
20309 pm_token_t in_keyword = parser->previous;
20310
20311 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));
20312 pm_do_loop_stack_pop(parser);
20313
20314 pm_token_t do_keyword = { 0 };
20315 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20316 do_keyword = parser->previous;
20317 } else {
20318 if (!match2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)) {
20319 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_str(parser->current.type));
20320 }
20321 }
20322
20323 pm_statements_node_t *statements = NULL;
20324 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20325 statements = parse_statements(parser, PM_CONTEXT_FOR, (uint16_t) (depth + 1));
20326 }
20327
20328 parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false);
20329 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword);
20330
20331 return UP(pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, NTOK2PTR(do_keyword), &parser->previous));
20332 }
20333 case PM_TOKEN_KEYWORD_IF:
20334 if (parser_end_of_line_p(parser)) {
20335 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
20336 }
20337
20338 size_t opening_newline_index = token_newline_index(parser);
20339 bool if_after_else = parser->previous.type == PM_TOKEN_KEYWORD_ELSE;
20340 parser_lex(parser);
20341
20342 return parse_conditional(parser, PM_CONTEXT_IF, opening_newline_index, if_after_else, (uint16_t) (depth + 1));
20343 case PM_TOKEN_KEYWORD_UNDEF: {
20344 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20345 pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF);
20346 }
20347
20348 parser_lex(parser);
20349 pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous);
20350 pm_node_t *name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20351
20352 if (PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20353 } else {
20354 pm_undef_node_append(parser->arena, undef, name);
20355
20356 while (match1(parser, PM_TOKEN_COMMA)) {
20357 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
20358 parser_lex(parser);
20359 name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20360
20361 if (PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20362 break;
20363 }
20364
20365 pm_undef_node_append(parser->arena, undef, name);
20366 }
20367 }
20368
20369 return UP(undef);
20370 }
20371 case PM_TOKEN_KEYWORD_NOT: {
20372 parser_lex(parser);
20373
20374 pm_token_t message = parser->previous;
20375 pm_arguments_t arguments = { 0 };
20376 pm_node_t *receiver = NULL;
20377
20378 // The `not` keyword without parentheses is only valid in contexts
20379 // where it would be parsed as an expression (i.e., at or below
20380 // the `not` binding power level). In other contexts (e.g., method
20381 // arguments, array elements, assignment right-hand sides),
20382 // parentheses are required: `not(x)`. An exception is made for
20383 // endless def bodies, where `not` is valid as both `arg` and
20384 // `command` (e.g., `def f = not 1`, `def f = not foo bar`).
20385 if (binding_power > PM_BINDING_POWER_NOT && !(flags & PM_PARSE_IN_ENDLESS_DEF) && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
20386 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) {
20387 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->previous), 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN);
20388 } else {
20389 accept1(parser, PM_TOKEN_NEWLINE);
20390 pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER);
20391 }
20392
20393 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
20394 }
20395
20396 accept1(parser, PM_TOKEN_NEWLINE);
20397
20398 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
20399 pm_token_t lparen = parser->previous;
20400
20401 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20402 receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
20403 } else {
20404 arguments.opening_loc = TOK2LOC(parser, &lparen);
20405 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));
20406
20407 if (!parser->recovering) {
20408 accept1(parser, PM_TOKEN_NEWLINE);
20409 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20410 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
20411 }
20412 }
20413 } else {
20414 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));
20415 }
20416
20417 return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
20418 }
20419 case PM_TOKEN_KEYWORD_UNLESS: {
20420 size_t opening_newline_index = token_newline_index(parser);
20421 parser_lex(parser);
20422
20423 return parse_conditional(parser, PM_CONTEXT_UNLESS, opening_newline_index, false, (uint16_t) (depth + 1));
20424 }
20425 case PM_TOKEN_KEYWORD_MODULE:
20426 return parse_module(parser, flags, depth);
20427 case PM_TOKEN_KEYWORD_NIL:
20428 parser_lex(parser);
20429 return UP(pm_nil_node_create(parser, &parser->previous));
20430 case PM_TOKEN_KEYWORD_REDO: {
20431 parser_lex(parser);
20432
20433 pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous));
20434 if (!parser->partial_script) parse_block_exit(parser, node);
20435
20436 return node;
20437 }
20438 case PM_TOKEN_KEYWORD_RETRY: {
20439 parser_lex(parser);
20440
20441 pm_node_t *node = UP(pm_retry_node_create(parser, &parser->previous));
20442 parse_retry(parser, node);
20443
20444 return node;
20445 }
20446 case PM_TOKEN_KEYWORD_SELF:
20447 parser_lex(parser);
20448 return UP(pm_self_node_create(parser, &parser->previous));
20449 case PM_TOKEN_KEYWORD_TRUE:
20450 parser_lex(parser);
20451 return UP(pm_true_node_create(parser, &parser->previous));
20452 case PM_TOKEN_KEYWORD_UNTIL: {
20453 size_t opening_newline_index = token_newline_index(parser);
20454
20455 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
20456 pm_do_loop_stack_push(parser, true);
20457
20458 parser_lex(parser);
20459 pm_token_t keyword = parser->previous;
20460 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));
20461
20462 pm_do_loop_stack_pop(parser);
20463 context_pop(parser);
20464
20465 pm_token_t do_keyword = { 0 };
20466 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20467 do_keyword = parser->previous;
20468 } else {
20469 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
20470 }
20471
20472 pm_statements_node_t *statements = NULL;
20473 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20474 pm_accepts_block_stack_push(parser, true);
20475 statements = parse_statements(parser, PM_CONTEXT_UNTIL, (uint16_t) (depth + 1));
20476 pm_accepts_block_stack_pop(parser);
20477 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20478 }
20479
20480 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
20481 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword);
20482
20483 return UP(pm_until_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0));
20484 }
20485 case PM_TOKEN_KEYWORD_WHILE: {
20486 size_t opening_newline_index = token_newline_index(parser);
20487
20488 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
20489 pm_do_loop_stack_push(parser, true);
20490
20491 parser_lex(parser);
20492 pm_token_t keyword = parser->previous;
20493 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));
20494
20495 pm_do_loop_stack_pop(parser);
20496 context_pop(parser);
20497
20498 pm_token_t do_keyword = { 0 };
20499 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20500 do_keyword = parser->previous;
20501 } else {
20502 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
20503 }
20504
20505 pm_statements_node_t *statements = NULL;
20506 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20507 pm_accepts_block_stack_push(parser, true);
20508 statements = parse_statements(parser, PM_CONTEXT_WHILE, (uint16_t) (depth + 1));
20509 pm_accepts_block_stack_pop(parser);
20510 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20511 }
20512
20513 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
20514 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword);
20515
20516 return UP(pm_while_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0));
20517 }
20518 case PM_TOKEN_PERCENT_LOWER_I: {
20519 parser_lex(parser);
20520 pm_token_t opening = parser->previous;
20521 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20522 pm_node_t *current = NULL;
20523
20524 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20525 accept1(parser, PM_TOKEN_WORDS_SEP);
20526 if (match1(parser, PM_TOKEN_STRING_END)) break;
20527
20528 // Interpolation is not possible but nested heredocs can still lead to
20529 // consecutive (disjoint) string tokens when the final newline is escaped.
20530 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20531 // Record the string node, moving to interpolation if needed.
20532 if (current == NULL) {
20533 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
20534 parser_lex(parser);
20535 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20536 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20537 parser_lex(parser);
20538 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
20539 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20540 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
20541 pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start + cast->content_loc.start, .end = parser->start + cast->content_loc.start + cast->content_loc.length };
20542 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
20543 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
20544 parser_lex(parser);
20545
20546 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20547 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
20548 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
20549
20550 // current is arena-allocated so no explicit free is needed.
20551 current = UP(interpolated);
20552 } else {
20553 assert(false && "unreachable");
20554 }
20555 }
20556
20557 if (current) {
20558 pm_array_node_elements_append(parser->arena, array, current);
20559 current = NULL;
20560 } else {
20561 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT);
20562 }
20563 }
20564
20565 pm_token_t closing = parser->current;
20566 if (match1(parser, PM_TOKEN_EOF)) {
20567 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_LOWER_TERM);
20568 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20569 } else {
20570 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM);
20571 }
20572 pm_array_node_close_set(parser, array, &closing);
20573
20574 return UP(array);
20575 }
20576 case PM_TOKEN_PERCENT_UPPER_I:
20577 return parse_symbol_array(parser, depth);
20578 case PM_TOKEN_PERCENT_LOWER_W: {
20579 parser_lex(parser);
20580 pm_token_t opening = parser->previous;
20581 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20582 pm_node_t *current = NULL;
20583
20584 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20585 accept1(parser, PM_TOKEN_WORDS_SEP);
20586 if (match1(parser, PM_TOKEN_STRING_END)) break;
20587
20588 // Interpolation is not possible but nested heredocs can still lead to
20589 // consecutive (disjoint) string tokens when the final newline is escaped.
20590 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20591 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20592
20593 // Record the string node, moving to interpolation if needed.
20594 if (current == NULL) {
20595 current = string;
20596 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20597 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
20598 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20599 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20600 pm_interpolated_string_node_append(parser, interpolated, current);
20601 pm_interpolated_string_node_append(parser, interpolated, string);
20602 current = UP(interpolated);
20603 } else {
20604 assert(false && "unreachable");
20605 }
20606 parser_lex(parser);
20607 }
20608
20609 if (current) {
20610 pm_array_node_elements_append(parser->arena, array, current);
20611 current = NULL;
20612 } else {
20613 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT);
20614 }
20615 }
20616
20617 pm_token_t closing = parser->current;
20618 if (match1(parser, PM_TOKEN_EOF)) {
20619 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_LOWER_TERM);
20620 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20621 } else {
20622 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM);
20623 }
20624
20625 pm_array_node_close_set(parser, array, &closing);
20626 return UP(array);
20627 }
20628 case PM_TOKEN_PERCENT_UPPER_W:
20629 return parse_string_array(parser, depth);
20630 case PM_TOKEN_REGEXP_BEGIN: {
20631 pm_token_t opening = parser->current;
20632 parser_lex(parser);
20633
20634 if (match1(parser, PM_TOKEN_REGEXP_END)) {
20635 // If we get here, then we have an end immediately after a start. In
20636 // that case we'll create an empty content token and return an
20637 // uninterpolated regular expression.
20638 pm_token_t content = (pm_token_t) {
20639 .type = PM_TOKEN_STRING_CONTENT,
20640 .start = parser->previous.end,
20641 .end = parser->previous.end
20642 };
20643
20644 parser_lex(parser);
20645
20646 pm_regular_expression_node_t *node = pm_regular_expression_node_create(parser, &opening, &content, &parser->previous);
20647 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
20648 return UP(node);
20649 }
20650
20652
20653 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20654 // In this case we've hit string content so we know the regular
20655 // expression at least has something in it. We'll need to check if the
20656 // following token is the end (in which case we can return a plain
20657 // regular expression) or if it's not then it has interpolation.
20658 pm_string_t unescaped = parser->current_string;
20659 pm_token_t content = parser->current;
20660 parser_lex(parser);
20661
20662 // If we hit an end, then we can create a regular expression
20663 // node without interpolation, which can be represented more
20664 // succinctly and more easily compiled.
20665 if (accept1(parser, PM_TOKEN_REGEXP_END)) {
20666 pm_regular_expression_node_t *node = (pm_regular_expression_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
20667
20668 // If we're not immediately followed by a =~, then we
20669 // parse and validate now. If it is followed by a =~,
20670 // then it will get parsed in the =~ handler where
20671 // named captures can also be extracted.
20672 if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) {
20673 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
20674 }
20675
20676 return UP(node);
20677 }
20678
20679 // If we get here, then we have interpolation so we'll need to create
20680 // a regular expression node with interpolation.
20681 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
20682
20683 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
20684 if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
20685 // This is extremely strange, but the first string part of a
20686 // regular expression will always be tagged as binary if we
20687 // are in a US-ASCII file, no matter its contents.
20688 pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING);
20689 }
20690
20691 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
20692 } else {
20693 // If the first part of the body of the regular expression is not a
20694 // string content, then we have interpolation and we need to create an
20695 // interpolated regular expression node.
20696 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
20697 }
20698
20699 // Now that we're here and we have interpolation, we'll parse all of the
20700 // parts into the list.
20701 pm_node_t *part;
20702 while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) {
20703 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
20704 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
20705 }
20706 }
20707
20708 pm_token_t closing = parser->current;
20709 if (match1(parser, PM_TOKEN_EOF)) {
20710 pm_parser_err_token(parser, &opening, PM_ERR_REGEXP_TERM);
20711 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20712 } else {
20713 expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM);
20714 }
20715
20716 pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing);
20717 return UP(interpolated);
20718 }
20719 case PM_TOKEN_XSTRING_BEGIN:
20720 case PM_TOKEN_PERCENT_LOWER_X: {
20721 parser_lex(parser);
20722 pm_token_t opening = parser->previous;
20723
20724 // When we get here, we don't know if this string is going to have
20725 // interpolation or not, even though it is allowed. Still, we want to be
20726 // able to return a string node without interpolation if we can since
20727 // it'll be faster.
20728 if (match1(parser, PM_TOKEN_STRING_END)) {
20729 // If we get here, then we have an end immediately after a start. In
20730 // that case we'll create an empty content token and return an
20731 // uninterpolated string.
20732 pm_token_t content = (pm_token_t) {
20733 .type = PM_TOKEN_STRING_CONTENT,
20734 .start = parser->previous.end,
20735 .end = parser->previous.end
20736 };
20737
20738 parser_lex(parser);
20739 return UP(pm_xstring_node_create(parser, &opening, &content, &parser->previous));
20740 }
20741
20743
20744 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20745 // In this case we've hit string content so we know the string
20746 // at least has something in it. We'll need to check if the
20747 // following token is the end (in which case we can return a
20748 // plain string) or if it's not then it has interpolation.
20749 pm_string_t unescaped = parser->current_string;
20750 pm_token_t content = parser->current;
20751 parser_lex(parser);
20752
20753 if (match1(parser, PM_TOKEN_STRING_END)) {
20754 pm_node_t *node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
20755 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
20756 parser_lex(parser);
20757 return node;
20758 }
20759
20760 // If we get here, then we have interpolation so we'll need to
20761 // create a string node with interpolation.
20762 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20763
20764 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
20765 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
20766
20767 pm_interpolated_xstring_node_append(parser->arena, node, part);
20768 } else {
20769 // If the first part of the body of the string is not a string
20770 // content, then we have interpolation and we need to create an
20771 // interpolated string node.
20772 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20773 }
20774
20775 pm_node_t *part;
20776 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20777 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
20778 pm_interpolated_xstring_node_append(parser->arena, node, part);
20779 }
20780 }
20781
20782 pm_token_t closing = parser->current;
20783 if (match1(parser, PM_TOKEN_EOF)) {
20784 pm_parser_err_token(parser, &opening, PM_ERR_XSTRING_TERM);
20785 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20786 } else {
20787 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM);
20788 }
20789 pm_interpolated_xstring_node_closing_set(parser, node, &closing);
20790
20791 return UP(node);
20792 }
20793 case PM_TOKEN_USTAR: {
20794 parser_lex(parser);
20795
20796 // * operators at the beginning of expressions are only valid in the
20797 // context of a multiple assignment. We enforce that here. We'll
20798 // still lex past it though and create a missing node place.
20799 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20800 pm_parser_err_prefix(parser, diag_id);
20801 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20802 }
20803
20804 pm_node_t *splat = parse_splat(parser, flags, depth);
20805
20806 if (match1(parser, PM_TOKEN_COMMA)) {
20807 return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20808 } else {
20809 return parse_target_validate(parser, splat, true);
20810 }
20811 }
20812 case PM_TOKEN_BANG: {
20813 if (binding_power > PM_BINDING_POWER_UNARY) {
20814 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20815 }
20816
20817 parser_lex(parser);
20818
20819 pm_token_t operator = parser->previous;
20820 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));
20821 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!");
20822
20823 pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
20824 return UP(node);
20825 }
20826 case PM_TOKEN_TILDE: {
20827 if (binding_power > PM_BINDING_POWER_UNARY) {
20828 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20829 }
20830 parser_lex(parser);
20831
20832 pm_token_t operator = parser->previous;
20833 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));
20834 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~");
20835
20836 return UP(node);
20837 }
20838 case PM_TOKEN_UMINUS: {
20839 if (binding_power > PM_BINDING_POWER_UNARY) {
20840 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20841 }
20842 parser_lex(parser);
20843
20844 pm_token_t operator = parser->previous;
20845 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));
20846 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "-@");
20847
20848 return UP(node);
20849 }
20850 case PM_TOKEN_UMINUS_NUM: {
20851 parser_lex(parser);
20852
20853 pm_token_t operator = parser->previous;
20854 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));
20855
20856 if (accept1(parser, PM_TOKEN_STAR_STAR)) {
20857 pm_token_t exponent_operator = parser->previous;
20858 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));
20859 node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0));
20860 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20861 } else {
20862 switch (PM_NODE_TYPE(node)) {
20863 case PM_INTEGER_NODE:
20864 case PM_FLOAT_NODE:
20865 case PM_RATIONAL_NODE:
20866 case PM_IMAGINARY_NODE:
20867 parse_negative_numeric(node);
20868 break;
20869 default:
20870 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20871 break;
20872 }
20873 }
20874
20875 return node;
20876 }
20877 case PM_TOKEN_MINUS_GREATER: {
20878 int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting;
20879 parser->lambda_enclosure_nesting = parser->enclosure_nesting;
20880
20881 size_t opening_newline_index = token_newline_index(parser);
20882 parser_lex(parser);
20883
20884 pm_token_t operator = parser->previous;
20885 pm_parser_scope_push(parser, false);
20886
20887 pm_block_parameters_node_t *block_parameters;
20888
20889 switch (parser->current.type) {
20890 case PM_TOKEN_PARENTHESIS_LEFT: {
20891 pm_token_t opening = parser->current;
20892 parser_lex(parser);
20893
20894 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20895 block_parameters = pm_block_parameters_node_create(parser, NULL, &opening);
20896 } else {
20897 block_parameters = parse_block_parameters(parser, false, &opening, true, true, (uint16_t) (depth + 1));
20898 }
20899
20900 accept1(parser, PM_TOKEN_NEWLINE);
20901 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20902
20903 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
20904 break;
20905 }
20906 case PM_CASE_PARAMETER: {
20907 block_parameters = parse_block_parameters(parser, false, NULL, true, false, (uint16_t) (depth + 1));
20908 break;
20909 }
20910 default: {
20911 block_parameters = NULL;
20912 break;
20913 }
20914 }
20915
20916 pm_token_t opening;
20917 pm_node_t *body = NULL;
20918
20919 if (accept1(parser, PM_TOKEN_LAMBDA_BEGIN)) {
20920 opening = parser->previous;
20921
20922 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
20923 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1)));
20924 }
20925
20926 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
20927
20928 /* Restore the enclosing lambda's nesting now that the body has
20929 * been parsed, so that the token following the closing `}` is
20930 * lexed in the enclosing context. During the body the nesting
20931 * held this lambda's own level, which every token inside the
20932 * braces sits above. This mirrors parse.y restoring
20933 * `p->lex.lpar_beg` after `lambda_body`. */
20934 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
20935 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
20936 } else {
20937 /* A `-> { }` body is delimited by `{`/`}`, whose block-accepting
20938 * frame the lexer manages. A `-> do end` body is delimited by
20939 * keywords, so push the frame here and pop it before `end`. The
20940 * push must precede consuming the `do`, which lexes the first
20941 * token of the body; this matches parse.y's CMDARG_PUSH(0)
20942 * before `lambda_body`. */
20943 pm_accepts_block_stack_push(parser, true);
20944 expect1(parser, PM_TOKEN_KEYWORD_DO_LAMBDA, PM_ERR_LAMBDA_OPEN);
20945 opening = parser->previous;
20946
20947 /* The lexer cleared the nesting when it produced the `do`. If
20948 * it was missing entirely, clear it here so that the body is
20949 * recovered the same way it would have been parsed: no token
20950 * within it sits at the beginning of a lambda. */
20951 parser->lambda_enclosure_nesting = -1;
20952
20953 if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
20954 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1)));
20955 }
20956
20957 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
20958 assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE));
20959 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)));
20960 } else {
20961 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
20962 }
20963
20964 pm_accepts_block_stack_pop(parser);
20965
20966 /* As with the brace branch above, restore the nesting before
20967 * consuming the closing `end`, which lexes the token that
20968 * follows it. */
20969 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
20970 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator);
20971 }
20972
20973 pm_constant_id_list_t locals;
20974 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
20975 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &operator, &parser->previous);
20976
20977 pm_parser_scope_pop(parser);
20978
20979 return UP(pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body));
20980 }
20981 case PM_TOKEN_UPLUS: {
20982 if (binding_power > PM_BINDING_POWER_UNARY) {
20983 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20984 }
20985 parser_lex(parser);
20986
20987 pm_token_t operator = parser->previous;
20988 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));
20989 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "+@");
20990
20991 return UP(node);
20992 }
20993 case PM_TOKEN_STRING_BEGIN:
20994 return parse_strings(parser, NULL, flags & PM_PARSE_ACCEPTS_LABEL, (uint16_t) (depth + 1));
20995 case PM_TOKEN_SYMBOL_BEGIN: {
20996 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
20997 parser_lex(parser);
20998
20999 return parse_symbol(parser, &lex_mode, PM_LEX_STATE_END, (uint16_t) (depth + 1));
21000 }
21001 default: {
21002 pm_context_t recoverable = context_recoverable(parser, &parser->current);
21003
21004 if (recoverable != PM_CONTEXT_NONE) {
21005 parser->recovering = true;
21006
21007 // If the given error is not the generic one, then we'll add it
21008 // here because it will provide more context in addition to the
21009 // recoverable error that we will also add.
21010 if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) {
21011 pm_parser_err_prefix(parser, diag_id);
21012 }
21013
21014 // If we get here, then we are assuming this token is closing a
21015 // parent context, so we'll indicate that to the user so that
21016 // they know how we behaved.
21017 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_str(parser->current.type), context_human(recoverable));
21018 } else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) {
21019 // We're going to make a special case here, because "cannot
21020 // parse expression" is pretty generic, and we know here that we
21021 // have an unexpected token.
21022 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_str(parser->current.type));
21023 } else {
21024 pm_parser_err_prefix(parser, diag_id);
21025 }
21026
21027 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
21028 }
21029 }
21030}
21031
21032static pm_node_t *
21033parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth);
21034
21042static void
21043parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
21044 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21045 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21046 parser_lex(parser);
21047 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));
21048 }
21049}
21050
21060static pm_node_t *
21061parse_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) {
21062 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));
21063
21064 // Assignments whose value is a command call (e.g., a = b c) can only
21065 // be followed by modifiers (if/unless/while/until/rescue) and not by
21066 // operators with higher binding power. If we find one, emit an error
21067 // and skip the operator and its right-hand side.
21068 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER && (pm_command_call_value_p(parser, value) || pm_block_call_p(value))) {
21069 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21070 parser_lex(parser);
21071 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));
21072 }
21073
21074 // Contradicting binding powers, the right-hand-side value of the assignment
21075 // allows the `rescue` modifier.
21076 if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21077 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21078
21079 pm_token_t rescue = parser->current;
21080 parser_lex(parser);
21081
21082 // As in parse_assignment_values, the resbody is a `stmt` (permitting a
21083 // multiple assignment / command call) when the rescued value is itself a
21084 // command call, and a plain `arg` otherwise.
21085 bool statement_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21086 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21087
21088 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21089 context_pop(parser);
21090
21091 // A pattern-match resbody is a statement, but here the rescue is nested
21092 // in an assignment value where parse_expression_terminator cannot see
21093 // it, so reject a trailing operator above the modifier level directly.
21094 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21095 parse_rescue_modifier_terminator(parser, flags, depth);
21096 }
21097
21098 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21099 }
21100
21101 return value;
21102}
21103
21108static void
21109parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) {
21110 switch (PM_NODE_TYPE(node)) {
21111 case PM_BEGIN_NODE: {
21112 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
21113 if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements);
21114 break;
21115 }
21116 case PM_LOCAL_VARIABLE_WRITE_NODE: {
21118 pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
21119 break;
21120 }
21121 case PM_PARENTHESES_NODE: {
21122 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
21123 if (cast->body != NULL) parse_assignment_value_local(parser, cast->body);
21124 break;
21125 }
21126 case PM_STATEMENTS_NODE: {
21127 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
21128 const pm_node_t *statement;
21129
21130 PM_NODE_LIST_FOREACH(&cast->body, index, statement) {
21131 parse_assignment_value_local(parser, statement);
21132 }
21133 break;
21134 }
21135 default:
21136 break;
21137 }
21138}
21139
21152static pm_node_t *
21153parse_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) {
21154 bool statement_level = (previous_binding_power == PM_BINDING_POWER_STATEMENT) || (flags & PM_PARSE_ACCEPTS_STATEMENT);
21155
21156 bool permitted = true;
21157 if (!statement_level && match1(parser, PM_TOKEN_USTAR)) permitted = false;
21158
21159 // A command call (e.g. `x = y z`) is permitted as the value when assigning
21160 // directly (carrying the caller's flag), or in any statement-level context
21161 // — which includes a rescue modifier value via the flag.
21162 uint8_t command_call_flag = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
21163 ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21164 : ((previous_binding_power < PM_BINDING_POWER_MODIFIER || statement_level) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21165
21166 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));
21167 if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
21168
21169 parse_assignment_value_local(parser, value);
21170 bool single_value = true;
21171
21172 // Block calls (command call + do block, e.g., `foo bar do end`) cannot
21173 // be followed by a comma to form a multi-value RHS because each element
21174 // of a multi-value assignment must be an `arg`, not a `block_call`.
21175 if (statement_level && !pm_block_call_p(value) && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) {
21176 single_value = false;
21177
21178 pm_array_node_t *array = pm_array_node_create(parser, NULL);
21179 pm_array_node_elements_append(parser->arena, array, value);
21180 value = UP(array);
21181
21182 while (accept1(parser, PM_TOKEN_COMMA)) {
21183 pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1));
21184
21185 pm_array_node_elements_append(parser->arena, array, element);
21186 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
21187
21188 parse_assignment_value_local(parser, element);
21189 }
21190 }
21191
21192 // Assignments whose value is a command call (e.g., a = b c) can only
21193 // be followed by modifiers (if/unless/while/until/rescue) and not by
21194 // operators with higher binding power. If we find one, emit an error
21195 // and skip the operator and its right-hand side.
21196 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))) {
21197 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21198 parser_lex(parser);
21199 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));
21200 }
21201
21202 // Contradicting binding powers, the right-hand-side value of the assignment
21203 // allows the `rescue` modifier.
21204 bool multiple_assignment = (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1));
21205 if ((single_value || multiple_assignment) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21206 bool command_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21207
21208 // A multiple assignment whose value is a command call (`x, y = foo
21209 // bar`) is a complete statement (parse.y: `mlhs '='
21210 // command_call_value`, which has no rescue), so a trailing `rescue`
21211 // modifies the whole assignment rather than the value. Leave it for the
21212 // statement-level rescue instead of binding it to the value here. For a
21213 // non-command value the rescue does bind to the value (parse.y:
21214 // `mlhs '=' mrhs_arg modifier_rescue stmt`).
21215 if (multiple_assignment && command_value) return value;
21216
21217 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21218
21219 pm_token_t rescue = parser->current;
21220 parser_lex(parser);
21221
21222 // The resbody is a `stmt` (parse.y: `command_rhs`/`mlhs '=' mrhs_arg`),
21223 // which permits a multiple assignment and a command call, when this is a
21224 // multiple assignment or the rescued value is itself a command call.
21225 // Otherwise it is a plain `arg` (parse.y: `arg_rhs`).
21226 bool statement_value = multiple_assignment || command_value;
21227 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21228
21229 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21230 context_pop(parser);
21231
21232 // A pattern-match resbody is a statement, but here the rescue is nested
21233 // in an assignment value where parse_expression_terminator cannot see
21234 // it, so reject a trailing operator above the modifier level directly.
21235 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21236 parse_rescue_modifier_terminator(parser, flags, depth);
21237 }
21238
21239 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21240 }
21241
21242 return value;
21243}
21244
21257static pm_node_t *
21258parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth) {
21259 if (statement) {
21260 pm_node_t *value;
21261 bool multiple;
21262
21263 if (match1(parser, PM_TOKEN_USTAR)) {
21264 // A leading splat can only begin a multiple assignment target list.
21265 parser_lex(parser);
21266 value = parse_splat(parser, flags, depth);
21267 multiple = true;
21268 } else {
21269 // The flag lets a single-target assignment take a multiple-value or
21270 // splat right-hand side (`b = c, d` / `b = *c`); a comma _before_ an
21271 // `=`, or a parenthesized target list (`(b, c), d = 1`), instead
21272 // promotes to a multiple assignment target list below.
21273 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));
21274 multiple = match1(parser, PM_TOKEN_COMMA) || PM_NODE_TYPE_P(value, PM_MULTI_TARGET_NODE);
21275 }
21276
21277 if (multiple) {
21278 pm_node_t *target = parse_targets_validate(parser, value, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21279
21280 // A promoted target list is only a valid rescue value as part of a
21281 // complete `targets = values`. parse_targets_validate already
21282 // reports a missing `=` for every terminator except `)` (which it
21283 // permits for an enclosing mlhs paren that does not apply here), so
21284 // reject that case.
21285 if (!match1(parser, PM_TOKEN_EQUAL)) {
21286 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
21287 return target;
21288 }
21289
21290 pm_token_t operator = parser->current;
21291 parser_lex(parser);
21292
21293 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));
21294 value = parse_write(parser, target, &operator, values);
21295 }
21296
21297 // Reject a trailing operator that cannot follow a statement resbody.
21298 // Pattern-match handlers are statements too, but for the bare statement
21299 // form they are reported by parse_expression_terminator instead (which
21300 // keeps its existing error-recovery), so they are excluded here.
21301 if (!PM_NODE_TYPE_P(value, PM_MATCH_REQUIRED_NODE) && !PM_NODE_TYPE_P(value, PM_MATCH_PREDICATE_NODE)) {
21302 parse_rescue_modifier_terminator(parser, flags, depth);
21303 }
21304
21305 return value;
21306 }
21307
21308 // Otherwise the resbody is a plain `arg` (parse.y: `arg modifier_rescue
21309 // arg`), parsed above the `and`/`or`/`not` level so those stay outside it.
21310 return parse_expression(parser, PM_BINDING_POWER_DEFINED, flags, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21311}
21312
21320static void
21321parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) {
21322 if (call_node->arguments != NULL) {
21323 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS);
21324 pm_node_unreference(parser, UP(call_node->arguments));
21325 call_node->arguments = NULL;
21326 }
21327
21328 if (call_node->block != NULL) {
21329 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK);
21330 pm_node_unreference(parser, UP(call_node->block));
21331 call_node->block = NULL;
21332 }
21333}
21334
21335static PRISM_INLINE const uint8_t *
21336pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21337 cursor++;
21338
21339 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21340 uint8_t value = escape_hexadecimal_digit(*cursor);
21341 cursor++;
21342
21343 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21344 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(*cursor));
21345 cursor++;
21346 }
21347
21348 pm_buffer_append_byte(unescaped, value);
21349 } else {
21350 pm_buffer_append_string(unescaped, "\\x", 2);
21351 }
21352
21353 return cursor;
21354}
21355
21356static PRISM_INLINE const uint8_t *
21357pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21358 uint8_t value = (uint8_t) (*cursor - '0');
21359 cursor++;
21360
21361 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21362 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21363 cursor++;
21364
21365 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21366 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21367 cursor++;
21368 }
21369 }
21370
21371 pm_buffer_append_byte(unescaped, value);
21372 return cursor;
21373}
21374
21375static PRISM_INLINE const uint8_t *
21376pm_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) {
21377 const uint8_t *start = cursor - 1;
21378 cursor++;
21379
21380 if (cursor >= end) {
21381 pm_buffer_append_string(unescaped, "\\u", 2);
21382 return cursor;
21383 }
21384
21385 if (*cursor != '{') {
21386 size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4));
21387 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21388
21389 if (!pm_buffer_append_unicode_codepoint(unescaped, value)) {
21390 pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start));
21391 }
21392
21393 return cursor + length;
21394 }
21395
21396 cursor++;
21397 for (;;) {
21398 while (cursor < end && *cursor == ' ') cursor++;
21399
21400 if (cursor >= end) break;
21401 if (*cursor == '}') {
21402 cursor++;
21403 break;
21404 }
21405
21406 size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor);
21407 if (length == 0) {
21408 break;
21409 }
21410 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21411
21412 (void) pm_buffer_append_unicode_codepoint(unescaped, value);
21413 cursor += length;
21414 }
21415
21416 return cursor;
21417}
21418
21419static void
21420pm_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) {
21421 const uint8_t *end = source + length;
21422 pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source));
21423
21424 for (;;) {
21425 if (++cursor >= end) {
21426 pm_buffer_append_byte(unescaped, '\\');
21427 return;
21428 }
21429
21430 switch (*cursor) {
21431 case 'x':
21432 cursor = pm_named_capture_escape_hex(unescaped, cursor, end);
21433 break;
21434 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
21435 cursor = pm_named_capture_escape_octal(unescaped, cursor, end);
21436 break;
21437 case 'u':
21438 cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location);
21439 break;
21440 default:
21441 pm_buffer_append_byte(unescaped, '\\');
21442 break;
21443 }
21444
21445 const uint8_t *next_cursor = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, parser->encoding);
21446 if (next_cursor == NULL) break;
21447
21448 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (next_cursor - cursor));
21449 cursor = next_cursor;
21450 }
21451
21452 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (end - cursor));
21453}
21454
21459static void
21460parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *capture, bool shared, pm_regexp_name_data_t *callback_data) {
21461 pm_call_node_t *call = callback_data->call;
21462 pm_constant_id_list_t *names = &callback_data->names;
21463
21464 const uint8_t *source = pm_string_source(capture);
21465 size_t length = pm_string_length(capture);
21466 pm_buffer_t unescaped = { 0 };
21467
21468 // First, we need to handle escapes within the name of the capture group.
21469 // This is because regular expressions have three different representations
21470 // in prism. The first is the plain source code. The second is the
21471 // representation that will be sent to the regular expression engine, which
21472 // is the value of the "unescaped" field. This is poorly named, because it
21473 // actually still contains escapes, just a subset of them that the regular
21474 // expression engine knows how to handle. The third representation is fully
21475 // unescaped, which is what we need.
21476 const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding);
21477 if (PRISM_UNLIKELY(cursor != NULL)) {
21478 pm_named_capture_escape(parser, &unescaped, source, length, cursor, shared ? NULL : &call->receiver->location);
21479 source = (const uint8_t *) pm_buffer_value(&unescaped);
21480 length = pm_buffer_length(&unescaped);
21481 }
21482
21483 const uint8_t *start;
21484 const uint8_t *end;
21485 pm_constant_id_t name;
21486
21487 // If the name of the capture group isn't a valid identifier, we do
21488 // not add it to the local table.
21489 if (!pm_slice_is_valid_local(parser, source, source + length)) {
21490 pm_buffer_cleanup(&unescaped);
21491 return;
21492 }
21493
21494 if (shared) {
21495 // If the unescaped string is a slice of the source, then we can
21496 // copy the names directly. The pointers will line up.
21497 start = source;
21498 end = source + length;
21499 name = pm_parser_constant_id_raw(parser, start, end);
21500 } else {
21501 // Otherwise, the name is a slice of the malloc-ed owned string,
21502 // in which case we need to copy it out into a new string.
21503 start = parser->start + PM_NODE_START(call->receiver);
21504 end = parser->start + PM_NODE_END(call->receiver);
21505
21506 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
21507 memcpy(memory, source, length);
21508 name = pm_parser_constant_id_owned(parser, memory, length);
21509 }
21510
21511 // Add this name to the list of constants if it is valid, not duplicated,
21512 // and not a keyword.
21513 if (name != 0 && !pm_constant_id_list_includes(names, name)) {
21514 pm_constant_id_list_append(parser->arena, names, name);
21515
21516 int depth;
21517 if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
21518 // If the local is not already a local but it is a keyword, then we
21519 // do not want to add a capture for this.
21520 if (pm_local_is_keyword((const char *) source, length)) {
21521 pm_buffer_cleanup(&unescaped);
21522 return;
21523 }
21524
21525 // If the identifier is not already a local, then we will add it to
21526 // the local table.
21527 pm_parser_local_add(parser, name, start, end, 0);
21528 }
21529
21530 // Here we lazily create the MatchWriteNode since we know we're
21531 // about to add a target.
21532 if (callback_data->match == NULL) {
21533 callback_data->match = pm_match_write_node_create(parser, call);
21534 }
21535
21536 // Next, create the local variable target and add it to the list of
21537 // targets for the match.
21538 pm_token_t token = { .type = 0, .start = start, .end = end };
21539 pm_location_t token_loc = TOK2LOC(parser, &token);
21540 pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &token_loc, name, depth == -1 ? 0 : (uint32_t) depth));
21541 pm_node_list_append(parser->arena, &callback_data->match->targets, target);
21542 }
21543
21544 pm_buffer_cleanup(&unescaped);
21545}
21546
21552static pm_node_t *
21553parse_interpolated_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call, bool extended_mode) {
21554 pm_regexp_name_data_t callback_data = {
21555 .call = call,
21556 .match = NULL,
21557 .names = { 0 },
21558 };
21559
21560 pm_regexp_parse_named_captures(parser, pm_string_source(content), pm_string_length(content), false, extended_mode, parse_regular_expression_named_capture, &callback_data);
21561
21562 if (callback_data.match != NULL) {
21563 return UP(callback_data.match);
21564 } else {
21565 return UP(call);
21566 }
21567}
21568
21569static PRISM_INLINE pm_node_t *
21570parse_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) {
21571 pm_token_t token = parser->current;
21572
21573 switch (token.type) {
21574 case PM_TOKEN_EQUAL: {
21575 switch (PM_NODE_TYPE(node)) {
21576 case PM_CALL_NODE: {
21577 // If we have no arguments to the call node and we need this
21578 // to be a target then this is either a method call or a
21579 // local variable write. This _must_ happen before the value
21580 // is parsed because it could be referenced in the value.
21581 pm_call_node_t *call_node = (pm_call_node_t *) node;
21582 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21583 pm_parser_local_add_location(parser, &call_node->message_loc, 0);
21584 }
21585 }
21587 case PM_CASE_WRITABLE: {
21588 // When we have `it = value`, we need to add `it` as a local
21589 // variable before parsing the value, in case the value
21590 // references the variable.
21591 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
21592 pm_parser_local_add_location(parser, &node->location, 0);
21593 }
21594
21595 parser_lex(parser);
21596 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));
21597
21598 if (PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) && previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
21599 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_MULTI_WRITE);
21600 }
21601
21602 return parse_write(parser, node, &token, value);
21603 }
21604 case PM_SPLAT_NODE: {
21605 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
21606 pm_multi_target_node_targets_append(parser, multi_target, node);
21607
21608 parser_lex(parser);
21609 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));
21610 return parse_write(parser, UP(multi_target), &token, value);
21611 }
21612 case PM_SOURCE_ENCODING_NODE:
21613 case PM_FALSE_NODE:
21614 case PM_SOURCE_FILE_NODE:
21615 case PM_SOURCE_LINE_NODE:
21616 case PM_NIL_NODE:
21617 case PM_SELF_NODE:
21618 case PM_TRUE_NODE: {
21619 // In these special cases, we have specific error messages
21620 // and we will replace them with local variable writes.
21621 parser_lex(parser);
21622 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
21623 return parse_unwriteable_write(parser, node, &token, value);
21624 }
21625 default:
21626 // In this case we have an = sign, but we don't know what
21627 // it's for. We need to treat it as an error. We'll mark it
21628 // as an error and skip past it.
21629 parser_lex(parser);
21630 pm_parser_err_token(parser, &token, PM_ERR_EXPRESSION_NOT_WRITABLE);
21631 return node;
21632 }
21633 }
21634 case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL: {
21635 switch (PM_NODE_TYPE(node)) {
21636 case PM_BACK_REFERENCE_READ_NODE:
21637 case PM_NUMBERED_REFERENCE_READ_NODE:
21638 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21640 case PM_GLOBAL_VARIABLE_READ_NODE: {
21641 parser_lex(parser);
21642
21643 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21644 pm_node_t *result = UP(pm_global_variable_and_write_node_create(parser, node, &token, value));
21645
21646 return result;
21647 }
21648 case PM_CLASS_VARIABLE_READ_NODE: {
21649 parser_lex(parser);
21650
21651 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21652 pm_node_t *result = UP(pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21653
21654 return result;
21655 }
21656 case PM_CONSTANT_PATH_NODE: {
21657 parser_lex(parser);
21658
21659 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21660 pm_node_t *write = UP(pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21661
21662 return parse_shareable_constant_write(parser, write);
21663 }
21664 case PM_CONSTANT_READ_NODE: {
21665 parser_lex(parser);
21666
21667 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21668 pm_node_t *write = UP(pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21669
21670 if (context_def_p(parser)) {
21671 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21672 }
21673
21674 return parse_shareable_constant_write(parser, write);
21675 }
21676 case PM_INSTANCE_VARIABLE_READ_NODE: {
21677 parser_lex(parser);
21678
21679 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21680 pm_node_t *result = UP(pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21681
21682 return result;
21683 }
21684 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21685 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21686 parser_lex(parser);
21687
21688 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21689 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0));
21690
21691 pm_node_unreference(parser, node);
21692 return result;
21693 }
21694 case PM_LOCAL_VARIABLE_READ_NODE: {
21695 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21696 PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + node->location.start);
21697 pm_node_unreference(parser, node);
21698 }
21699
21701 parser_lex(parser);
21702
21703 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21704 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21705
21706 return result;
21707 }
21708 case PM_CALL_NODE: {
21709 pm_call_node_t *cast = (pm_call_node_t *) node;
21710
21711 // If we have a vcall (a method with no arguments and no
21712 // receiver that could have been a local variable) then we
21713 // will transform it into a local variable write.
21714 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21715 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
21716 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
21717 parser_lex(parser);
21718
21719 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21720 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21721
21722 return result;
21723 }
21724
21725 // Move past the token here so that we have already added
21726 // the local variable by this point.
21727 parser_lex(parser);
21728
21729 // If there is no call operator and the message is "[]" then
21730 // this is an aref expression, and we can transform it into
21731 // an aset expression.
21732 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
21733 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21734 return UP(pm_index_and_write_node_create(parser, cast, &token, value));
21735 }
21736
21737 // If this node cannot be writable, then we have an error.
21738 if (pm_call_node_writable_p(parser, cast)) {
21739 parse_write_name(parser, &cast->name);
21740 } else {
21741 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
21742 }
21743
21744 parse_call_operator_write(parser, cast, &token);
21745 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21746 return UP(pm_call_and_write_node_create(parser, cast, &token, value));
21747 }
21748 case PM_MULTI_WRITE_NODE: {
21749 parser_lex(parser);
21750 pm_parser_err_token(parser, &token, PM_ERR_AMPAMPEQ_MULTI_ASSIGN);
21751 return node;
21752 }
21753 default:
21754 parser_lex(parser);
21755
21756 // In this case we have an &&= sign, but we don't know what it's for.
21757 // We need to treat it as an error. For now, we'll mark it as an error
21758 // and just skip right past it.
21759 pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
21760 return node;
21761 }
21762 }
21763 case PM_TOKEN_PIPE_PIPE_EQUAL: {
21764 switch (PM_NODE_TYPE(node)) {
21765 case PM_BACK_REFERENCE_READ_NODE:
21766 case PM_NUMBERED_REFERENCE_READ_NODE:
21767 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21769 case PM_GLOBAL_VARIABLE_READ_NODE: {
21770 parser_lex(parser);
21771
21772 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21773 pm_node_t *result = UP(pm_global_variable_or_write_node_create(parser, node, &token, value));
21774
21775 return result;
21776 }
21777 case PM_CLASS_VARIABLE_READ_NODE: {
21778 parser_lex(parser);
21779
21780 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21781 pm_node_t *result = UP(pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21782
21783 return result;
21784 }
21785 case PM_CONSTANT_PATH_NODE: {
21786 parser_lex(parser);
21787
21788 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21789 pm_node_t *write = UP(pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21790
21791 return parse_shareable_constant_write(parser, write);
21792 }
21793 case PM_CONSTANT_READ_NODE: {
21794 parser_lex(parser);
21795
21796 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21797 pm_node_t *write = UP(pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21798
21799 if (context_def_p(parser)) {
21800 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21801 }
21802
21803 return parse_shareable_constant_write(parser, write);
21804 }
21805 case PM_INSTANCE_VARIABLE_READ_NODE: {
21806 parser_lex(parser);
21807
21808 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21809 pm_node_t *result = UP(pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21810
21811 return result;
21812 }
21813 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21814 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21815 parser_lex(parser);
21816
21817 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21818 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0));
21819
21820 pm_node_unreference(parser, node);
21821 return result;
21822 }
21823 case PM_LOCAL_VARIABLE_READ_NODE: {
21824 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21825 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
21826 pm_node_unreference(parser, node);
21827 }
21828
21830 parser_lex(parser);
21831
21832 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21833 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21834
21835 return result;
21836 }
21837 case PM_CALL_NODE: {
21838 pm_call_node_t *cast = (pm_call_node_t *) node;
21839
21840 // If we have a vcall (a method with no arguments and no
21841 // receiver that could have been a local variable) then we
21842 // will transform it into a local variable write.
21843 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21844 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
21845 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
21846 parser_lex(parser);
21847
21848 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21849 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21850
21851 return result;
21852 }
21853
21854 // Move past the token here so that we have already added
21855 // the local variable by this point.
21856 parser_lex(parser);
21857
21858 // If there is no call operator and the message is "[]" then
21859 // this is an aref expression, and we can transform it into
21860 // an aset expression.
21861 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
21862 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21863 return UP(pm_index_or_write_node_create(parser, cast, &token, value));
21864 }
21865
21866 // If this node cannot be writable, then we have an error.
21867 if (pm_call_node_writable_p(parser, cast)) {
21868 parse_write_name(parser, &cast->name);
21869 } else {
21870 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
21871 }
21872
21873 parse_call_operator_write(parser, cast, &token);
21874 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21875 return UP(pm_call_or_write_node_create(parser, cast, &token, value));
21876 }
21877 case PM_MULTI_WRITE_NODE: {
21878 parser_lex(parser);
21879 pm_parser_err_token(parser, &token, PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN);
21880 return node;
21881 }
21882 default:
21883 parser_lex(parser);
21884
21885 // In this case we have an ||= sign, but we don't know what it's for.
21886 // We need to treat it as an error. For now, we'll mark it as an error
21887 // and just skip right past it.
21888 pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
21889 return node;
21890 }
21891 }
21892 case PM_TOKEN_AMPERSAND_EQUAL:
21893 case PM_TOKEN_CARET_EQUAL:
21894 case PM_TOKEN_GREATER_GREATER_EQUAL:
21895 case PM_TOKEN_LESS_LESS_EQUAL:
21896 case PM_TOKEN_MINUS_EQUAL:
21897 case PM_TOKEN_PERCENT_EQUAL:
21898 case PM_TOKEN_PIPE_EQUAL:
21899 case PM_TOKEN_PLUS_EQUAL:
21900 case PM_TOKEN_SLASH_EQUAL:
21901 case PM_TOKEN_STAR_EQUAL:
21902 case PM_TOKEN_STAR_STAR_EQUAL: {
21903 switch (PM_NODE_TYPE(node)) {
21904 case PM_BACK_REFERENCE_READ_NODE:
21905 case PM_NUMBERED_REFERENCE_READ_NODE:
21906 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21908 case PM_GLOBAL_VARIABLE_READ_NODE: {
21909 parser_lex(parser);
21910
21911 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21912 pm_node_t *result = UP(pm_global_variable_operator_write_node_create(parser, node, &token, value));
21913
21914 return result;
21915 }
21916 case PM_CLASS_VARIABLE_READ_NODE: {
21917 parser_lex(parser);
21918
21919 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21920 pm_node_t *result = UP(pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21921
21922 return result;
21923 }
21924 case PM_CONSTANT_PATH_NODE: {
21925 parser_lex(parser);
21926
21927 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21928 pm_node_t *write = UP(pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21929
21930 return parse_shareable_constant_write(parser, write);
21931 }
21932 case PM_CONSTANT_READ_NODE: {
21933 parser_lex(parser);
21934
21935 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21936 pm_node_t *write = UP(pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21937
21938 if (context_def_p(parser)) {
21939 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21940 }
21941
21942 return parse_shareable_constant_write(parser, write);
21943 }
21944 case PM_INSTANCE_VARIABLE_READ_NODE: {
21945 parser_lex(parser);
21946
21947 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21948 pm_node_t *result = UP(pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21949
21950 return result;
21951 }
21952 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21953 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21954 parser_lex(parser);
21955
21956 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21957 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0));
21958
21959 pm_node_unreference(parser, node);
21960 return result;
21961 }
21962 case PM_LOCAL_VARIABLE_READ_NODE: {
21963 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21964 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
21965 pm_node_unreference(parser, node);
21966 }
21967
21969 parser_lex(parser);
21970
21971 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21972 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21973
21974 return result;
21975 }
21976 case PM_CALL_NODE: {
21977 parser_lex(parser);
21978 pm_call_node_t *cast = (pm_call_node_t *) node;
21979
21980 // If we have a vcall (a method with no arguments and no
21981 // receiver that could have been a local variable) then we
21982 // will transform it into a local variable write.
21983 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21984 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
21985 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
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 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21988
21989 return result;
21990 }
21991
21992 // If there is no call operator and the message is "[]" then
21993 // this is an aref expression, and we can transform it into
21994 // an aset expression.
21995 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
21996 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21997 return UP(pm_index_operator_write_node_create(parser, cast, &token, value));
21998 }
21999
22000 // If this node cannot be writable, then we have an error.
22001 if (pm_call_node_writable_p(parser, cast)) {
22002 parse_write_name(parser, &cast->name);
22003 } else {
22004 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
22005 }
22006
22007 parse_call_operator_write(parser, cast, &token);
22008 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22009 return UP(pm_call_operator_write_node_create(parser, cast, &token, value));
22010 }
22011 case PM_MULTI_WRITE_NODE: {
22012 parser_lex(parser);
22013 pm_parser_err_token(parser, &token, PM_ERR_OPERATOR_MULTI_ASSIGN);
22014 return node;
22015 }
22016 default:
22017 parser_lex(parser);
22018
22019 // In this case we have an operator but we don't know what it's for.
22020 // We need to treat it as an error. For now, we'll mark it as an error
22021 // and just skip right past it.
22022 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_str(parser->current.type));
22023 return node;
22024 }
22025 }
22026 case PM_TOKEN_AMPERSAND_AMPERSAND:
22027 case PM_TOKEN_KEYWORD_AND: {
22028 parser_lex(parser);
22029
22030 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));
22031 return UP(pm_and_node_create(parser, node, &token, right));
22032 }
22033 case PM_TOKEN_KEYWORD_OR:
22034 case PM_TOKEN_PIPE_PIPE: {
22035 parser_lex(parser);
22036
22037 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));
22038 return UP(pm_or_node_create(parser, node, &token, right));
22039 }
22040 case PM_TOKEN_EQUAL_TILDE: {
22041 // Note that we _must_ parse the value before adding the local
22042 // variables in order to properly mirror the behavior of Ruby. For
22043 // example,
22044 //
22045 // /(?<foo>bar)/ =~ foo
22046 //
22047 // In this case, `foo` should be a method call and not a local yet.
22048 parser_lex(parser);
22049 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));
22050
22051 // By default, we're going to create a call node and then return it.
22052 pm_call_node_t *call = pm_call_node_binary_create(parser, node, &token, argument, 0);
22053 pm_node_t *result = UP(call);
22054
22055 // If the receiver of this =~ is a regular expression node, then we
22056 // need to introduce local variables for it based on its named
22057 // capture groups.
22058 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE)) {
22059 // It's possible to have an interpolated regular expression node
22060 // that only contains strings. This is because it can be split
22061 // up by a heredoc. In this case we need to concat the unescaped
22062 // strings together and then parse them as a regular expression.
22064
22065 bool interpolated = false;
22066 size_t total_length = 0;
22067
22068 pm_node_t *part;
22069 PM_NODE_LIST_FOREACH(parts, index, part) {
22070 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
22071 total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped);
22072 } else {
22073 interpolated = true;
22074 break;
22075 }
22076 }
22077
22078 if (!interpolated && total_length > 0) {
22079 void *memory = xmalloc(total_length);
22080 if (!memory) abort();
22081
22082 uint8_t *cursor = memory;
22083 PM_NODE_LIST_FOREACH(parts, index, part) {
22084 pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
22085 size_t length = pm_string_length(unescaped);
22086
22087 memcpy(cursor, pm_string_source(unescaped), length);
22088 cursor += length;
22089 }
22090
22091 pm_string_t owned;
22092 pm_string_owned_init(&owned, (uint8_t *) memory, total_length);
22093
22094 result = parse_interpolated_regular_expression_named_captures(parser, &owned, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
22095 pm_string_cleanup(&owned);
22096 }
22097 } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
22098 // If we have a regular expression node, then we can parse
22099 // the named captures and validate encoding in one pass.
22101
22102 pm_regexp_name_data_t name_data = {
22103 .call = call,
22104 .match = NULL,
22105 .names = { 0 },
22106 };
22107
22108 pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data));
22109
22110 if (name_data.match != NULL) {
22111 result = UP(name_data.match);
22112 }
22113 }
22114
22115 return result;
22116 }
22117 case PM_TOKEN_UAMPERSAND:
22118 case PM_TOKEN_USTAR:
22119 case PM_TOKEN_USTAR_STAR:
22120 // The only times this will occur are when we are in an error state,
22121 // but we'll put them in here so that errors can propagate.
22122 case PM_TOKEN_BANG_EQUAL:
22123 case PM_TOKEN_BANG_TILDE:
22124 case PM_TOKEN_EQUAL_EQUAL:
22125 case PM_TOKEN_EQUAL_EQUAL_EQUAL:
22126 case PM_TOKEN_LESS_EQUAL_GREATER:
22127 case PM_TOKEN_CARET:
22128 case PM_TOKEN_PIPE:
22129 case PM_TOKEN_AMPERSAND:
22130 case PM_TOKEN_GREATER_GREATER:
22131 case PM_TOKEN_LESS_LESS:
22132 case PM_TOKEN_MINUS:
22133 case PM_TOKEN_PLUS:
22134 case PM_TOKEN_PERCENT:
22135 case PM_TOKEN_SLASH:
22136 case PM_TOKEN_STAR:
22137 case PM_TOKEN_STAR_STAR: {
22138 parser_lex(parser);
22139 pm_token_t operator = parser->previous;
22140 switch (PM_NODE_TYPE(node)) {
22141 case PM_RESCUE_MODIFIER_NODE: {
22143 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22144 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22145 }
22146 break;
22147 }
22148 case PM_AND_NODE: {
22149 pm_and_node_t *cast = (pm_and_node_t *) node;
22150 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22151 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22152 }
22153 break;
22154 }
22155 case PM_OR_NODE: {
22156 pm_or_node_t *cast = (pm_or_node_t *) node;
22157 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22158 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22159 }
22160 break;
22161 }
22162 default:
22163 break;
22164 }
22165
22166 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));
22167 return UP(pm_call_node_binary_create(parser, node, &token, argument, 0));
22168 }
22169 case PM_TOKEN_GREATER:
22170 case PM_TOKEN_GREATER_EQUAL:
22171 case PM_TOKEN_LESS:
22172 case PM_TOKEN_LESS_EQUAL: {
22173 if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) {
22174 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON);
22175 }
22176
22177 parser_lex(parser);
22178 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));
22179 return UP(pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON));
22180 }
22181 case PM_TOKEN_AMPERSAND_DOT:
22182 case PM_TOKEN_DOT: {
22183 parser_lex(parser);
22184 pm_token_t operator = parser->previous;
22185 pm_arguments_t arguments = { 0 };
22186
22187 // This if statement handles the foo.() syntax.
22188 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
22189 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
22190 return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments));
22191 }
22192
22193 switch (PM_NODE_TYPE(node)) {
22194 case PM_RESCUE_MODIFIER_NODE: {
22196 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22197 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22198 }
22199 break;
22200 }
22201 case PM_AND_NODE: {
22202 pm_and_node_t *cast = (pm_and_node_t *) node;
22203 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22204 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22205 }
22206 break;
22207 }
22208 case PM_OR_NODE: {
22209 pm_or_node_t *cast = (pm_or_node_t *) node;
22210 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22211 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22212 }
22213 break;
22214 }
22215 default:
22216 break;
22217 }
22218
22219 pm_token_t message;
22220
22221 switch (parser->current.type) {
22222 case PM_CASE_OPERATOR:
22223 case PM_CASE_KEYWORD:
22224 case PM_TOKEN_CONSTANT:
22225 case PM_TOKEN_IDENTIFIER:
22226 case PM_TOKEN_METHOD_NAME: {
22227 parser_lex(parser);
22228 message = parser->previous;
22229 break;
22230 }
22231 default: {
22232 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_str(parser->current.type));
22233 message = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
22234 }
22235 }
22236
22237 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22238 pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments);
22239
22240 if (
22241 (previous_binding_power == PM_BINDING_POWER_STATEMENT) &&
22242 arguments.arguments == NULL &&
22243 arguments.opening_loc.length == 0 &&
22244 match1(parser, PM_TOKEN_COMMA)
22245 ) {
22246 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22247 } else {
22248 return UP(call);
22249 }
22250 }
22251 case PM_TOKEN_DOT_DOT:
22252 case PM_TOKEN_DOT_DOT_DOT: {
22253 parser_lex(parser);
22254
22255 pm_node_t *right = NULL;
22256 if (token_begins_expression_p(parser->current.type)) {
22257 right = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22258 }
22259
22260 return UP(pm_range_node_create(parser, node, &token, right));
22261 }
22262 case PM_TOKEN_KEYWORD_IF_MODIFIER: {
22263 pm_token_t keyword = parser->current;
22264 parser_lex(parser);
22265
22266 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));
22267 return UP(pm_if_node_modifier_create(parser, node, &keyword, predicate));
22268 }
22269 case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: {
22270 pm_token_t keyword = parser->current;
22271 parser_lex(parser);
22272
22273 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));
22274 return UP(pm_unless_node_modifier_create(parser, node, &keyword, predicate));
22275 }
22276 case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: {
22277 parser_lex(parser);
22278 pm_statements_node_t *statements = pm_statements_node_create(parser);
22279 pm_statements_node_body_append(parser, statements, node, true);
22280
22281 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));
22282 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));
22283 }
22284 case PM_TOKEN_KEYWORD_WHILE_MODIFIER: {
22285 parser_lex(parser);
22286 pm_statements_node_t *statements = pm_statements_node_create(parser);
22287 pm_statements_node_body_append(parser, statements, node, true);
22288
22289 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));
22290 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));
22291 }
22292 case PM_TOKEN_QUESTION_MARK: {
22293 context_push(parser, PM_CONTEXT_TERNARY);
22294 pm_node_list_t current_block_exits = { 0 };
22295 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
22296
22297 pm_token_t qmark = parser->current;
22298 parser_lex(parser);
22299
22300 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));
22301
22302 if (parser->recovering) {
22303 // If parsing the true expression of this ternary resulted in a syntax
22304 // error that we can recover from, then we're going to put missing nodes
22305 // and tokens into the remaining places. We want to be sure to do this
22306 // before the `expect` function call to make sure it doesn't
22307 // accidentally move past a ':' token that occurs after the syntax
22308 // error.
22309 pm_token_t colon = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
22310 pm_node_t *false_expression = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &colon), PM_TOKEN_LENGTH(&colon)));
22311
22312 context_pop(parser);
22313 pop_block_exits(parser, previous_block_exits);
22314 return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression));
22315 }
22316
22317 accept1(parser, PM_TOKEN_NEWLINE);
22318 expect1(parser, PM_TOKEN_COLON, PM_ERR_TERNARY_COLON);
22319
22320 pm_token_t colon = parser->previous;
22321 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));
22322
22323 context_pop(parser);
22324 pop_block_exits(parser, previous_block_exits);
22325 return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression));
22326 }
22327 case PM_TOKEN_COLON_COLON: {
22328 parser_lex(parser);
22329 pm_token_t delimiter = parser->previous;
22330
22331 switch (parser->current.type) {
22332 case PM_TOKEN_CONSTANT: {
22333 parser_lex(parser);
22334 pm_node_t *path;
22335
22336 if (
22337 (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
22338 ((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)))
22339 ) {
22340 // If we have a constant immediately following a '::' operator, then
22341 // this can either be a constant path or a method call, depending on
22342 // what follows the constant.
22343 //
22344 // If we have parentheses, then this is a method call. That would
22345 // look like Foo::Bar().
22346 pm_token_t message = parser->previous;
22347 pm_arguments_t arguments = { 0 };
22348
22349 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22350 path = UP(pm_call_node_call_create(parser, node, &delimiter, &message, &arguments));
22351 } else {
22352 // Otherwise, this is a constant path. That would look like Foo::Bar.
22353 path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
22354 }
22355
22356 // If this is followed by a comma then it is a multiple assignment.
22357 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22358 return parse_targets_validate(parser, path, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22359 }
22360
22361 return path;
22362 }
22363 case PM_CASE_OPERATOR:
22364 case PM_CASE_KEYWORD:
22365 case PM_TOKEN_IDENTIFIER:
22366 case PM_TOKEN_METHOD_NAME: {
22367 parser_lex(parser);
22368 pm_token_t message = parser->previous;
22369
22370 // If we have an identifier following a '::' operator, then it is for
22371 // sure a method call.
22372 pm_arguments_t arguments = { 0 };
22373 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22374 pm_call_node_t *call = pm_call_node_call_create(parser, node, &delimiter, &message, &arguments);
22375
22376 // If this is followed by a comma then it is a multiple assignment.
22377 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22378 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22379 }
22380
22381 return UP(call);
22382 }
22383 case PM_TOKEN_PARENTHESIS_LEFT: {
22384 // If we have a parenthesis following a '::' operator, then it is the
22385 // method call shorthand. That would look like Foo::(bar).
22386 pm_arguments_t arguments = { 0 };
22387 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
22388
22389 return UP(pm_call_node_shorthand_create(parser, node, &delimiter, &arguments));
22390 }
22391 default: {
22392 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
22393 return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
22394 }
22395 }
22396 }
22397 case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: {
22398 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
22399 parser_lex(parser);
22400 accept1(parser, PM_TOKEN_NEWLINE);
22401
22402 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));
22403 context_pop(parser);
22404
22405 return UP(pm_rescue_modifier_node_create(parser, node, &token, value));
22406 }
22407 case PM_TOKEN_BRACKET_LEFT: {
22408 parser_lex(parser);
22409
22410 pm_arguments_t arguments = { 0 };
22411 arguments.opening_loc = TOK2LOC(parser, &parser->previous);
22412
22413 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
22414 parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
22415 expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET);
22416 }
22417
22418 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
22419
22420 // If we have a comma after the closing bracket then this is a multiple
22421 // assignment and we should parse the targets.
22422 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22423 pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments);
22424 return parse_targets_validate(parser, UP(aref), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22425 }
22426
22427 // If we're at the end of the arguments, we can now check if there is a
22428 // block node that starts with a {. If there is, then we can parse it and
22429 // add it to the arguments.
22430 pm_block_node_t *block = NULL;
22431 if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
22432 block = parse_block(parser, (uint16_t) (depth + 1));
22433 pm_arguments_validate_block(parser, &arguments, block);
22434 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
22435 block = parse_block(parser, (uint16_t) (depth + 1));
22436 }
22437
22438 if (block != NULL) {
22439 if (arguments.block != NULL) {
22440 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_AFTER_BLOCK);
22441 if (arguments.arguments == NULL) {
22442 arguments.arguments = pm_arguments_node_create(parser);
22443 }
22444 pm_arguments_node_arguments_append(parser->arena, arguments.arguments, arguments.block);
22445 }
22446
22447 arguments.block = UP(block);
22448 }
22449
22450 return UP(pm_call_node_aref_create(parser, node, &arguments));
22451 }
22452 case PM_TOKEN_KEYWORD_IN: {
22453 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22454 parser->pattern_matching_newlines = true;
22455
22456 pm_token_t operator = parser->current;
22457 parser->command_start = false;
22458 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22459 parser_lex(parser);
22460
22461 pm_constant_id_list_t captures = { 0 };
22462 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));
22463
22464 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22465
22466 return UP(pm_match_predicate_node_create(parser, node, pattern, &operator));
22467 }
22468 case PM_TOKEN_EQUAL_GREATER: {
22469 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22470 parser->pattern_matching_newlines = true;
22471
22472 pm_token_t operator = parser->current;
22473 parser->command_start = false;
22474 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22475 parser_lex(parser);
22476
22477 pm_constant_id_list_t captures = { 0 };
22478 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));
22479
22480 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22481
22482 return UP(pm_match_required_node_create(parser, node, pattern, &operator));
22483 }
22484 default:
22485 assert(false && "unreachable");
22486 return NULL;
22487 }
22488}
22489
22490#undef PM_PARSE_PATTERN_SINGLE
22491#undef PM_PARSE_PATTERN_TOP
22492#undef PM_PARSE_PATTERN_MULTI
22493
22506static bool
22507parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) {
22508 pm_binding_power_t left = pm_binding_powers[parser->current.type].left;
22509
22510 switch (PM_NODE_TYPE(node)) {
22511 case PM_MULTI_WRITE_NODE:
22512 case PM_RETURN_NODE:
22513 case PM_BREAK_NODE:
22514 case PM_NEXT_NODE:
22515 return left > PM_BINDING_POWER_MODIFIER;
22516 case PM_CLASS_VARIABLE_WRITE_NODE:
22517 case PM_CONSTANT_PATH_WRITE_NODE:
22518 case PM_CONSTANT_WRITE_NODE:
22519 case PM_GLOBAL_VARIABLE_WRITE_NODE:
22520 case PM_INSTANCE_VARIABLE_WRITE_NODE:
22521 case PM_LOCAL_VARIABLE_WRITE_NODE:
22522 return PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && left > PM_BINDING_POWER_MODIFIER;
22523 case PM_CALL_NODE: {
22524 // Calls with an implicit array on the right-hand side are
22525 // statements and can only be followed by modifiers.
22526 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)) {
22527 return left > PM_BINDING_POWER_MODIFIER;
22528 }
22529
22530 // Command-style calls (including block commands like
22531 // `foo bar do end`) can only be followed by composition
22532 // (and/or) and modifier (if/unless/etc.) operators.
22533 if (pm_command_call_value_p(parser, node)) {
22534 return left > PM_BINDING_POWER_COMPOSITION;
22535 }
22536
22537 // A block call (command with do-block, or any call chained
22538 // from one) can only be followed by call chaining (., ::,
22539 // &.), composition (and/or), and modifier operators.
22540 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22541 }
22542 case PM_SUPER_NODE:
22543 case PM_YIELD_NODE:
22544 // Command-style super/yield (without parens) can only be followed
22545 // by composition and modifier operators.
22546 if (pm_command_call_value_p(parser, node)) {
22547 return left > PM_BINDING_POWER_COMPOSITION;
22548 }
22549
22550 /* A super carrying a do-block is a block call, so it may also be
22551 * followed by call chaining (`.`, `::`, `&.`). */
22552 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22553 case PM_DEF_NODE:
22554 // An endless method whose body is a command-style call (e.g.,
22555 // `def f = foo bar`) is a command assignment and can only be
22556 // followed by modifiers.
22557 return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(parser, node);
22558 case PM_RESCUE_MODIFIER_NODE:
22559 // A rescue modifier whose handler is a pattern match (=> or in)
22560 // produces a statement and cannot be followed by operators above
22561 // the modifier level.
22562 if (left > PM_BINDING_POWER_MODIFIER) {
22564 pm_node_t *rescue_expression = cast->rescue_expression;
22565 return PM_NODE_TYPE_P(rescue_expression, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(rescue_expression, PM_MATCH_PREDICATE_NODE);
22566 }
22567 return false;
22568 default:
22569 return false;
22570 }
22571}
22572
22581static pm_node_t *
22582parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
22583 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
22584 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
22585 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
22586 }
22587
22588 pm_node_t *node = parse_expression_prefix(parser, binding_power, flags, diag_id, depth);
22589
22590 // Some prefix nodes are statements and can only be followed by modifiers
22591 // (if/unless/while/until/rescue) or nothing at all. We check these cheaply
22592 // here before entering the infix loop.
22593 switch (PM_NODE_TYPE(node)) {
22594 case PM_ERROR_RECOVERY_NODE:
22595 return node;
22596 case PM_PRE_EXECUTION_NODE:
22597 return node;
22598 case PM_POST_EXECUTION_NODE:
22599 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
22600 case PM_ALIAS_METHOD_NODE:
22601 case PM_UNDEF_NODE:
22602 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
22603 return node;
22604 }
22605 break;
22606 case PM_CALL_NODE:
22607 case PM_SUPER_NODE:
22608 case PM_YIELD_NODE:
22609 case PM_DEF_NODE:
22610 if (parse_expression_terminator(parser, node)) {
22611 return node;
22612 }
22613 break;
22614 case PM_SYMBOL_NODE:
22615 if (pm_symbol_node_label_p(parser, node)) {
22616 return node;
22617 }
22618 break;
22619 default:
22620 break;
22621 }
22622
22623 // Look and see if the next token can be parsed as an infix operator. If it
22624 // can, then we'll parse it using parse_expression_infix.
22625 pm_binding_powers_t current_binding_powers;
22626 pm_token_type_t current_token_type;
22627
22628 while (
22629 current_token_type = parser->current.type,
22630 current_binding_powers = pm_binding_powers[current_token_type],
22631 binding_power <= current_binding_powers.left &&
22632 current_binding_powers.binary
22633 ) {
22634 node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, flags, (uint16_t) (depth + 1));
22635 if (parse_expression_terminator(parser, node)) return node;
22636
22637 // If the operator is nonassoc and we should not be able to parse the
22638 // upcoming infix operator, break.
22639 if (current_binding_powers.nonassoc) {
22640 // If we are about to parse another non-associative operator at the
22641 // same precedence as the one we just parsed, then we need to add an
22642 // error. This covers chaining the same operator (`1 == 2 == 3`) as
22643 // well as different operators that share a precedence, since they
22644 // are equally non-associative with one another (`1 == 2 != 3`,
22645 // `1...2..3`).
22646 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22647 if (next_binding_powers.nonassoc && next_binding_powers.left == current_binding_powers.left) {
22648 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));
22649 break;
22650 }
22651
22652 // If this is an endless range, then we need to reject a couple of
22653 // additional operators because it violates the normal operator
22654 // precedence rules. Those patterns are:
22655 //
22656 // 1.. & 2
22657 // 1.. * 2
22658 //
22659 if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) {
22660 if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) {
22661 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));
22662 break;
22663 }
22664
22665 if (PM_BINDING_POWER_TERM <= next_binding_powers.left) {
22666 break;
22667 }
22668 } else if (current_binding_powers.left <= next_binding_powers.left) {
22669 break;
22670 }
22671 }
22672
22673 if (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) {
22674 // A command-style method call is only accepted on method chains.
22675 // Thus, we check whether the parsed node can continue method chains.
22676 // The method chain can continue if the parsed node is one of the following five kinds:
22677 // (1) index access: foo[1]
22678 // (2) attribute access: foo.bar
22679 // (3) method call with parenthesis: foo.bar(1)
22680 // (4) method call with a block: foo.bar do end
22681 // (5) constant path: foo::Bar
22682 switch (node->type) {
22683 case PM_CALL_NODE: {
22684 pm_call_node_t *cast = (pm_call_node_t *)node;
22685 if (
22686 // (1) foo[1]
22687 !(
22688 cast->call_operator_loc.length == 0 &&
22689 cast->message_loc.length > 0 &&
22690 parser->start[cast->message_loc.start] == '[' &&
22691 parser->start[cast->message_loc.start + cast->message_loc.length - 1] == ']'
22692 ) &&
22693 // (2) foo.bar
22694 !(
22695 cast->call_operator_loc.length > 0 &&
22696 cast->arguments == NULL &&
22697 cast->block == NULL &&
22698 cast->opening_loc.length == 0
22699 ) &&
22700 // (3) foo.bar(1)
22701 !(
22702 cast->call_operator_loc.length > 0 &&
22703 cast->opening_loc.length > 0
22704 ) &&
22705 // (4) foo.bar do end
22706 !(
22707 cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
22708 )
22709 ) {
22710 flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
22711 }
22712 break;
22713 }
22714 // (5) foo::Bar
22715 case PM_CONSTANT_PATH_NODE:
22716 break;
22717 default:
22718 flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
22719 break;
22720 }
22721 }
22722
22723 if (context_terminator(parser->current_context->context, &parser->current)) {
22724 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22725 if (
22726 !next_binding_powers.binary ||
22727 binding_power > next_binding_powers.left ||
22728 (PM_NODE_TYPE_P(node, PM_CALL_NODE) && pm_call_node_command_p((pm_call_node_t *) node))
22729 ) {
22730 return node;
22731 }
22732 }
22733 }
22734
22735 return node;
22736}
22737
22742static pm_statements_node_t *
22743wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
22744 if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) {
22745 if (statements == NULL) {
22746 statements = pm_statements_node_create(parser);
22747 }
22748
22749 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22750 pm_arguments_node_arguments_append(
22751 parser->arena,
22752 arguments,
22753 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)))
22754 );
22755
22756 pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create(
22757 parser,
22758 arguments,
22759 pm_parser_constant_id_constant(parser, "print", 5)
22760 )), true);
22761 }
22762
22763 if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) {
22764 if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) {
22765 if (statements == NULL) {
22766 statements = pm_statements_node_create(parser);
22767 }
22768
22769 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22770 pm_arguments_node_arguments_append(
22771 parser->arena,
22772 arguments,
22773 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)))
22774 );
22775
22776 pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2));
22777 pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, UP(receiver), "split", arguments);
22778
22779 pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create(
22780 parser,
22781 pm_parser_constant_id_constant(parser, "$F", 2),
22782 UP(call)
22783 );
22784
22785 pm_statements_node_body_prepend(parser->arena, statements, UP(write));
22786 }
22787
22788 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22789 pm_arguments_node_arguments_append(
22790 parser->arena,
22791 arguments,
22792 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)))
22793 );
22794
22795 if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) {
22796 pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser);
22797 pm_keyword_hash_node_elements_append(parser->arena, keywords, UP(pm_assoc_node_create(
22798 parser,
22799 UP(pm_symbol_node_synthesized_create(parser, "chomp")),
22800 NULL,
22801 UP(pm_true_node_synthesized_create(parser))
22802 )));
22803
22804 pm_arguments_node_arguments_append(parser->arena, arguments, UP(keywords));
22805 pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS);
22806 }
22807
22808 pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
22809 pm_statements_node_body_append(parser, wrapped_statements, UP(pm_while_node_synthesized_create(
22810 parser,
22811 UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))),
22812 statements
22813 )), true);
22814
22815 statements = wrapped_statements;
22816 }
22817
22818 return statements;
22819}
22820
22824static pm_node_t *
22825parse_program(pm_parser_t *parser) {
22826 // If the current scope is NULL, then we want to push a new top level scope.
22827 // The current scope could exist in the event that we are parsing an eval
22828 // and the user has passed into scopes that already exist.
22829 if (parser->current_scope == NULL) {
22830 pm_parser_scope_push(parser, true);
22831 }
22832
22833 pm_node_list_t current_block_exits = { 0 };
22834 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
22835
22836 parser_lex(parser);
22837 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN, 0);
22838
22839 if (statements != NULL && !parser->parsing_eval) {
22840 // If we have statements, then the top-level statement should be
22841 // explicitly checked as well. We have to do this here because
22842 // everywhere else we check all but the last statement.
22843 assert(statements->body.size > 0);
22844 pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]);
22845 }
22846
22847 pm_constant_id_list_t locals;
22848 pm_locals_order(parser, &parser->current_scope->locals, &locals, true);
22849 pm_parser_scope_pop(parser);
22850
22851 // At the top level, see if we need to wrap the statements in a program
22852 // node with a while loop based on the options.
22853 if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
22854 statements = wrap_statements(parser, statements);
22855 } else {
22856 flush_block_exits(parser, previous_block_exits);
22857 }
22858
22859 // If this is an empty file, then we're still going to parse all of the
22860 // statements in order to gather up all of the comments and such. Here we'll
22861 // correct the location information.
22862 if (statements == NULL) {
22863 statements = pm_statements_node_create(parser);
22864 statements->base.location = (pm_location_t) { 0 };
22865 }
22866
22867 return UP(pm_program_node_create(parser, &locals, statements));
22868}
22869
22870/******************************************************************************/
22871/* External functions */
22872/******************************************************************************/
22873
22883static const char *
22884pm_strnstr(const char *big, const char *little, size_t big_length) {
22885 size_t little_length = strlen(little);
22886
22887 for (const char *max = big + big_length - little_length; big <= max; big++) {
22888 if (*big == *little && memcmp(big, little, little_length) == 0) return big;
22889 }
22890
22891 return NULL;
22892}
22893
22894#ifdef _WIN32
22895#define pm_parser_warn_shebang_carriage_return(parser, start, length) ((void) 0)
22896#else
22902static void
22903pm_parser_warn_shebang_carriage_return(pm_parser_t *parser, const uint8_t *start, size_t length) {
22904 if (length > 2 && start[length - 2] == '\r' && start[length - 1] == '\n') {
22905 pm_parser_warn(parser, U32(start - parser->start), U32(length), PM_WARN_SHEBANG_CARRIAGE_RETURN);
22906 }
22907}
22908#endif
22909
22914static void
22915pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const char *engine, size_t length) {
22916 const char *switches = pm_strnstr(engine, " -", length);
22917 if (switches == NULL) return;
22918
22919 pm_options_t next_options = *options;
22920 options->shebang_callback(
22921 &next_options,
22922 (const uint8_t *) (switches + 1),
22923 length - ((size_t) (switches - engine)) - 1,
22924 options->shebang_callback_data
22925 );
22926
22927 size_t encoding_length;
22928 if ((encoding_length = pm_string_length(&next_options.encoding)) > 0) {
22929 const uint8_t *encoding_source = pm_string_source(&next_options.encoding);
22930 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
22931 }
22932
22933 parser->command_line = next_options.command_line;
22934 parser->frozen_string_literal = next_options.frozen_string_literal;
22935}
22936
22940void
22941pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) {
22942 assert(arena != NULL);
22943 assert(source != NULL);
22944
22945 *parser = (pm_parser_t) {
22946 .arena = arena,
22947 .metadata_arena = { 0 },
22948 .node_id = 0,
22949 .lex_state = PM_LEX_STATE_BEG,
22950 .enclosure_nesting = 0,
22951 .lambda_enclosure_nesting = -1,
22952 .brace_nesting = 0,
22953 .do_loop_stack = 0,
22954 .accepts_block_stack = 0,
22955 .lex_modes = {
22956 .index = 0,
22957 .stack = {{ .mode = PM_LEX_DEFAULT }},
22958 .current = &parser->lex_modes.stack[0],
22959 },
22960 .start = source,
22961 .end = source + size,
22962 .previous = { .type = PM_TOKEN_EOF, .start = source, .end = source },
22963 .current = { .type = PM_TOKEN_EOF, .start = source, .end = source },
22964 .next_start = NULL,
22965 .heredoc_end = NULL,
22966 .data_loc = { 0 },
22967 .comment_list = { 0 },
22968 .magic_comment_list = { 0 },
22969 .warning_list = { 0 },
22970 .error_list = { 0 },
22971 .current_scope = NULL,
22972 .current_context = NULL,
22973 .encoding = PM_ENCODING_UTF_8_ENTRY,
22974 .encoding_changed_callback = NULL,
22975 .encoding_comment_start = source,
22976 .lex_callback = { 0 },
22977 .filepath = { 0 },
22978 .constant_pool = { 0 },
22979 .line_offsets = { 0 },
22980 .integer = { 0 },
22981 .current_string = PM_STRING_EMPTY,
22982 .start_line = 1,
22983 .explicit_encoding = NULL,
22984 .command_line = 0,
22985 .parsing_eval = false,
22986 .partial_script = false,
22987 .command_start = true,
22988 .recovering = false,
22989 .continuable = true,
22990 .encoding_locked = false,
22991 .encoding_changed = false,
22992 .pattern_matching_newlines = false,
22993 .in_keyword_arg = false,
22994 .current_block_exits = NULL,
22995 .semantic_token_seen = false,
22996 .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET,
22997 .warn_mismatched_indentation = true
22998 };
22999
23000 /* Pre-size the arenas based on input size to reduce the number of block
23001 * allocations (and the kernel page zeroing they trigger). The ratios were
23002 * measured empirically: AST arena ~3.3x input, metadata arena ~1.1x input.
23003 * The reserve call is a no-op when the capacity is at or below the default
23004 * arena block size, so small inputs don't waste an extra allocation. */
23005 if (size <= SIZE_MAX / 4) pm_arena_reserve(arena, size * 4);
23006 if (size <= SIZE_MAX / 5 * 4) pm_arena_reserve(&parser->metadata_arena, size + size / 4);
23007
23008 /* Initialize the constant pool. Measured across 1532 Ruby stdlib files, the
23009 * bytes/constant ratio has a median of ~56 and a 90th percentile of ~135.
23010 * We use 120 as a balance between over-allocation waste and resize
23011 * frequency. Resizes are cheap with arena allocation, so we lean toward
23012 * under-estimating. */
23013 uint32_t constant_size = ((uint32_t) size) / 120;
23014 pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size < 4 ? 4 : constant_size);
23015
23016 /* Initialize the line offset list. Similar to the constant pool, we are
23017 * going to estimate the number of newlines that we will need based on the
23018 * size of the input. */
23019 size_t newline_size = size / 22;
23020 pm_line_offset_list_init(&parser->metadata_arena, &parser->line_offsets, newline_size < 4 ? 4 : newline_size);
23021
23022 // If options were provided to this parse, establish them here.
23023 if (options != NULL) {
23024 // filepath option
23025 parser->filepath = options->filepath;
23026
23027 // line option
23028 parser->start_line = options->line;
23029
23030 // encoding option
23031 size_t encoding_length = pm_string_length(&options->encoding);
23032 if (encoding_length > 0) {
23033 const uint8_t *encoding_source = pm_string_source(&options->encoding);
23034 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
23035 }
23036
23037 // encoding_locked option
23038 parser->encoding_locked = options->encoding_locked;
23039
23040 // frozen_string_literal option
23041 parser->frozen_string_literal = options->frozen_string_literal;
23042
23043 // command_line option
23044 parser->command_line = options->command_line;
23045
23046 // version option
23047 parser->version = options->version;
23048
23049 // partial_script
23050 parser->partial_script = options->partial_script;
23051
23052 // scopes option
23053 parser->parsing_eval = options->scopes_count > 0;
23054 if (parser->parsing_eval) parser->warn_mismatched_indentation = false;
23055
23056 for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
23057 const pm_options_scope_t *scope = pm_options_scope(options, scope_index);
23058 pm_parser_scope_push(parser, scope_index == 0);
23059
23060 // Scopes given from the outside are not allowed to have numbered
23061 // parameters.
23062 parser->current_scope->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED;
23063
23064 for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
23065 const pm_string_t *local = pm_options_scope_local(scope, local_index);
23066
23067 const uint8_t *source = pm_string_source(local);
23068 size_t length = pm_string_length(local);
23069
23070 uint8_t *allocated = (uint8_t *) pm_arena_alloc(&parser->metadata_arena, length, 1);
23071 memcpy(allocated, source, length);
23072 pm_parser_local_add_owned(parser, allocated, length);
23073 }
23074 }
23075 }
23076
23077 // Now that we have established the user-provided options, check if
23078 // a version was given and parse as the latest version otherwise.
23079 if (parser->version == PM_OPTIONS_VERSION_UNSET) {
23080 parser->version = PM_OPTIONS_VERSION_LATEST;
23081 }
23082
23083 pm_accepts_block_stack_push(parser, true);
23084
23085 // Skip past the UTF-8 BOM if it exists.
23086 if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) {
23087 parser->current.end += 3;
23088 parser->encoding_comment_start += 3;
23089
23090 if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
23091 parser->encoding = PM_ENCODING_UTF_8_ENTRY;
23092 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
23093 }
23094 }
23095
23096 // If the -x command line flag is set, or the first shebang of the file does
23097 // not include "ruby", then we'll search for a shebang that does include
23098 // "ruby" and start parsing from there.
23099 bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
23100
23101 // If the first two bytes of the source are a shebang, then we will do a bit
23102 // of extra processing.
23103 //
23104 // First, we'll indicate that the encoding comment is at the end of the
23105 // shebang. This means that when a shebang is present the encoding comment
23106 // can begin on the second line.
23107 //
23108 // Second, we will check if the shebang includes "ruby". If it does, then we
23109 // we will start parsing from there. We will also potentially warning the
23110 // user if there is a carriage return at the end of the shebang. We will
23111 // also potentially call the shebang callback if this is the main script to
23112 // allow the caller to parse the shebang and find any command-line options.
23113 // If the shebang does not include "ruby" and this is the main script being
23114 // parsed, then we will start searching the file for a shebang that does
23115 // contain "ruby" as if -x were passed on the command line.
23116 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
23117 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end);
23118
23119 if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') {
23120 const char *engine;
23121
23122 if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) {
23123 if (newline != NULL) {
23124 parser->encoding_comment_start = newline + 1;
23125
23126 if (options == NULL || options->main_script) {
23127 pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1);
23128 }
23129 }
23130
23131 if (options != NULL && options->main_script && options->shebang_callback != NULL) {
23132 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start)));
23133 }
23134
23135 search_shebang = false;
23136 } else if (options != NULL && options->main_script && !parser->parsing_eval) {
23137 search_shebang = true;
23138 }
23139 }
23140
23141 // Here we're going to find the first shebang that includes "ruby" and start
23142 // parsing from there.
23143 if (search_shebang) {
23144 // If a shebang that includes "ruby" is not found, then we're going to a
23145 // a load error to the list of errors on the parser.
23146 bool found_shebang = false;
23147
23148 // This is going to point to the start of each line as we check it.
23149 // We'll maintain a moving window looking at each line at they come.
23150 const uint8_t *cursor = parser->start;
23151
23152 // The newline pointer points to the end of the current line that we're
23153 // considering. If it is NULL, then we're at the end of the file.
23154 const uint8_t *newline = next_newline(cursor, parser->end - cursor);
23155
23156 while (newline != NULL) {
23157 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
23158
23159 cursor = newline + 1;
23160 newline = next_newline(cursor, parser->end - cursor);
23161
23162 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - cursor);
23163 if (length > 2 && cursor[0] == '#' && cursor[1] == '!') {
23164 const char *engine;
23165 if ((engine = pm_strnstr((const char *) cursor, "ruby", length)) != NULL) {
23166 found_shebang = true;
23167
23168 if (newline != NULL) {
23169 pm_parser_warn_shebang_carriage_return(parser, cursor, length + 1);
23170 parser->encoding_comment_start = newline + 1;
23171 }
23172
23173 if (options != NULL && options->shebang_callback != NULL) {
23174 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) cursor)));
23175 }
23176
23177 break;
23178 }
23179 }
23180 }
23181
23182 if (found_shebang) {
23183 parser->previous = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23184 parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23185 } else {
23186 pm_parser_err(parser, 0, 0, PM_ERR_SCRIPT_NOT_FOUND);
23187 pm_line_offset_list_clear(&parser->line_offsets);
23188 }
23189 }
23190
23191 // The encoding comment can start after any amount of inline whitespace, so
23192 // here we'll advance it to the first non-inline-whitespace character so
23193 // that it is ready for future comparisons.
23194 parser->encoding_comment_start += pm_strspn_inline_whitespace(parser->encoding_comment_start, parser->end - parser->encoding_comment_start);
23195}
23196
23205pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) {
23206 pm_parser_t *parser = (pm_parser_t *) xmalloc(sizeof(pm_parser_t));
23207 if (parser == NULL) abort();
23208
23209 pm_parser_init(arena, parser, source, size, options);
23210 return parser;
23211}
23212
23216void
23217pm_parser_cleanup(pm_parser_t *parser) {
23218 pm_string_cleanup(&parser->filepath);
23219 pm_arena_cleanup(&parser->metadata_arena);
23220
23221 while (parser->current_scope != NULL) {
23222 // Normally, popping the scope doesn't free the locals since it is
23223 // assumed that ownership has transferred to the AST. However if we have
23224 // scopes while we're freeing the parser, it's likely they came from
23225 // eval scopes and we need to free them explicitly here.
23226 pm_parser_scope_pop(parser);
23227 }
23228
23229 while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
23230 lex_mode_pop(parser);
23231 }
23232}
23233
23237void
23239 pm_parser_cleanup(parser);
23240 xfree_sized(parser, sizeof(pm_parser_t));
23241}
23242
23248static bool
23249pm_parse_err_is_fatal(pm_diagnostic_id_t diag_id) {
23250 switch (diag_id) {
23251 case PM_ERR_ARRAY_EXPRESSION_AFTER_STAR:
23252 case PM_ERR_BEGIN_UPCASE_BRACE:
23253 case PM_ERR_CLASS_VARIABLE_BARE:
23254 case PM_ERR_END_UPCASE_BRACE:
23255 case PM_ERR_ESCAPE_INVALID_HEXADECIMAL:
23256 case PM_ERR_ESCAPE_INVALID_UNICODE_LIST:
23257 case PM_ERR_ESCAPE_INVALID_UNICODE_SHORT:
23258 case PM_ERR_EXPRESSION_NOT_WRITABLE:
23259 case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF:
23260 case PM_ERR_FLOAT_PARSE:
23261 case PM_ERR_GLOBAL_VARIABLE_BARE:
23262 case PM_ERR_HASH_KEY:
23263 case PM_ERR_HEREDOC_IDENTIFIER:
23264 case PM_ERR_INSTANCE_VARIABLE_BARE:
23265 case PM_ERR_INVALID_BLOCK_EXIT:
23266 case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT:
23267 case PM_ERR_INVALID_FLOAT_EXPONENT:
23268 case PM_ERR_INVALID_NUMBER_BINARY:
23269 case PM_ERR_INVALID_NUMBER_DECIMAL:
23270 case PM_ERR_INVALID_NUMBER_HEXADECIMAL:
23271 case PM_ERR_INVALID_NUMBER_OCTAL:
23272 case PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING:
23273 case PM_ERR_NO_LOCAL_VARIABLE:
23274 case PM_ERR_PARAMETER_ORDER:
23275 case PM_ERR_STATEMENT_UNDEF:
23276 case PM_ERR_VOID_EXPRESSION:
23277 return true;
23278 default:
23279 return false;
23280 }
23281}
23282
23316static void
23317pm_parse_continuable(pm_parser_t *parser) {
23318 // If there are no errors then there is nothing to continue.
23319 if (parser->error_list.size == 0) {
23320 parser->continuable = false;
23321 return;
23322 }
23323
23324 if (!parser->continuable) return;
23325
23326 size_t source_length = (size_t) (parser->end - parser->start);
23327
23328 // First pass: check if there are any non-stray, non-fatal errors.
23329 bool has_non_stray_error = false;
23330 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23331 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)) {
23332 has_non_stray_error = true;
23333 break;
23334 }
23335 }
23336
23337 // Second pass: check each error. We track the minimum source position
23338 // among non-stray, non-fatal errors seen so far in list order, which
23339 // lets us detect cascade stray tokens.
23340 size_t non_stray_min_start = SIZE_MAX;
23341
23342 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23343 size_t error_start = (size_t) error->location.start;
23344 size_t error_end = error_start + (size_t) error->location.length;
23345 bool at_eof = error_end >= source_length;
23346
23347 // Fatal errors are non-continuable unless they occur at EOF.
23348 if (pm_parse_err_is_fatal(error->diag_id) && !at_eof) {
23349 parser->continuable = false;
23350 return;
23351 }
23352
23353 // Track non-stray, non-fatal error positions in list order.
23354 if (error->diag_id != PM_ERR_UNEXPECTED_TOKEN_IGNORE &&
23355 error->diag_id != PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT) {
23356 if (error_start < non_stray_min_start) non_stray_min_start = error_start;
23357 continue;
23358 }
23359
23360 // This is a stray token. Determine if it is a cascade effect
23361 // of a preceding error or genuinely stray.
23362
23363 // Rule (a): a non-stray error was seen earlier in the list at a
23364 // strictly earlier position — this stray is a cascade effect.
23365 if (non_stray_min_start < error_start) continue;
23366
23367 // Rule (b): this stray is at EOF with valid code before it.
23368 // Single-byte stray tokens at EOF (like `\` for line continuation)
23369 // are likely truncated tokens. Multi-byte stray tokens (like the
23370 // keyword `end`) need additional evidence that they are cascade
23371 // effects (i.e. non-stray errors exist elsewhere).
23372 if (at_eof && error_start > 0) {
23373 // Exception: closing delimiters at EOF are genuinely stray.
23374 if (error->location.length == 1) {
23375 const uint8_t *byte = parser->start + error_start;
23376 if (*byte == ')' || *byte == ']' || *byte == '}') {
23377 parser->continuable = false;
23378 return;
23379 }
23380
23381 // Single-byte non-delimiter stray at EOF: cascade.
23382 continue;
23383 }
23384
23385 // Multi-byte stray at EOF: cascade only if there are
23386 // non-stray errors (evidence of a preceding parse failure).
23387 if (has_non_stray_error) continue;
23388 }
23389
23390 // Rule (c): a stray `=` at the start of a line could be the
23391 // beginning of an embedded document (`=begin`). The remaining
23392 // bytes after `=` parse as an identifier, so the error is not
23393 // at EOF, but the construct is genuinely incomplete.
23394 if (error->location.length == 1) {
23395 const uint8_t *byte = parser->start + error_start;
23396 if (*byte == '=' && (error_start == 0 || *(byte - 1) == '\n')) continue;
23397 }
23398
23399 // This stray token is genuinely non-continuable.
23400 parser->continuable = false;
23401 return;
23402 }
23403}
23404
23408pm_node_t *
23410 pm_node_t *node = parse_program(parser);
23411 pm_parse_continuable(parser);
23412 return node;
23413}
23414
23421pm_node_t *
23422pm_parse_stream(pm_parser_t **parser, pm_arena_t *arena, pm_source_t *source, const pm_options_t *options) {
23423 bool eof = pm_source_stream_read(source);
23424
23425 pm_parser_t *tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23426 pm_node_t *node = pm_parse(tmp);
23427
23428 while (!eof && tmp->error_list.size > 0) {
23429 eof = pm_source_stream_read(source);
23430
23431 pm_parser_free(tmp);
23432 pm_arena_cleanup(arena);
23433
23434 tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23435 node = pm_parse(tmp);
23436 }
23437
23438 *parser = tmp;
23439 return node;
23440}
23441
23442#undef PM_CASE_KEYWORD
23443#undef PM_CASE_OPERATOR
23444#undef PM_CASE_WRITABLE
23445#undef PM_STRING_EMPTY
23446
23447// We optionally support serializing to a binary string. For systems that don't
23448// want or need this functionality, it can be turned off with the
23449// PRISM_EXCLUDE_SERIALIZATION define.
23450#ifndef PRISM_EXCLUDE_SERIALIZATION
23451
23452static PRISM_INLINE void
23453pm_serialize_header(pm_buffer_t *buffer) {
23454 pm_buffer_append_string(buffer, "PRISM", 5);
23455 pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR);
23456 pm_buffer_append_byte(buffer, PRISM_VERSION_MINOR);
23457 pm_buffer_append_byte(buffer, PRISM_VERSION_PATCH);
23458 pm_buffer_append_byte(buffer, PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS ? 1 : 0);
23459}
23460
23464void
23465pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
23466 pm_serialize_header(buffer);
23467 pm_serialize_content(parser, node, buffer);
23468 pm_buffer_append_byte(buffer, '\0');
23469}
23470
23475void
23476pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23477 pm_options_t options = { 0 };
23478 pm_options_read(&options, data);
23479
23480 pm_arena_t arena = { 0 };
23481 pm_parser_t parser;
23482 pm_parser_init(&arena, &parser, source, size, &options);
23483
23484 pm_node_t *node = pm_parse(&parser);
23485
23486 pm_serialize_header(buffer);
23487 pm_serialize_content(&parser, node, buffer);
23488 pm_buffer_append_byte(buffer, '\0');
23489
23490 pm_parser_cleanup(&parser);
23491 pm_arena_cleanup(&arena);
23492 pm_options_cleanup(&options);
23493}
23494
23499void
23500pm_serialize_parse_stream(pm_buffer_t *buffer, pm_source_t *source, const char *data) {
23501 pm_arena_t arena = { 0 };
23502 pm_parser_t *parser;
23503 pm_options_t options = { 0 };
23504 pm_options_read(&options, data);
23505
23506 pm_node_t *node = pm_parse_stream(&parser, &arena, source, &options);
23507 pm_serialize_header(buffer);
23508 pm_serialize_content(parser, node, buffer);
23509 pm_buffer_append_byte(buffer, '\0');
23510
23511 pm_parser_free(parser);
23512 pm_arena_cleanup(&arena);
23513 pm_options_cleanup(&options);
23514}
23515
23524int8_t
23525pm_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) {
23526 pm_options_t options = { 0 };
23527 pm_options_read(&options, data);
23528
23529 pm_arena_t arena = { 0 };
23530 pm_parser_t parser;
23531 pm_parser_init(&arena, &parser, source, size, &options);
23532
23533 pm_parse(&parser);
23534
23535 int8_t result = -1;
23536 if (parser.error_list.size > 0) {
23537 const char *encoding_name = parser.encoding->name;
23538 pm_buffer_append_string(buffer, encoding_name, strlen(encoding_name));
23539 pm_buffer_append_byte(buffer, '\0');
23540
23541 result = (int8_t) pm_errors_format(&parser, buffer, format_type);
23542 }
23543
23544 pm_parser_cleanup(&parser);
23545 pm_arena_cleanup(&arena);
23546 pm_options_cleanup(&options);
23547
23548 return result;
23549}
23550
23554void
23555pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23556 pm_options_t options = { 0 };
23557 pm_options_read(&options, data);
23558
23559 pm_arena_t arena = { 0 };
23560 pm_parser_t parser;
23561 pm_parser_init(&arena, &parser, source, size, &options);
23562
23563 pm_parse(&parser);
23564 pm_serialize_header(buffer);
23565 pm_serialize_encoding(parser.encoding, buffer);
23566 pm_buffer_append_varsint(buffer, parser.start_line);
23567 pm_serialize_line_offset_list(&parser.line_offsets, buffer);
23568 pm_serialize_comment_list(&parser.comment_list, buffer);
23569
23570 pm_parser_cleanup(&parser);
23571 pm_arena_cleanup(&arena);
23572 pm_options_cleanup(&options);
23573}
23574
23575#endif
#define PRISM_ALIGNOF
Get the alignment requirement of a type.
Definition align.h:15
pm_comment_type_t
This is the type of a comment that we've found while parsing.
Definition comments.h:18
uint32_t pm_constant_id_t
A constant id is a unique identifier for a constant in the constant pool.
pm_errors_format_type_t
The type of formatting to use when formatting errors.
A header file that defines macros to exclude certain features of the prism library.
#define PRISM_FALLTHROUGH
We use -Wimplicit-fallthrough to guard potentially unintended fall-through between cases of a switch.
Definition fallthrough.h:15
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define xcalloc
Old name of ruby_xcalloc.
Definition xmalloc.h:55
int len
Length of the buffer.
Definition io.h:8
#define PRISM_INLINE
Old Visual Studio versions do not support the inline keyword, so we need to define it to be __inline.
Definition inline.h:12
VALUE type(ANYARGS)
ANYARGS-ed function type.
static const uint8_t PM_OPTIONS_COMMAND_LINE_N
A bit representing whether or not the command line -n option was set.
Definition options.h:96
#define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
String literals should not be frozen.
Definition options.h:31
#define PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
String literals should be made frozen.
Definition options.h:42
#define PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET
String literals may be frozen or mutable depending on the implementation default.
Definition options.h:37
static const uint8_t PM_OPTIONS_COMMAND_LINE_P
A bit representing whether or not the command line -p option was set.
Definition options.h:102
PRISM_NODISCARD PRISM_EXPORTED_FUNCTION pm_parser_t * pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) PRISM_NONNULL(1)
Allocate and initialize a parser with the given start and end pointers.
Definition prism.c:23205
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:23238
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:23409
#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 content_loc
SymbolNode::content_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