Ruby 4.1.0dev (2026-09-07 revision b57404b461ba8bf34e802d86b0db78388216e182)
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_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6468 break;
6469 }
6470
6471 return pm_source_file_node_new(
6472 parser->arena,
6473 ++parser->node_id,
6474 flags,
6475 PM_LOCATION_INIT_TOKEN(parser, file_keyword),
6476 parser->filepath
6477 );
6478}
6479
6483static pm_source_line_node_t *
6484pm_source_line_node_create(pm_parser_t *parser, const pm_token_t *token) {
6485 assert(token->type == PM_TOKEN_KEYWORD___LINE__);
6486
6487 return pm_source_line_node_new(
6488 parser->arena,
6489 ++parser->node_id,
6490 PM_NODE_FLAG_STATIC_LITERAL,
6491 PM_LOCATION_INIT_TOKEN(parser, token)
6492 );
6493}
6494
6498static pm_splat_node_t *
6499pm_splat_node_create(pm_parser_t *parser, const pm_token_t *operator, pm_node_t *expression) {
6500 return pm_splat_node_new(
6501 parser->arena,
6502 ++parser->node_id,
6503 0,
6504 (expression == NULL) ? PM_LOCATION_INIT_TOKEN(parser, operator) : PM_LOCATION_INIT_TOKEN_NODE(parser, operator, expression),
6505 TOK2LOC(parser, operator),
6506 expression
6507 );
6508}
6509
6513static pm_statements_node_t *
6514pm_statements_node_create(pm_parser_t *parser) {
6515 return pm_statements_node_new(
6516 parser->arena,
6517 ++parser->node_id,
6518 0,
6519 PM_LOCATION_INIT_UNSET,
6520 ((pm_node_list_t) { 0 })
6521 );
6522}
6523
6527static size_t
6528pm_statements_node_body_length(pm_statements_node_t *node) {
6529 return node && node->body.size;
6530}
6531
6536static PRISM_INLINE void
6537pm_statements_node_body_update(pm_statements_node_t *node, pm_node_t *statement) {
6538 if (pm_statements_node_body_length(node) == 0 || PM_NODE_START(statement) < PM_NODE_START(node)) {
6539 PM_NODE_START_SET_NODE(node, statement);
6540 }
6541
6542 if (PM_NODE_END(statement) > PM_NODE_END(node)) {
6543 PM_NODE_LENGTH_SET_NODE(node, statement);
6544 }
6545}
6546
6550static void
6551pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node, pm_node_t *statement, bool newline) {
6552 pm_statements_node_body_update(node, statement);
6553
6554 if (node->body.size > 0) {
6555 const pm_node_t *previous = node->body.nodes[node->body.size - 1];
6556
6557 switch (PM_NODE_TYPE(previous)) {
6558 case PM_BREAK_NODE:
6559 case PM_NEXT_NODE:
6560 case PM_REDO_NODE:
6561 case PM_RETRY_NODE:
6562 case PM_RETURN_NODE:
6563 pm_parser_warn_node(parser, statement, PM_WARN_UNREACHABLE_STATEMENT);
6564 break;
6565 default:
6566 break;
6567 }
6568 }
6569
6570 pm_node_list_append(parser->arena, &node->body, statement);
6571 if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6572}
6573
6577static void
6578pm_statements_node_body_prepend(pm_arena_t *arena, pm_statements_node_t *node, pm_node_t *statement) {
6579 pm_statements_node_body_update(node, statement);
6580 pm_node_list_prepend(arena, &node->body, statement);
6581 pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6582}
6583
6587static PRISM_INLINE pm_string_node_t *
6588pm_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) {
6589 pm_node_flags_t flags = 0;
6590
6591 switch (parser->frozen_string_literal) {
6592 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6593 flags = PM_STRING_FLAGS_MUTABLE;
6594 break;
6595 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6596 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6597 break;
6598 }
6599
6600 uint32_t start = PM_TOKEN_START(parser, opening == NULL ? content : opening);
6601 uint32_t end = PM_TOKEN_END(parser, closing == NULL ? content : closing);
6602
6603 return pm_string_node_new(
6604 parser->arena,
6605 ++parser->node_id,
6606 flags,
6607 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6608 NTOK2LOC(parser, opening),
6609 TOK2LOC(parser, content),
6610 NTOK2LOC(parser, closing),
6611 *string
6612 );
6613}
6614
6618static pm_string_node_t *
6619pm_string_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6620 return pm_string_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
6621}
6622
6627static pm_string_node_t *
6628pm_string_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
6629 pm_string_node_t *node = pm_string_node_create_unescaped(parser, opening, content, closing, &parser->current_string);
6630 parser->current_string = PM_STRING_EMPTY;
6631 return node;
6632}
6633
6637static pm_super_node_t *
6638pm_super_node_create(pm_parser_t *parser, const pm_token_t *keyword, pm_arguments_t *arguments) {
6639 assert(keyword->type == PM_TOKEN_KEYWORD_SUPER);
6640
6641 const pm_location_t *end = pm_arguments_end(arguments);
6642 assert(end != NULL && "unreachable");
6643
6644 return pm_super_node_new(
6645 parser->arena,
6646 ++parser->node_id,
6647 0,
6648 ((pm_location_t) { .start = PM_TOKEN_START(parser, keyword), .length = PM_LOCATION_END(end) - PM_TOKEN_START(parser, keyword) }),
6649 TOK2LOC(parser, keyword),
6650 arguments->opening_loc,
6651 arguments->arguments,
6652 arguments->closing_loc,
6653 arguments->block
6654 );
6655}
6656
6661static bool
6662pm_ascii_only_p(const pm_string_t *contents) {
6663 const size_t length = pm_string_length(contents);
6664 const uint8_t *source = pm_string_source(contents);
6665
6666 for (size_t index = 0; index < length; index++) {
6667 if (source[index] & 0x80) return false;
6668 }
6669
6670 return true;
6671}
6672
6676static void
6677parse_symbol_encoding_validate_utf8(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6678 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6679 size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor);
6680
6681 if (width == 0) {
6682 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6683 break;
6684 }
6685
6686 cursor += width;
6687 }
6688}
6689
6694static void
6695parse_symbol_encoding_validate_other(pm_parser_t *parser, const pm_token_t *location, const pm_string_t *contents) {
6696 const pm_encoding_t *encoding = parser->encoding;
6697
6698 for (const uint8_t *cursor = pm_string_source(contents), *end = cursor + pm_string_length(contents); cursor < end;) {
6699 size_t width = encoding->char_width(cursor, end - cursor);
6700
6701 if (width == 0) {
6702 pm_parser_err(parser, PM_TOKEN_START(parser, location), PM_TOKEN_LENGTH(location), PM_ERR_INVALID_SYMBOL);
6703 break;
6704 }
6705
6706 cursor += width;
6707 }
6708}
6709
6719static PRISM_INLINE pm_node_flags_t
6720parse_symbol_encoding(pm_parser_t *parser, const pm_encoding_t *explicit_encoding, const pm_token_t *location, const pm_string_t *contents, bool validate) {
6721 if (explicit_encoding != NULL) {
6722 // A Symbol may optionally have its encoding explicitly set. This will
6723 // happen if an escape sequence results in a non-ASCII code point.
6724 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
6725 if (validate) parse_symbol_encoding_validate_utf8(parser, location, contents);
6726 return PM_SYMBOL_FLAGS_FORCED_UTF8_ENCODING;
6727 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
6728 return PM_SYMBOL_FLAGS_FORCED_BINARY_ENCODING;
6729 } else if (validate) {
6730 parse_symbol_encoding_validate_other(parser, location, contents);
6731 }
6732 } else if (pm_ascii_only_p(contents)) {
6733 // Ruby stipulates that all source files must use an ASCII-compatible
6734 // encoding. Thus, all symbols appearing in source are eligible for
6735 // "downgrading" to US-ASCII.
6736 return PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING;
6737 } else if (validate) {
6738 parse_symbol_encoding_validate_other(parser, location, contents);
6739 }
6740
6741 return 0;
6742}
6743
6748static pm_symbol_node_t *
6749pm_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) {
6750 uint32_t start = opening == NULL ? PM_TOKEN_START(parser, value) : PM_TOKEN_START(parser, opening);
6751 uint32_t end = closing == NULL ? PM_TOKEN_END(parser, value) : PM_TOKEN_END(parser, closing);
6752
6753 return pm_symbol_node_new(
6754 parser->arena,
6755 ++parser->node_id,
6756 PM_NODE_FLAG_STATIC_LITERAL | flags,
6757 ((pm_location_t) { .start = start, .length = U32(end - start) }),
6758 NTOK2LOC(parser, opening),
6759 NTOK2LOC(parser, value),
6760 NTOK2LOC(parser, closing),
6761 *unescaped
6762 );
6763}
6764
6768static PRISM_INLINE pm_symbol_node_t *
6769pm_symbol_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6770 return pm_symbol_node_create_unescaped(parser, opening, value, closing, &PM_STRING_EMPTY, 0);
6771}
6772
6776static pm_symbol_node_t *
6777pm_symbol_node_create_current_string(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *value, const pm_token_t *closing) {
6778 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));
6779 parser->current_string = PM_STRING_EMPTY;
6780 return node;
6781}
6782
6786static pm_symbol_node_t *
6787pm_symbol_node_label_create(pm_parser_t *parser, const pm_token_t *token) {
6788 assert(token->type == PM_TOKEN_LABEL);
6789
6790 pm_token_t closing = { .type = PM_TOKEN_LABEL_END, .start = token->end - 1, .end = token->end };
6791 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = token->start, .end = token->end - 1 };
6792 pm_symbol_node_t *node = pm_symbol_node_create(parser, NULL, &label, &closing);
6793
6794 assert((label.end - label.start) >= 0);
6795 pm_string_shared_init(&node->unescaped, label.start, label.end);
6796 pm_node_flag_set(UP(node), parse_symbol_encoding(parser, parser->explicit_encoding, &label, &node->unescaped, false));
6797
6798 return node;
6799}
6800
6804static pm_symbol_node_t *
6805pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
6806 pm_symbol_node_t *node = pm_symbol_node_new(
6807 parser->arena,
6808 ++parser->node_id,
6809 PM_NODE_FLAG_STATIC_LITERAL | PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING,
6810 PM_LOCATION_INIT_UNSET,
6811 ((pm_location_t) { 0 }),
6812 ((pm_location_t) { 0 }),
6813 ((pm_location_t) { 0 }),
6814 ((pm_string_t) { 0 })
6815 );
6816
6817 pm_string_constant_init(&node->unescaped, content, strlen(content));
6818 return node;
6819}
6820
6824static bool
6825pm_symbol_node_label_p(const pm_parser_t *parser, const pm_node_t *node) {
6826 const pm_location_t *location = NULL;
6827
6828 switch (PM_NODE_TYPE(node)) {
6829 case PM_SYMBOL_NODE: {
6830 const pm_symbol_node_t *cast = (pm_symbol_node_t *) node;
6831 if (cast->closing_loc.length > 0) {
6832 location = &cast->closing_loc;
6833 }
6834 break;
6835 }
6836 case PM_INTERPOLATED_SYMBOL_NODE: {
6837 const pm_interpolated_symbol_node_t *cast = (pm_interpolated_symbol_node_t *) node;
6838 if (cast->closing_loc.length > 0) {
6839 location = &cast->closing_loc;
6840 }
6841 break;
6842 }
6843 default:
6844 return false;
6845 }
6846
6847 return (location != NULL) && (parser->start[PM_LOCATION_END(location) - 1] == ':');
6848}
6849
6853static pm_symbol_node_t *
6854pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const pm_token_t *opening, const pm_token_t *closing) {
6855 pm_symbol_node_t *new_node = pm_symbol_node_new(
6856 parser->arena,
6857 ++parser->node_id,
6858 PM_NODE_FLAG_STATIC_LITERAL,
6859 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
6860 TOK2LOC(parser, opening),
6861 node->content_loc,
6862 TOK2LOC(parser, closing),
6863 node->unescaped
6864 );
6865
6866 pm_token_t content = {
6867 .type = PM_TOKEN_IDENTIFIER,
6868 .start = parser->start + node->content_loc.start,
6869 .end = parser->start + node->content_loc.start + node->content_loc.length
6870 };
6871
6872 pm_node_flag_set(UP(new_node), parse_symbol_encoding(parser, parser->explicit_encoding, &content, &node->unescaped, true));
6873
6874 /* The old node is arena-allocated so no explicit free is needed. */
6875 return new_node;
6876}
6877
6881static pm_string_node_t *
6882pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) {
6883 pm_node_flags_t flags = 0;
6884
6885 switch (parser->frozen_string_literal) {
6886 case PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED:
6887 flags = PM_STRING_FLAGS_MUTABLE;
6888 break;
6889 case PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED:
6890 flags = PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN;
6891 break;
6892 }
6893
6894 pm_string_node_t *new_node = pm_string_node_new(
6895 parser->arena,
6896 ++parser->node_id,
6897 flags,
6898 PM_LOCATION_INIT_NODE(node),
6899 node->opening_loc,
6900 node->value_loc,
6901 node->closing_loc,
6902 node->unescaped
6903 );
6904
6905 /* The old node is arena-allocated so no explicit free is needed. */
6906 return new_node;
6907}
6908
6912static pm_true_node_t *
6913pm_true_node_create(pm_parser_t *parser, const pm_token_t *token) {
6914 assert(token->type == PM_TOKEN_KEYWORD_TRUE);
6915
6916 return pm_true_node_new(
6917 parser->arena,
6918 ++parser->node_id,
6919 PM_NODE_FLAG_STATIC_LITERAL,
6920 PM_LOCATION_INIT_TOKEN(parser, token)
6921 );
6922}
6923
6927static pm_true_node_t *
6928pm_true_node_synthesized_create(pm_parser_t *parser) {
6929 return pm_true_node_new(
6930 parser->arena,
6931 ++parser->node_id,
6932 PM_NODE_FLAG_STATIC_LITERAL,
6933 PM_LOCATION_INIT_UNSET
6934 );
6935}
6936
6940static pm_undef_node_t *
6941pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
6942 assert(token->type == PM_TOKEN_KEYWORD_UNDEF);
6943
6944 return pm_undef_node_new(
6945 parser->arena,
6946 ++parser->node_id,
6947 0,
6948 PM_LOCATION_INIT_TOKEN(parser, token),
6949 ((pm_node_list_t) { 0 }),
6950 TOK2LOC(parser, token)
6951 );
6952}
6953
6957static void
6958pm_undef_node_append(pm_arena_t *arena, pm_undef_node_t *node, pm_node_t *name) {
6959 PM_NODE_LENGTH_SET_NODE(node, name);
6960 pm_node_list_append(arena, &node->names, name);
6961}
6962
6966static pm_unless_node_t *
6967pm_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) {
6968 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6969 pm_node_t *end = statements == NULL ? predicate : UP(statements);
6970
6971 return pm_unless_node_new(
6972 parser->arena,
6973 ++parser->node_id,
6974 PM_NODE_FLAG_NEWLINE,
6975 PM_LOCATION_INIT_TOKEN_NODE(parser, keyword, end),
6976 TOK2LOC(parser, keyword),
6977 predicate,
6978 NTOK2LOC(parser, then_keyword),
6979 statements,
6980 NULL,
6981 ((pm_location_t) { 0 })
6982 );
6983}
6984
6988static pm_unless_node_t *
6989pm_unless_node_modifier_create(pm_parser_t *parser, pm_node_t *statement, const pm_token_t *unless_keyword, pm_node_t *predicate) {
6990 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
6991
6992 pm_statements_node_t *statements = pm_statements_node_create(parser);
6993 pm_statements_node_body_append(parser, statements, statement, true);
6994
6995 return pm_unless_node_new(
6996 parser->arena,
6997 ++parser->node_id,
6998 PM_NODE_FLAG_NEWLINE,
6999 PM_LOCATION_INIT_NODES(statement, predicate),
7000 TOK2LOC(parser, unless_keyword),
7001 predicate,
7002 ((pm_location_t) { 0 }),
7003 statements,
7004 NULL,
7005 ((pm_location_t) { 0 })
7006 );
7007}
7008
7009static PRISM_INLINE void
7010pm_unless_node_end_keyword_loc_set(const pm_parser_t *parser, pm_unless_node_t *node, const pm_token_t *end_keyword) {
7011 node->end_keyword_loc = TOK2LOC(parser, end_keyword);
7012 PM_NODE_LENGTH_SET_TOKEN(parser, node, end_keyword);
7013}
7014
7020static void
7021pm_loop_modifier_block_exits(pm_parser_t *parser, pm_statements_node_t *statements) {
7022 assert(parser->current_block_exits != NULL);
7023
7024 // All of the block exits that we want to remove should be within the
7025 // statements, and since we are modifying the statements, we shouldn't have
7026 // to check the end location.
7027 uint32_t start = statements->base.location.start;
7028
7029 for (size_t index = parser->current_block_exits->size; index > 0; index--) {
7030 pm_node_t *block_exit = parser->current_block_exits->nodes[index - 1];
7031 if (block_exit->location.start < start) break;
7032
7033 // Implicitly remove from the list by lowering the size.
7034 parser->current_block_exits->size--;
7035 }
7036}
7037
7041static pm_until_node_t *
7042pm_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) {
7043 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7044
7045 return pm_until_node_new(
7046 parser->arena,
7047 ++parser->node_id,
7048 flags,
7049 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7050 TOK2LOC(parser, keyword),
7051 NTOK2LOC(parser, do_keyword),
7052 TOK2LOC(parser, closing),
7053 predicate,
7054 statements
7055 );
7056}
7057
7061static pm_until_node_t *
7062pm_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) {
7063 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7064 pm_loop_modifier_block_exits(parser, statements);
7065
7066 return pm_until_node_new(
7067 parser->arena,
7068 ++parser->node_id,
7069 flags,
7070 PM_LOCATION_INIT_NODES(statements, predicate),
7071 TOK2LOC(parser, keyword),
7072 ((pm_location_t) { 0 }),
7073 ((pm_location_t) { 0 }),
7074 predicate,
7075 statements
7076 );
7077}
7078
7082static pm_when_node_t *
7083pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
7084 return pm_when_node_new(
7085 parser->arena,
7086 ++parser->node_id,
7087 0,
7088 PM_LOCATION_INIT_TOKEN(parser, keyword),
7089 TOK2LOC(parser, keyword),
7090 ((pm_node_list_t) { 0 }),
7091 ((pm_location_t) { 0 }),
7092 NULL
7093 );
7094}
7095
7099static void
7100pm_when_node_conditions_append(pm_arena_t *arena, pm_when_node_t *node, pm_node_t *condition) {
7101 PM_NODE_LENGTH_SET_NODE(node, condition);
7102 pm_node_list_append(arena, &node->conditions, condition);
7103}
7104
7108static PRISM_INLINE void
7109pm_when_node_then_keyword_loc_set(const pm_parser_t *parser, pm_when_node_t *node, const pm_token_t *then_keyword) {
7110 PM_NODE_LENGTH_SET_TOKEN(parser, node, then_keyword);
7111 node->then_keyword_loc = TOK2LOC(parser, then_keyword);
7112}
7113
7117static void
7118pm_when_node_statements_set(pm_when_node_t *node, pm_statements_node_t *statements) {
7119 if (PM_NODE_END(statements) > PM_NODE_END(node)) {
7120 PM_NODE_LENGTH_SET_NODE(node, statements);
7121 }
7122
7123 node->statements = statements;
7124}
7125
7129static pm_while_node_t *
7130pm_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) {
7131 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7132
7133 return pm_while_node_new(
7134 parser->arena,
7135 ++parser->node_id,
7136 flags,
7137 PM_LOCATION_INIT_TOKENS(parser, keyword, closing),
7138 TOK2LOC(parser, keyword),
7139 NTOK2LOC(parser, do_keyword),
7140 TOK2LOC(parser, closing),
7141 predicate,
7142 statements
7143 );
7144}
7145
7149static pm_while_node_t *
7150pm_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) {
7151 pm_conditional_predicate(parser, predicate, PM_CONDITIONAL_PREDICATE_TYPE_CONDITIONAL);
7152 pm_loop_modifier_block_exits(parser, statements);
7153
7154 return pm_while_node_new(
7155 parser->arena,
7156 ++parser->node_id,
7157 flags,
7158 PM_LOCATION_INIT_NODES(statements, predicate),
7159 TOK2LOC(parser, keyword),
7160 ((pm_location_t) { 0 }),
7161 ((pm_location_t) { 0 }),
7162 predicate,
7163 statements
7164 );
7165}
7166
7170static pm_while_node_t *
7171pm_while_node_synthesized_create(pm_parser_t *parser, pm_node_t *predicate, pm_statements_node_t *statements) {
7172 return pm_while_node_new(
7173 parser->arena,
7174 ++parser->node_id,
7175 0,
7176 PM_LOCATION_INIT_UNSET,
7177 ((pm_location_t) { 0 }),
7178 ((pm_location_t) { 0 }),
7179 ((pm_location_t) { 0 }),
7180 predicate,
7181 statements
7182 );
7183}
7184
7189static pm_x_string_node_t *
7190pm_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) {
7191 return pm_x_string_node_new(
7192 parser->arena,
7193 ++parser->node_id,
7194 PM_STRING_FLAGS_FROZEN,
7195 PM_LOCATION_INIT_TOKENS(parser, opening, closing),
7196 TOK2LOC(parser, opening),
7197 TOK2LOC(parser, content),
7198 TOK2LOC(parser, closing),
7199 *unescaped
7200 );
7201}
7202
7206static PRISM_INLINE pm_x_string_node_t *
7207pm_xstring_node_create(pm_parser_t *parser, const pm_token_t *opening, const pm_token_t *content, const pm_token_t *closing) {
7208 return pm_xstring_node_create_unescaped(parser, opening, content, closing, &PM_STRING_EMPTY);
7209}
7210
7214static pm_yield_node_t *
7215pm_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) {
7216 uint32_t start = PM_TOKEN_START(parser, keyword);
7217 uint32_t end;
7218
7219 if (rparen_loc->length > 0) {
7220 end = PM_LOCATION_END(rparen_loc);
7221 } else if (arguments != NULL) {
7222 end = PM_NODE_END(arguments);
7223 } else if (lparen_loc->length > 0) {
7224 end = PM_LOCATION_END(lparen_loc);
7225 } else {
7226 end = PM_TOKEN_END(parser, keyword);
7227 }
7228
7229 return pm_yield_node_new(
7230 parser->arena,
7231 ++parser->node_id,
7232 0,
7233 ((pm_location_t) { .start = start, .length = U32(end - start) }),
7234 TOK2LOC(parser, keyword),
7235 *lparen_loc,
7236 arguments,
7237 *rparen_loc
7238 );
7239}
7240
7245static int
7246pm_parser_local_depth_constant_id(pm_parser_t *parser, pm_constant_id_t constant_id) {
7247 pm_scope_t *scope = parser->current_scope;
7248 int depth = 0;
7249
7250 while (scope != NULL) {
7251 if (pm_locals_find(&scope->locals, constant_id) != UINT32_MAX) return depth;
7252 if (scope->closed) break;
7253
7254 scope = scope->previous;
7255 depth++;
7256 }
7257
7258 return -1;
7259}
7260
7266static PRISM_INLINE int
7267pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
7268 return pm_parser_local_depth_constant_id(parser, pm_parser_constant_id_token(parser, token));
7269}
7270
7274static PRISM_INLINE void
7275pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7276 pm_locals_write(&parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
7277}
7278
7282static pm_constant_id_t
7283pm_parser_local_add_raw(pm_parser_t *parser, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7284 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
7285 if (constant_id != 0) pm_parser_local_add(parser, constant_id, start, end, reads);
7286 return constant_id;
7287}
7288
7292static PRISM_INLINE pm_constant_id_t
7293pm_parser_local_add_location(pm_parser_t *parser, pm_location_t *location, uint32_t reads) {
7294 return pm_parser_local_add_raw(parser, parser->start + location->start, parser->start + location->start + location->length, reads);
7295}
7296
7300static PRISM_INLINE pm_constant_id_t
7301pm_parser_local_add_token(pm_parser_t *parser, pm_token_t *token, uint32_t reads) {
7302 return pm_parser_local_add_raw(parser, token->start, token->end, reads);
7303}
7304
7308static pm_constant_id_t
7309pm_parser_local_add_owned(pm_parser_t *parser, uint8_t *start, size_t length) {
7310 pm_constant_id_t constant_id = pm_parser_constant_id_owned(parser, start, length);
7311 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7312 return constant_id;
7313}
7314
7318static pm_constant_id_t
7319pm_parser_local_add_constant(pm_parser_t *parser, const char *start, size_t length) {
7320 pm_constant_id_t constant_id = pm_parser_constant_id_constant(parser, start, length);
7321 if (constant_id != 0) pm_parser_local_add(parser, constant_id, parser->start, parser->start, 1);
7322 return constant_id;
7323}
7324
7332static bool
7333pm_parser_parameter_name_check(pm_parser_t *parser, const pm_token_t *name) {
7334 // We want to check whether the parameter name is a numbered parameter or
7335 // not.
7336 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, name), PM_TOKEN_LENGTH(name));
7337
7338 // Otherwise we'll fetch the constant id for the parameter name and check
7339 // whether it's already in the current scope.
7340 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, name);
7341
7342 if (pm_locals_find(&parser->current_scope->locals, constant_id) != UINT32_MAX) {
7343 // Add an error if the parameter doesn't start with _ and has been seen before
7344 if ((name->start < name->end) && (*name->start != '_')) {
7345 pm_parser_err_token(parser, name, PM_ERR_PARAMETER_NAME_DUPLICATED);
7346 }
7347 return true;
7348 }
7349 return false;
7350}
7351
7355static void
7356pm_parser_scope_pop(pm_parser_t *parser) {
7357 pm_scope_t *scope = parser->current_scope;
7358 parser->current_scope = scope->previous;
7359 pm_locals_free(&scope->locals);
7360 xfree_sized(scope, sizeof(pm_scope_t));
7361}
7362
7363/******************************************************************************/
7364/* Stack helpers */
7365/******************************************************************************/
7366
7370static PRISM_INLINE void
7371pm_state_stack_push(pm_state_stack_t *stack, bool value) {
7372 *stack = (*stack << 1) | (value & 1);
7373}
7374
7378static PRISM_INLINE void
7379pm_state_stack_pop(pm_state_stack_t *stack) {
7380 *stack >>= 1;
7381}
7382
7386static PRISM_INLINE bool
7387pm_state_stack_p(const pm_state_stack_t *stack) {
7388 return *stack & 1;
7389}
7390
7391static PRISM_INLINE void
7392pm_accepts_block_stack_push(pm_parser_t *parser, bool value) {
7393 // Use the negation of the value to prevent stack overflow.
7394 pm_state_stack_push(&parser->accepts_block_stack, !value);
7395}
7396
7397static PRISM_INLINE void
7398pm_accepts_block_stack_pop(pm_parser_t *parser) {
7399 pm_state_stack_pop(&parser->accepts_block_stack);
7400}
7401
7402static PRISM_INLINE bool
7403pm_accepts_block_stack_p(pm_parser_t *parser) {
7404 return !pm_state_stack_p(&parser->accepts_block_stack);
7405}
7406
7407static PRISM_INLINE void
7408pm_do_loop_stack_push(pm_parser_t *parser, bool value) {
7409 pm_state_stack_push(&parser->do_loop_stack, value);
7410}
7411
7412static PRISM_INLINE void
7413pm_do_loop_stack_pop(pm_parser_t *parser) {
7414 pm_state_stack_pop(&parser->do_loop_stack);
7415}
7416
7417static PRISM_INLINE bool
7418pm_do_loop_stack_p(pm_parser_t *parser) {
7419 return pm_state_stack_p(&parser->do_loop_stack);
7420}
7421
7446static PRISM_INLINE void
7447pm_enclosure_frame_push(pm_parser_t *parser) {
7448 pm_do_loop_stack_push(parser, false);
7449 pm_accepts_block_stack_push(parser, true);
7450}
7451
7452static PRISM_INLINE void
7453pm_enclosure_frame_pop(pm_parser_t *parser) {
7454 pm_do_loop_stack_pop(parser);
7455 pm_accepts_block_stack_pop(parser);
7456}
7457
7458/******************************************************************************/
7459/* Lexer check helpers */
7460/******************************************************************************/
7461
7466static PRISM_INLINE uint8_t
7467peek_at(const pm_parser_t *parser, const uint8_t *cursor) {
7468 if (cursor < parser->end) {
7469 return *cursor;
7470 } else {
7471 return '\0';
7472 }
7473}
7474
7480static PRISM_INLINE uint8_t
7481peek_offset(pm_parser_t *parser, ptrdiff_t offset) {
7482 return peek_at(parser, parser->current.end + offset);
7483}
7484
7489static PRISM_INLINE uint8_t
7490peek(const pm_parser_t *parser) {
7491 return peek_at(parser, parser->current.end);
7492}
7493
7498static PRISM_INLINE bool
7499match(pm_parser_t *parser, uint8_t value) {
7500 if (peek(parser) == value) {
7501 parser->current.end++;
7502 return true;
7503 }
7504 return false;
7505}
7506
7511static PRISM_INLINE size_t
7512match_eol_at(pm_parser_t *parser, const uint8_t *cursor) {
7513 if (peek_at(parser, cursor) == '\n') {
7514 return 1;
7515 }
7516 if (peek_at(parser, cursor) == '\r' && peek_at(parser, cursor + 1) == '\n') {
7517 return 2;
7518 }
7519 return 0;
7520}
7521
7527static PRISM_INLINE size_t
7528match_eol_offset(pm_parser_t *parser, ptrdiff_t offset) {
7529 return match_eol_at(parser, parser->current.end + offset);
7530}
7531
7537static PRISM_INLINE size_t
7538match_eol(pm_parser_t *parser) {
7539 return match_eol_at(parser, parser->current.end);
7540}
7541
7545static PRISM_INLINE const uint8_t *
7546next_newline(const uint8_t *cursor, ptrdiff_t length) {
7547 assert(length >= 0);
7548
7549 // Note that it's okay for us to use memchr here to look for \n because none
7550 // of the encodings that we support have \n as a component of a multi-byte
7551 // character.
7552 return memchr(cursor, '\n', (size_t) length);
7553}
7554
7558static PRISM_INLINE bool
7559ambiguous_operator_p(const pm_parser_t *parser, bool space_seen) {
7560 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));
7561}
7562
7567static bool
7568parser_lex_magic_comment_encoding_value(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
7569 const pm_encoding_t *encoding = pm_encoding_find(start, end);
7570
7571 if (encoding != NULL) {
7572 if (parser->encoding != encoding) {
7573 parser->encoding = encoding;
7574 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
7575 }
7576
7577 parser->encoding_changed = (encoding != PM_ENCODING_UTF_8_ENTRY);
7578 return true;
7579 }
7580
7581 return false;
7582}
7583
7588static void
7589parser_lex_magic_comment_encoding(pm_parser_t *parser) {
7590 const uint8_t *cursor = parser->current.start + 1;
7591 const uint8_t *end = parser->current.end;
7592
7593 bool separator = false;
7594 while (true) {
7595 if (end - cursor <= 6) return;
7596 switch (cursor[6]) {
7597 case 'C': case 'c': cursor += 6; continue;
7598 case 'O': case 'o': cursor += 5; continue;
7599 case 'D': case 'd': cursor += 4; continue;
7600 case 'I': case 'i': cursor += 3; continue;
7601 case 'N': case 'n': cursor += 2; continue;
7602 case 'G': case 'g': cursor += 1; continue;
7603 case '=': case ':':
7604 separator = true;
7605 cursor += 6;
7606 break;
7607 default:
7608 cursor += 6;
7609 if (pm_char_is_whitespace(*cursor)) break;
7610 continue;
7611 }
7612 if (pm_strncasecmp(cursor - 6, (const uint8_t *) "coding", 6) == 0) break;
7613 separator = false;
7614 }
7615
7616 while (true) {
7617 do {
7618 if (++cursor >= end) return;
7619 } while (pm_char_is_whitespace(*cursor));
7620
7621 if (separator) break;
7622 if (*cursor != '=' && *cursor != ':') return;
7623
7624 separator = true;
7625 cursor++;
7626 }
7627
7628 const uint8_t *value_start = cursor;
7629 while ((*cursor == '-' || *cursor == '_' || parser->encoding->alnum_char(cursor, 1)) && ++cursor < end);
7630
7631 if (!parser_lex_magic_comment_encoding_value(parser, value_start, cursor)) {
7632 // If we were unable to parse the encoding value, then we've got an
7633 // issue because we didn't understand the encoding that the user was
7634 // trying to use. In this case we'll keep using the default encoding but
7635 // add an error to the parser to indicate an unsuccessful parse.
7636 pm_parser_err(parser, U32(value_start - parser->start), U32(cursor - value_start), PM_ERR_INVALID_ENCODING_MAGIC_COMMENT);
7637 }
7638}
7639
7640typedef enum {
7641 PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE,
7642 PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE,
7643 PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID
7644} pm_magic_comment_boolean_value_t;
7645
7650static pm_magic_comment_boolean_value_t
7651parser_lex_magic_comment_boolean_value(const uint8_t *value_start, uint32_t value_length) {
7652 if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "true", 4) == 0) {
7653 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE;
7654 } else if (value_length == 5 && pm_strncasecmp(value_start, (const uint8_t *) "false", 5) == 0) {
7655 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE;
7656 } else {
7657 return PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID;
7658 }
7659}
7660
7661static PRISM_INLINE bool
7662pm_char_is_magic_comment_key_delimiter(const uint8_t b) {
7663 return b == '\'' || b == '"' || b == ':' || b == ';';
7664}
7665
7671static PRISM_INLINE const uint8_t *
7672parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor, const uint8_t *end) {
7673 // Scan for '*' as the middle character, since it is rarer than '-' in
7674 // typical comments and avoids repeated memchr calls for '-' that hit
7675 // dashes in words like "foo-bar".
7676 while ((cursor + 3 <= end) && (cursor = pm_memchr(cursor + 1, '*', (size_t) (end - cursor - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7677 if (cursor[-1] == '-' && cursor + 1 < end && cursor[1] == '-') {
7678 return cursor - 1;
7679 }
7680 }
7681 return NULL;
7682}
7683
7694static PRISM_INLINE bool
7695parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
7696 bool result = true;
7697
7698 const uint8_t *start = parser->current.start + 1;
7699 const uint8_t *end = parser->current.end;
7700 if (end - start <= 7) return false;
7701
7702 const uint8_t *cursor;
7703 bool indicator = false;
7704
7705 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7706 start = cursor + 3;
7707
7708 if ((cursor = parser_lex_magic_comment_emacs_marker(parser, start, end)) != NULL) {
7709 end = cursor;
7710 indicator = true;
7711 } else {
7712 // If we have a start marker but not an end marker, then we cannot
7713 // have a magic comment.
7714 return false;
7715 }
7716 } else {
7717 // Non-emacs magic comments must contain a colon for `key: value`.
7718 // Reject early if there is no colon to avoid scanning the entire
7719 // comment character-by-character.
7720 if (pm_memchr(start, ':', (size_t) (end - start), parser->encoding_changed, parser->encoding) == NULL) {
7721 return false;
7722 }
7723
7724 // Advance start past leading whitespace so the main loop begins
7725 // directly at the key, avoiding a redundant whitespace scan.
7726 start += pm_strspn_whitespace(start, end - start);
7727 }
7728
7729 cursor = start;
7730 while (cursor < end) {
7731 if (indicator) {
7732 while (cursor < end && (pm_char_is_magic_comment_key_delimiter(*cursor) || pm_char_is_whitespace(*cursor))) cursor++;
7733 }
7734
7735 const uint8_t *key_start = cursor;
7736 while (cursor < end && (!pm_char_is_magic_comment_key_delimiter(*cursor) && !pm_char_is_whitespace(*cursor))) cursor++;
7737
7738 const uint8_t *key_end = cursor;
7739 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7740 if (cursor == end) break;
7741
7742 if (*cursor == ':') {
7743 cursor++;
7744 } else {
7745 if (!indicator) return false;
7746 continue;
7747 }
7748
7749 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7750 if (cursor == end) break;
7751
7752 const uint8_t *value_start;
7753 const uint8_t *value_end;
7754
7755 if (*cursor == '"') {
7756 value_start = ++cursor;
7757 for (; cursor < end && *cursor != '"'; cursor++) {
7758 if (*cursor == '\\' && (cursor + 1 < end)) cursor++;
7759 }
7760 value_end = cursor;
7761 if (cursor < end && *cursor == '"') cursor++;
7762 } else {
7763 value_start = cursor;
7764 while (cursor < end && *cursor != '"' && *cursor != ';' && !pm_char_is_whitespace(*cursor)) cursor++;
7765 value_end = cursor;
7766 }
7767
7768 if (indicator) {
7769 while (cursor < end && (*cursor == ';' || pm_char_is_whitespace(*cursor))) cursor++;
7770 } else {
7771 while (cursor < end && pm_char_is_whitespace(*cursor)) cursor++;
7772 if (cursor != end) return false;
7773 }
7774
7775 // Here, we need to do some processing on the key to swap out dashes for
7776 // underscores. We only need to do this if there _is_ a dash in the key.
7777 pm_string_t key;
7778 const size_t key_length = (size_t) (key_end - key_start);
7779 const uint8_t *dash = pm_memchr(key_start, '-', key_length, parser->encoding_changed, parser->encoding);
7780
7781 if (dash == NULL) {
7782 pm_string_shared_init(&key, key_start, key_end);
7783 } else {
7784 uint8_t *buffer = xmalloc(key_length);
7785 if (buffer == NULL) break;
7786
7787 memcpy(buffer, key_start, key_length);
7788 buffer[dash - key_start] = '_';
7789
7790 while ((dash = pm_memchr(dash + 1, '-', (size_t) (key_end - dash - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7791 buffer[dash - key_start] = '_';
7792 }
7793
7794 pm_string_owned_init(&key, buffer, key_length);
7795 }
7796
7797 // Finally, we can start checking the key against the list of known
7798 // magic comment keys, and potentially change state based on that.
7799 const uint8_t *key_source = pm_string_source(&key);
7800 uint32_t value_length = (uint32_t) (value_end - value_start);
7801
7802 // We only want to attempt to compare against encoding comments if it's
7803 // the first line in the file (or the second in the case of a shebang).
7804 if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7805 if (
7806 (key_length == 8 && pm_strncasecmp(key_source, (const uint8_t *) "encoding", 8) == 0) ||
7807 (key_length == 6 && pm_strncasecmp(key_source, (const uint8_t *) "coding", 6) == 0)
7808 ) {
7809 result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7810 }
7811 }
7812
7813 if (key_length == 11) {
7814 if (pm_strncasecmp(key_source, (const uint8_t *) "warn_indent", 11) == 0) {
7815 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7816 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7817 PM_PARSER_WARN_TOKEN_FORMAT(
7818 parser,
7819 &parser->current,
7820 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7821 (int) key_length,
7822 (const char *) key_source,
7823 (int) value_length,
7824 (const char *) value_start
7825 );
7826 break;
7827 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7828 parser->warn_mismatched_indentation = false;
7829 break;
7830 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7831 parser->warn_mismatched_indentation = true;
7832 break;
7833 }
7834 }
7835 } else if (key_length == 21) {
7836 if (pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) {
7837 // We only want to handle frozen string literal comments if it's
7838 // before any semantic tokens have been seen.
7839 if (semantic_token_seen) {
7840 pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
7841 } else {
7842 switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7843 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7844 PM_PARSER_WARN_TOKEN_FORMAT(
7845 parser,
7846 &parser->current,
7847 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7848 (int) key_length,
7849 (const char *) key_source,
7850 (int) value_length,
7851 (const char *) value_start
7852 );
7853 break;
7854 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7855 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
7856 break;
7857 case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7858 parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
7859 break;
7860 }
7861 }
7862 }
7863 } else if (key_length == 24) {
7864 if (pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) {
7865 const uint8_t *cursor = parser->current.start;
7866 while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
7867
7868 if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
7869 pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
7870 } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
7871 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
7872 } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
7873 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
7874 } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
7875 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
7876 } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
7877 pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
7878 } else {
7879 PM_PARSER_WARN_TOKEN_FORMAT(
7880 parser,
7881 &parser->current,
7882 PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7883 (int) key_length,
7884 (const char *) key_source,
7885 (int) value_length,
7886 (const char *) value_start
7887 );
7888 }
7889 }
7890 }
7891
7892 // When we're done, we want to free the string in case we had to
7893 // allocate memory for it.
7894 pm_string_cleanup(&key);
7895
7896 // Allocate a new magic comment node to append to the parser's list.
7897 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));
7898 magic_comment->node.next = NULL;
7899 magic_comment->key = (pm_location_t) { .start = U32(key_start - parser->start), .length = U32(key_length) };
7900 magic_comment->value = (pm_location_t) { .start = U32(value_start - parser->start), .length = value_length };
7901 pm_list_append(&parser->magic_comment_list, (pm_list_node_t *) magic_comment);
7902 }
7903
7904 return result;
7905}
7906
7907/******************************************************************************/
7908/* Context manipulations */
7909/******************************************************************************/
7910
7911static const uint32_t context_terminators[] = {
7912 [PM_CONTEXT_NONE] = 0,
7913 [PM_CONTEXT_BEGIN] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7914 [PM_CONTEXT_BEGIN_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7915 [PM_CONTEXT_BEGIN_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7916 [PM_CONTEXT_BEGIN_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7917 [PM_CONTEXT_BLOCK_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7918 [PM_CONTEXT_BLOCK_KEYWORDS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7919 [PM_CONTEXT_BLOCK_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7920 [PM_CONTEXT_BLOCK_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7921 [PM_CONTEXT_BLOCK_PARAMETERS] = (1U << PM_TOKEN_PIPE),
7922 [PM_CONTEXT_BLOCK_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7923 [PM_CONTEXT_CASE_WHEN] = (1U << PM_TOKEN_KEYWORD_WHEN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7924 [PM_CONTEXT_CASE_IN] = (1U << PM_TOKEN_KEYWORD_IN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
7925 [PM_CONTEXT_CLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7926 [PM_CONTEXT_CLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7927 [PM_CONTEXT_CLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7928 [PM_CONTEXT_CLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7929 [PM_CONTEXT_DEF] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7930 [PM_CONTEXT_DEF_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7931 [PM_CONTEXT_DEF_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7932 [PM_CONTEXT_DEF_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7933 [PM_CONTEXT_DEF_PARAMS] = (1U << PM_TOKEN_EOF),
7934 [PM_CONTEXT_DEFINED] = (1U << PM_TOKEN_EOF),
7935 [PM_CONTEXT_DEFAULT_PARAMS] = (1U << PM_TOKEN_COMMA) | (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7936 [PM_CONTEXT_ELSE] = (1U << PM_TOKEN_KEYWORD_END),
7937 [PM_CONTEXT_ELSIF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7938 [PM_CONTEXT_EMBEXPR] = (1U << PM_TOKEN_EMBEXPR_END),
7939 [PM_CONTEXT_FOR] = (1U << PM_TOKEN_KEYWORD_END),
7940 [PM_CONTEXT_FOR_INDEX] = (1U << PM_TOKEN_KEYWORD_IN),
7941 [PM_CONTEXT_IF] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_ELSIF) | (1U << PM_TOKEN_KEYWORD_END),
7942 [PM_CONTEXT_LAMBDA_BRACES] = (1U << PM_TOKEN_BRACE_RIGHT),
7943 [PM_CONTEXT_LAMBDA_DO_END] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7944 [PM_CONTEXT_LAMBDA_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7945 [PM_CONTEXT_LAMBDA_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7946 [PM_CONTEXT_LAMBDA_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7947 [PM_CONTEXT_LOOP_PREDICATE] = (1U << PM_TOKEN_KEYWORD_DO) | (1U << PM_TOKEN_KEYWORD_THEN),
7948 [PM_CONTEXT_MAIN] = (1U << PM_TOKEN_EOF),
7949 [PM_CONTEXT_MODULE] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7950 [PM_CONTEXT_MODULE_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7951 [PM_CONTEXT_MODULE_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7952 [PM_CONTEXT_MODULE_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7953 [PM_CONTEXT_MULTI_TARGET] = (1U << PM_TOKEN_EOF),
7954 [PM_CONTEXT_PARENS] = (1U << PM_TOKEN_PARENTHESIS_RIGHT),
7955 [PM_CONTEXT_POSTEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7956 [PM_CONTEXT_PREDICATE] = (1U << PM_TOKEN_KEYWORD_THEN) | (1U << PM_TOKEN_NEWLINE) | (1U << PM_TOKEN_SEMICOLON),
7957 [PM_CONTEXT_PREEXE] = (1U << PM_TOKEN_BRACE_RIGHT),
7958 [PM_CONTEXT_RESCUE_MODIFIER] = (1U << PM_TOKEN_EOF),
7959 [PM_CONTEXT_SCLASS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
7960 [PM_CONTEXT_SCLASS_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
7961 [PM_CONTEXT_SCLASS_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
7962 [PM_CONTEXT_SCLASS_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7963 [PM_CONTEXT_TERNARY] = (1U << PM_TOKEN_EOF),
7964 [PM_CONTEXT_UNLESS] = (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
7965 [PM_CONTEXT_UNTIL] = (1U << PM_TOKEN_KEYWORD_END),
7966 [PM_CONTEXT_WHILE] = (1U << PM_TOKEN_KEYWORD_END),
7967};
7968
7969static PRISM_INLINE bool
7970context_terminator(pm_context_t context, pm_token_t *token) {
7971 return token->type < 32 && (context_terminators[context] & (1U << token->type));
7972}
7973
7978static pm_context_t
7979context_recoverable(const pm_parser_t *parser, pm_token_t *token) {
7980 pm_context_node_t *context_node = parser->current_context;
7981
7982 while (context_node != NULL) {
7983 if (context_terminator(context_node->context, token)) return context_node->context;
7984 context_node = context_node->prev;
7985 }
7986
7987 return PM_CONTEXT_NONE;
7988}
7989
7990static bool
7991context_push(pm_parser_t *parser, pm_context_t context) {
7992 pm_context_node_t *context_node = (pm_context_node_t *) xmalloc(sizeof(pm_context_node_t));
7993 if (context_node == NULL) return false;
7994
7995 *context_node = (pm_context_node_t) { .context = context, .prev = NULL };
7996
7997 if (parser->current_context == NULL) {
7998 parser->current_context = context_node;
7999 } else {
8000 context_node->prev = parser->current_context;
8001 parser->current_context = context_node;
8002 }
8003
8004 return true;
8005}
8006
8007static void
8008context_pop(pm_parser_t *parser) {
8009 pm_context_node_t *prev = parser->current_context->prev;
8010 xfree_sized(parser->current_context, sizeof(pm_context_node_t));
8011 parser->current_context = prev;
8012}
8013
8014static bool
8015context_p(const pm_parser_t *parser, pm_context_t context) {
8016 pm_context_node_t *context_node = parser->current_context;
8017
8018 while (context_node != NULL) {
8019 if (context_node->context == context) return true;
8020 context_node = context_node->prev;
8021 }
8022
8023 return false;
8024}
8025
8026static bool
8027context_def_p(const pm_parser_t *parser) {
8028 pm_context_node_t *context_node = parser->current_context;
8029
8030 while (context_node != NULL) {
8031 switch (context_node->context) {
8032 case PM_CONTEXT_DEF:
8033 case PM_CONTEXT_DEF_PARAMS:
8034 case PM_CONTEXT_DEF_ENSURE:
8035 case PM_CONTEXT_DEF_RESCUE:
8036 case PM_CONTEXT_DEF_ELSE:
8037 return true;
8038 case PM_CONTEXT_CLASS:
8039 case PM_CONTEXT_CLASS_ENSURE:
8040 case PM_CONTEXT_CLASS_RESCUE:
8041 case PM_CONTEXT_CLASS_ELSE:
8042 case PM_CONTEXT_MODULE:
8043 case PM_CONTEXT_MODULE_ENSURE:
8044 case PM_CONTEXT_MODULE_RESCUE:
8045 case PM_CONTEXT_MODULE_ELSE:
8046 case PM_CONTEXT_SCLASS:
8047 case PM_CONTEXT_SCLASS_ENSURE:
8048 case PM_CONTEXT_SCLASS_RESCUE:
8049 case PM_CONTEXT_SCLASS_ELSE:
8050 return false;
8051 default:
8052 context_node = context_node->prev;
8053 }
8054 }
8055
8056 return false;
8057}
8058
8063static const char *
8064context_human(pm_context_t context) {
8065 switch (context) {
8066 case PM_CONTEXT_NONE:
8067 assert(false && "unreachable");
8068 return "";
8069 case PM_CONTEXT_BEGIN: return "begin statement";
8070 case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block";
8071 case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block";
8072 case PM_CONTEXT_BLOCK_PARAMETERS: return "'|'..'|' block parameter";
8073 case PM_CONTEXT_CASE_WHEN: return "'when' clause";
8074 case PM_CONTEXT_CASE_IN: return "'in' clause";
8075 case PM_CONTEXT_CLASS: return "class definition";
8076 case PM_CONTEXT_DEF: return "method definition";
8077 case PM_CONTEXT_DEF_PARAMS: return "method parameters";
8078 case PM_CONTEXT_DEFAULT_PARAMS: return "parameter default value";
8079 case PM_CONTEXT_DEFINED: return "'defined?' expression";
8080 case PM_CONTEXT_ELSE:
8081 case PM_CONTEXT_BEGIN_ELSE:
8082 case PM_CONTEXT_BLOCK_ELSE:
8083 case PM_CONTEXT_CLASS_ELSE:
8084 case PM_CONTEXT_DEF_ELSE:
8085 case PM_CONTEXT_LAMBDA_ELSE:
8086 case PM_CONTEXT_MODULE_ELSE:
8087 case PM_CONTEXT_SCLASS_ELSE: return "'else' clause";
8088 case PM_CONTEXT_ELSIF: return "'elsif' clause";
8089 case PM_CONTEXT_EMBEXPR: return "embedded expression";
8090 case PM_CONTEXT_BEGIN_ENSURE:
8091 case PM_CONTEXT_BLOCK_ENSURE:
8092 case PM_CONTEXT_CLASS_ENSURE:
8093 case PM_CONTEXT_DEF_ENSURE:
8094 case PM_CONTEXT_LAMBDA_ENSURE:
8095 case PM_CONTEXT_MODULE_ENSURE:
8096 case PM_CONTEXT_SCLASS_ENSURE: return "'ensure' clause";
8097 case PM_CONTEXT_FOR: return "for loop";
8098 case PM_CONTEXT_FOR_INDEX: return "for loop index";
8099 case PM_CONTEXT_IF: return "if statement";
8100 case PM_CONTEXT_LAMBDA_BRACES: return "'{'..'}' lambda block";
8101 case PM_CONTEXT_LAMBDA_DO_END: return "'do'..'end' lambda block";
8102 case PM_CONTEXT_LOOP_PREDICATE: return "loop predicate";
8103 case PM_CONTEXT_MAIN: return "top level context";
8104 case PM_CONTEXT_MODULE: return "module definition";
8105 case PM_CONTEXT_MULTI_TARGET: return "multiple targets";
8106 case PM_CONTEXT_PARENS: return "parentheses";
8107 case PM_CONTEXT_POSTEXE: return "'END' block";
8108 case PM_CONTEXT_PREDICATE: return "predicate";
8109 case PM_CONTEXT_PREEXE: return "'BEGIN' block";
8110 case PM_CONTEXT_BEGIN_RESCUE:
8111 case PM_CONTEXT_BLOCK_RESCUE:
8112 case PM_CONTEXT_CLASS_RESCUE:
8113 case PM_CONTEXT_DEF_RESCUE:
8114 case PM_CONTEXT_LAMBDA_RESCUE:
8115 case PM_CONTEXT_MODULE_RESCUE:
8116 case PM_CONTEXT_RESCUE_MODIFIER:
8117 case PM_CONTEXT_SCLASS_RESCUE: return "'rescue' clause";
8118 case PM_CONTEXT_SCLASS: return "singleton class definition";
8119 case PM_CONTEXT_TERNARY: return "ternary expression";
8120 case PM_CONTEXT_UNLESS: return "unless statement";
8121 case PM_CONTEXT_UNTIL: return "until statement";
8122 case PM_CONTEXT_WHILE: return "while statement";
8123 }
8124
8125 assert(false && "unreachable");
8126 return "";
8127}
8128
8129/******************************************************************************/
8130/* Specific token lexers */
8131/******************************************************************************/
8132
8133static PRISM_INLINE void
8134pm_strspn_number_validate(pm_parser_t *parser, const uint8_t *string, size_t length, const uint8_t *invalid) {
8135 if (invalid != NULL) {
8136 pm_diagnostic_id_t diag_id = (invalid == (string + length - 1)) ? PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING : PM_ERR_INVALID_NUMBER_UNDERSCORE_INNER;
8137 pm_parser_err(parser, U32(invalid - parser->start), 1, diag_id);
8138 }
8139}
8140
8141static size_t
8142pm_strspn_binary_number_validate(pm_parser_t *parser, const uint8_t *string) {
8143 const uint8_t *invalid = NULL;
8144 size_t length = pm_strspn_binary_number(string, parser->end - string, &invalid);
8145 pm_strspn_number_validate(parser, string, length, invalid);
8146 return length;
8147}
8148
8149static size_t
8150pm_strspn_octal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8151 const uint8_t *invalid = NULL;
8152 size_t length = pm_strspn_octal_number(string, parser->end - string, &invalid);
8153 pm_strspn_number_validate(parser, string, length, invalid);
8154 return length;
8155}
8156
8157static size_t
8158pm_strspn_decimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8159 const uint8_t *invalid = NULL;
8160 size_t length = pm_strspn_decimal_number(string, parser->end - string, &invalid);
8161 pm_strspn_number_validate(parser, string, length, invalid);
8162 return length;
8163}
8164
8165static size_t
8166pm_strspn_hexadecimal_number_validate(pm_parser_t *parser, const uint8_t *string) {
8167 const uint8_t *invalid = NULL;
8168 size_t length = pm_strspn_hexadecimal_number(string, parser->end - string, &invalid);
8169 pm_strspn_number_validate(parser, string, length, invalid);
8170 return length;
8171}
8172
8173static pm_token_type_t
8174lex_optional_float_suffix(pm_parser_t *parser, bool* seen_e) {
8175 pm_token_type_t type = PM_TOKEN_INTEGER;
8176
8177 // Here we're going to attempt to parse the optional decimal portion of a
8178 // float. If it's not there, then it's okay and we'll just continue on.
8179 if (peek(parser) == '.') {
8180 if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8181 parser->current.end += 2;
8182 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8183 type = PM_TOKEN_FLOAT;
8184 } else {
8185 // If we had a . and then something else, then it's not a float
8186 // suffix on a number it's a method call or something else.
8187 return type;
8188 }
8189 }
8190
8191 // Here we're going to attempt to parse the optional exponent portion of a
8192 // float. If it's not there, it's okay and we'll just continue on.
8193 if ((peek(parser) == 'e') || (peek(parser) == 'E')) {
8194 if ((peek_offset(parser, 1) == '+') || (peek_offset(parser, 1) == '-')) {
8195 parser->current.end += 2;
8196
8197 if (pm_char_is_decimal_digit(peek(parser))) {
8198 parser->current.end++;
8199 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8200 } else {
8201 pm_parser_err_current(parser, PM_ERR_INVALID_FLOAT_EXPONENT);
8202 }
8203 } else if (pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8204 parser->current.end++;
8205 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8206 } else {
8207 return type;
8208 }
8209
8210 *seen_e = true;
8211 type = PM_TOKEN_FLOAT;
8212 }
8213
8214 return type;
8215}
8216
8217static pm_token_type_t
8218lex_numeric_prefix(pm_parser_t *parser, bool* seen_e) {
8219 pm_token_type_t type = PM_TOKEN_INTEGER;
8220 *seen_e = false;
8221
8222 if (peek_offset(parser, -1) == '0') {
8223 switch (*parser->current.end) {
8224 // 0d1111 is a decimal number
8225 case 'd':
8226 case 'D':
8227 parser->current.end++;
8228 if (pm_char_is_decimal_digit(peek(parser))) {
8229 parser->current.end += pm_strspn_decimal_number_validate(parser, parser->current.end);
8230 } else {
8231 match(parser, '_');
8232 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_DECIMAL);
8233 }
8234
8235 break;
8236
8237 // 0b1111 is a binary number
8238 case 'b':
8239 case 'B':
8240 parser->current.end++;
8241 if (pm_char_is_binary_digit(peek(parser))) {
8242 parser->current.end += pm_strspn_binary_number_validate(parser, parser->current.end);
8243 } else {
8244 match(parser, '_');
8245 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_BINARY);
8246 }
8247
8248 parser->integer.base = PM_INTEGER_BASE_FLAGS_BINARY;
8249 break;
8250
8251 // 0o1111 is an octal number
8252 case 'o':
8253 case 'O':
8254 parser->current.end++;
8255 if (pm_char_is_octal_digit(peek(parser))) {
8256 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8257 } else {
8258 match(parser, '_');
8259 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_OCTAL);
8260 }
8261
8262 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8263 break;
8264
8265 // 01111 is an octal number
8266 case '_':
8267 case '0':
8268 case '1':
8269 case '2':
8270 case '3':
8271 case '4':
8272 case '5':
8273 case '6':
8274 case '7':
8275 parser->current.end += pm_strspn_octal_number_validate(parser, parser->current.end);
8276 parser->integer.base = PM_INTEGER_BASE_FLAGS_OCTAL;
8277 break;
8278
8279 // 0x1111 is a hexadecimal number
8280 case 'x':
8281 case 'X':
8282 parser->current.end++;
8283 if (pm_char_is_hexadecimal_digit(peek(parser))) {
8284 parser->current.end += pm_strspn_hexadecimal_number_validate(parser, parser->current.end);
8285 } else {
8286 match(parser, '_');
8287 pm_parser_err_current(parser, PM_ERR_INVALID_NUMBER_HEXADECIMAL);
8288 }
8289
8290 parser->integer.base = PM_INTEGER_BASE_FLAGS_HEXADECIMAL;
8291 break;
8292
8293 // 0.xxx is a float
8294 case '.': {
8295 type = lex_optional_float_suffix(parser, seen_e);
8296 break;
8297 }
8298
8299 // 0exxx is a float
8300 case 'e':
8301 case 'E': {
8302 type = lex_optional_float_suffix(parser, seen_e);
8303 break;
8304 }
8305 }
8306 } else {
8307 // If it didn't start with a 0, then we'll lex as far as we can into a
8308 // decimal number. We compute the integer value inline to avoid
8309 // re-scanning the digits later in pm_integer_parse.
8310 {
8311 const uint8_t *cursor = parser->current.end;
8312 const uint8_t *end = parser->end;
8313 uint64_t value = (uint64_t) (cursor[-1] - '0');
8314
8315 bool has_underscore = false;
8316 bool prev_underscore = false;
8317 const uint8_t *invalid = NULL;
8318
8319 while (cursor < end) {
8320 uint8_t c = *cursor;
8321 if (c >= '0' && c <= '9') {
8322 if (value <= UINT32_MAX) value = value * 10 + (uint64_t) (c - '0');
8323 prev_underscore = false;
8324 cursor++;
8325 } else if (c == '_') {
8326 has_underscore = true;
8327 if (prev_underscore && invalid == NULL) invalid = cursor;
8328 prev_underscore = true;
8329 cursor++;
8330 } else {
8331 break;
8332 }
8333 }
8334
8335 if (has_underscore) {
8336 if (prev_underscore && invalid == NULL) invalid = cursor - 1;
8337 pm_strspn_number_validate(parser, parser->current.end, (size_t) (cursor - parser->current.end), invalid);
8338 }
8339
8340 if (value <= UINT32_MAX) {
8341 parser->integer.value = (uint32_t) value;
8342 parser->integer.lexed = true;
8343 }
8344
8345 parser->current.end = cursor;
8346 }
8347
8348 // Afterward, we'll lex as far as we can into an optional float suffix.
8349 // Guard the function call: the vast majority of decimal numbers are
8350 // plain integers, so avoid the call when the next byte cannot start a
8351 // float suffix.
8352 {
8353 uint8_t next = peek(parser);
8354 if (next == '.' || next == 'e' || next == 'E') {
8355 type = lex_optional_float_suffix(parser, seen_e);
8356
8357 // If it turned out to be a float, the cached integer value is
8358 // invalid.
8359 if (type != PM_TOKEN_INTEGER) {
8360 parser->integer.lexed = false;
8361 }
8362 }
8363 }
8364 }
8365
8366 // At this point we have a completed number, but we want to provide the user
8367 // with a good experience if they put an additional .xxx fractional
8368 // component on the end, so we'll check for that here.
8369 if (peek_offset(parser, 0) == '.' && pm_char_is_decimal_digit(peek_offset(parser, 1))) {
8370 const uint8_t *fraction_start = parser->current.end;
8371 const uint8_t *fraction_end = parser->current.end + 2;
8372 fraction_end += pm_strspn_decimal_digit(fraction_end, parser->end - fraction_end);
8373 pm_parser_err(parser, U32(fraction_start - parser->start), U32(fraction_end - fraction_start), PM_ERR_INVALID_NUMBER_FRACTION);
8374 }
8375
8376 return type;
8377}
8378
8379static pm_token_type_t
8380lex_numeric(pm_parser_t *parser) {
8381 pm_token_type_t type = PM_TOKEN_INTEGER;
8382 parser->integer.base = PM_INTEGER_BASE_FLAGS_DECIMAL;
8383 parser->integer.lexed = false;
8384
8385 if (parser->current.end < parser->end) {
8386 bool seen_e = false;
8387 type = lex_numeric_prefix(parser, &seen_e);
8388
8389 const uint8_t *end = parser->current.end;
8390 pm_token_type_t suffix_type = type;
8391
8392 if (type == PM_TOKEN_INTEGER) {
8393 if (match(parser, 'r')) {
8394 suffix_type = PM_TOKEN_INTEGER_RATIONAL;
8395
8396 if (match(parser, 'i')) {
8397 suffix_type = PM_TOKEN_INTEGER_RATIONAL_IMAGINARY;
8398 }
8399 } else if (match(parser, 'i')) {
8400 suffix_type = PM_TOKEN_INTEGER_IMAGINARY;
8401 }
8402 } else {
8403 if (!seen_e && match(parser, 'r')) {
8404 suffix_type = PM_TOKEN_FLOAT_RATIONAL;
8405
8406 if (match(parser, 'i')) {
8407 suffix_type = PM_TOKEN_FLOAT_RATIONAL_IMAGINARY;
8408 }
8409 } else if (match(parser, 'i')) {
8410 suffix_type = PM_TOKEN_FLOAT_IMAGINARY;
8411 }
8412 }
8413
8414 const uint8_t b = peek(parser);
8415 if (b != '\0' && (b >= 0x80 || ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) || b == '_')) {
8416 parser->current.end = end;
8417 } else {
8418 type = suffix_type;
8419 }
8420 }
8421
8422 return type;
8423}
8424
8425static pm_token_type_t
8426lex_global_variable(pm_parser_t *parser) {
8427 if (parser->current.end >= parser->end) {
8428 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8429 return PM_TOKEN_GLOBAL_VARIABLE;
8430 }
8431
8432 // True if multiple characters are allowed after the declaration of the
8433 // global variable. Not true when it starts with "$-".
8434 bool allow_multiple = true;
8435
8436 switch (*parser->current.end) {
8437 case '~': // $~: match-data
8438 case '*': // $*: argv
8439 case '$': // $$: pid
8440 case '?': // $?: last status
8441 case '!': // $!: error string
8442 case '@': // $@: error position
8443 case '/': // $/: input record separator
8444 case '\\': // $\: output record separator
8445 case ';': // $;: field separator
8446 case ',': // $,: output field separator
8447 case '.': // $.: last read line number
8448 case '=': // $=: ignorecase
8449 case ':': // $:: load path
8450 case '<': // $<: reading filename
8451 case '>': // $>: default output handle
8452 case '\"': // $": already loaded files
8453 parser->current.end++;
8454 return PM_TOKEN_GLOBAL_VARIABLE;
8455
8456 case '&': // $&: last match
8457 case '`': // $`: string before last match
8458 case '\'': // $': string after last match
8459 case '+': // $+: string matches last paren.
8460 parser->current.end++;
8461 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_BACK_REFERENCE;
8462
8463 case '0': {
8464 parser->current.end++;
8465 size_t width;
8466
8467 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8468 do {
8469 parser->current.end += width;
8470 } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8471
8472 // $0 isn't allowed to be followed by anything.
8473 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;
8474 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, diag_id);
8475 }
8476
8477 return PM_TOKEN_GLOBAL_VARIABLE;
8478 }
8479
8480 case '1':
8481 case '2':
8482 case '3':
8483 case '4':
8484 case '5':
8485 case '6':
8486 case '7':
8487 case '8':
8488 case '9':
8489 parser->current.end += pm_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end);
8490 return lex_state_p(parser, PM_LEX_STATE_FNAME) ? PM_TOKEN_GLOBAL_VARIABLE : PM_TOKEN_NUMBERED_REFERENCE;
8491
8492 case '-':
8493 parser->current.end++;
8494 allow_multiple = false;
8496 default: {
8497 size_t width;
8498
8499 if ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0) {
8500 do {
8501 parser->current.end += width;
8502 } while (allow_multiple && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
8503 } else if (pm_char_is_whitespace(peek(parser))) {
8504 // If we get here, then we have a $ followed by whitespace,
8505 // which is not allowed.
8506 pm_parser_err_token(parser, &parser->current, PM_ERR_GLOBAL_VARIABLE_BARE);
8507 } else {
8508 // If we get here, then we have a $ followed by something that
8509 // isn't recognized as a global variable.
8510 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;
8511 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
8512 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);
8513 }
8514
8515 return PM_TOKEN_GLOBAL_VARIABLE;
8516 }
8517 }
8518}
8519
8532static PRISM_INLINE pm_token_type_t
8533lex_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) {
8534 if (memcmp(current_start, value, vlen) == 0) {
8535 pm_lex_state_t last_state = parser->lex_state;
8536
8537 if (parser->lex_state & PM_LEX_STATE_FNAME) {
8538 lex_state_set(parser, PM_LEX_STATE_ENDFN);
8539 } else {
8540 lex_state_set(parser, state);
8541 if (state == PM_LEX_STATE_BEG) {
8542 parser->command_start = true;
8543 }
8544
8545 if ((modifier_type != PM_TOKEN_EOF) && !(last_state & (PM_LEX_STATE_BEG | PM_LEX_STATE_LABELED | PM_LEX_STATE_CLASS))) {
8546 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
8547 return modifier_type;
8548 }
8549 }
8550
8551 return type;
8552 }
8553
8554 return PM_TOKEN_EOF;
8555}
8556
8557static pm_token_type_t
8558lex_identifier(pm_parser_t *parser, bool previous_command_start) {
8559 // Lex as far as we can into the current identifier.
8560 size_t width;
8561 const uint8_t *end = parser->end;
8562 const uint8_t *current_start = parser->current.start;
8563 const uint8_t *current_end = parser->current.end;
8564 bool encoding_changed = parser->encoding_changed;
8565
8566 if (encoding_changed) {
8567 while ((width = char_is_identifier(parser, current_end, end - current_end)) > 0) {
8568 current_end += width;
8569 }
8570 } else {
8571 // Fast path: scan ASCII identifier bytes using wide operations.
8572 current_end += scan_identifier_ascii(current_end, end);
8573
8574 // Byte-at-a-time fallback for the tail and any UTF-8 sequences.
8575 while ((width = char_is_identifier_utf8(current_end, end - current_end)) > 0) {
8576 current_end += width;
8577 }
8578 }
8579 parser->current.end = current_end;
8580
8581 // Now cache the length of the identifier so that we can quickly compare it
8582 // against known keywords.
8583 width = (size_t) (current_end - current_start);
8584
8585 if (current_end < end) {
8586 if (((current_end + 1 >= end) || (current_end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) {
8587 // First we'll attempt to extend the identifier by a ! or ?. Then we'll
8588 // check if we're returning the defined? keyword or just an identifier.
8589 width++;
8590
8591 if (
8592 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8593 (peek(parser) == ':') && (peek_offset(parser, 1) != ':')
8594 ) {
8595 // If we're in a position where we can accept a : at the end of an
8596 // identifier, then we'll optionally accept it.
8597 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8598 (void) match(parser, ':');
8599
8600 /* A label is a symbol lexed inline rather than through a lex
8601 * mode, so it clears the encoding here. */
8602 parser->explicit_encoding = NULL;
8603 return PM_TOKEN_LABEL;
8604 }
8605
8606 if (parser->lex_state != PM_LEX_STATE_DOT) {
8607 if (width == 8 && (lex_keyword(parser, current_start, "defined?", width, PM_LEX_STATE_ARG, PM_TOKEN_KEYWORD_DEFINED, PM_TOKEN_EOF) != PM_TOKEN_EOF)) {
8608 return PM_TOKEN_KEYWORD_DEFINED;
8609 }
8610 }
8611
8612 return PM_TOKEN_METHOD_NAME;
8613 }
8614
8615 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, '=')) {
8616 // If we're in a position where we can accept a = at the end of an
8617 // identifier, then we'll optionally accept it.
8618 return PM_TOKEN_IDENTIFIER;
8619 }
8620
8621 if (
8622 ((lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
8623 peek(parser) == ':' && peek_offset(parser, 1) != ':'
8624 ) {
8625 // If we're in a position where we can accept a : at the end of an
8626 // identifier, then we'll optionally accept it.
8627 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
8628 (void) match(parser, ':');
8629
8630 /* A label is a symbol lexed inline rather than through a lex
8631 * mode, so it clears the encoding here. */
8632 parser->explicit_encoding = NULL;
8633 return PM_TOKEN_LABEL;
8634 }
8635 }
8636
8637 if (parser->lex_state != PM_LEX_STATE_DOT) {
8638 pm_token_type_t type;
8639
8640 /* The lex state from before lex_keyword transitions it, mirroring the
8641 * `state = p->lex.state` capture in parse.y's keyword handling. */
8642 pm_lex_state_t previous_lex_state = parser->lex_state;
8643
8644 switch (width) {
8645 case 2:
8646 if (lex_keyword(parser, current_start, "do", width, PM_LEX_STATE_BEG, PM_TOKEN_KEYWORD_DO, PM_TOKEN_EOF) != PM_TOKEN_EOF) {
8647 /* In FNAME position (a symbol like `:do` or a method name
8648 * like `def do`), `do` is a plain name rather than a
8649 * block, loop, or lambda opener, so none of the
8650 * discrimination below applies. This mirrors parse.y,
8651 * whose EXPR_FNAME early-return precedes all of the
8652 * keyword_do special-casing (and never touches
8653 * lpar_beg). */
8654 if (previous_lex_state & PM_LEX_STATE_FNAME) {
8655 return PM_TOKEN_KEYWORD_DO;
8656 }
8657 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
8658 // At the bare nesting level of a lambda literal (no
8659 // delimiter opened since `->`), a `do` opens the lambda
8660 // body. This is a distinct token so that a command in a
8661 // parameter default cannot consume it as its own block
8662 // (`-> a = foo do end` is `->(a = foo) do end`). It
8663 // mirrors CRuby's keyword_do_LAMBDA.
8664 //
8665 // Clear the nesting so that no token within the
8666 // `do`/`end` body is considered to be at the beginning
8667 // of a lambda; the parser restores the enclosing value
8668 // once the lambda has been fully parsed. This mirrors
8669 // parse.y setting `p->lex.lpar_beg = -1` when lexing
8670 // keyword_do_LAMBDA.
8671 parser->lambda_enclosure_nesting = -1;
8672 return PM_TOKEN_KEYWORD_DO_LAMBDA;
8673 }
8674 if (pm_do_loop_stack_p(parser)) {
8675 return PM_TOKEN_KEYWORD_DO_LOOP;
8676 }
8677 if (!pm_accepts_block_stack_p(parser)) {
8678 return PM_TOKEN_KEYWORD_DO_BLOCK;
8679 }
8680 return PM_TOKEN_KEYWORD_DO;
8681 }
8682
8683 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;
8684 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;
8685 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;
8686 break;
8687 case 3:
8688 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;
8689 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;
8690 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;
8691 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;
8692 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;
8693 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;
8694 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;
8695 break;
8696 case 4:
8697 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;
8698 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;
8699 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;
8700 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;
8701 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;
8702 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;
8703 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;
8704 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;
8705 break;
8706 case 5:
8707 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;
8708 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;
8709 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;
8710 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;
8711 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;
8712 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;
8713 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;
8714 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;
8715 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;
8716 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;
8717 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;
8718 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;
8719 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;
8720 break;
8721 case 6:
8722 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;
8723 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;
8724 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;
8725 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;
8726 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;
8727 break;
8728 case 8:
8729 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;
8730 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;
8731 break;
8732 case 12:
8733 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;
8734 break;
8735 }
8736 }
8737
8738 if (encoding_changed) {
8739 return parser->encoding->isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8740 }
8741
8742 /* Identifiers usually start with an ASCII byte, for which the uppercase
8743 * check is a simple range comparison. This avoids the call into the
8744 * encoding module for every identifier. */
8745 if (*current_start < 0x80) {
8746 return (*current_start >= 'A' && *current_start <= 'Z') ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8747 }
8748 return pm_encoding_utf_8_isupper_char(current_start, end - current_start) ? PM_TOKEN_CONSTANT : PM_TOKEN_IDENTIFIER;
8749}
8750
8755static bool
8756current_token_starts_line(pm_parser_t *parser) {
8757 return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n');
8758}
8759
8774static pm_token_type_t
8775lex_interpolation(pm_parser_t *parser, const uint8_t *pound) {
8776 // If there is no content following this #, then we're at the end of
8777 // the string and we can safely return string content.
8778 if (pound + 1 >= parser->end) {
8779 parser->current.end = pound + 1;
8780 return PM_TOKEN_STRING_CONTENT;
8781 }
8782
8783 // Now we'll check against the character that follows the #. If it
8784 // constitutes valid interplation, we'll handle that, otherwise we'll return
8785 // 0.
8786 switch (pound[1]) {
8787 case '@': {
8788 // In this case we may have hit an embedded instance or class variable.
8789 if (pound + 2 >= parser->end) {
8790 parser->current.end = pound + 1;
8791 return PM_TOKEN_STRING_CONTENT;
8792 }
8793
8794 // If we're looking at a @ and there's another @, then we'll skip past the
8795 // second @.
8796 const uint8_t *variable = pound + 2;
8797 if (*variable == '@' && pound + 3 < parser->end) variable++;
8798
8799 if (char_is_identifier_start(parser, variable, parser->end - variable)) {
8800 // At this point we're sure that we've either hit an embedded instance
8801 // or class variable. In this case we'll first need to check if we've
8802 // already consumed content.
8803 if (pound > parser->current.start) {
8804 parser->current.end = pound;
8805 return PM_TOKEN_STRING_CONTENT;
8806 }
8807
8808 // Otherwise we need to return the embedded variable token
8809 // and then switch to the embedded variable lex mode.
8810 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8811 parser->current.end = pound + 1;
8812 return PM_TOKEN_EMBVAR;
8813 }
8814
8815 // If we didn't get a valid interpolation, then this is just regular
8816 // string content. This is like if we get "#@-". In this case the caller
8817 // should keep lexing.
8818 parser->current.end = pound + 1;
8819 return 0;
8820 }
8821 case '$':
8822 // In this case we may have hit an embedded global variable. If there's
8823 // not enough room, then we'll just return string content.
8824 if (pound + 2 >= parser->end) {
8825 parser->current.end = pound + 1;
8826 return PM_TOKEN_STRING_CONTENT;
8827 }
8828
8829 // This is the character that we're going to check to see if it is the
8830 // start of an identifier that would indicate that this is a global
8831 // variable.
8832 const uint8_t *check = pound + 2;
8833
8834 if (pound[2] == '-') {
8835 if (pound + 3 >= parser->end) {
8836 parser->current.end = pound + 2;
8837 return PM_TOKEN_STRING_CONTENT;
8838 }
8839
8840 check++;
8841 }
8842
8843 // If the character that we're going to check is the start of an
8844 // identifier, or we don't have a - and the character is a decimal number
8845 // or a global name punctuation character, then we've hit an embedded
8846 // global variable.
8847 if (
8848 char_is_identifier_start(parser, check, parser->end - check) ||
8849 (pound[2] != '-' && (pm_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2])))
8850 ) {
8851 // In this case we've hit an embedded global variable. First check to
8852 // see if we've already consumed content. If we have, then we need to
8853 // return that content as string content first.
8854 if (pound > parser->current.start) {
8855 parser->current.end = pound;
8856 return PM_TOKEN_STRING_CONTENT;
8857 }
8858
8859 // Otherwise, we need to return the embedded variable token and switch
8860 // to the embedded variable lex mode.
8861 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBVAR });
8862 parser->current.end = pound + 1;
8863 return PM_TOKEN_EMBVAR;
8864 }
8865
8866 // In this case we've hit a #$ that does not indicate a global variable.
8867 // In this case we'll continue lexing past it.
8868 parser->current.end = pound + 1;
8869 return 0;
8870 case '{':
8871 // In this case it's the start of an embedded expression. If we have
8872 // already consumed content, then we need to return that content as string
8873 // content first.
8874 if (pound > parser->current.start) {
8875 parser->current.end = pound;
8876 return PM_TOKEN_STRING_CONTENT;
8877 }
8878
8879 parser->enclosure_nesting++;
8880
8881 // Otherwise we'll skip past the #{ and begin lexing the embedded
8882 // expression.
8883 lex_mode_push(parser, (pm_lex_mode_t) { .mode = PM_LEX_EMBEXPR });
8884 parser->current.end = pound + 2;
8885 parser->command_start = true;
8886 pm_enclosure_frame_push(parser);
8887 return PM_TOKEN_EMBEXPR_BEGIN;
8888 default:
8889 // In this case we've hit a # that doesn't constitute interpolation. We'll
8890 // mark that by returning the not provided token type. This tells the
8891 // consumer to keep lexing forward.
8892 parser->current.end = pound + 1;
8893 return 0;
8894 }
8895}
8896
8897static const uint8_t PM_ESCAPE_FLAG_NONE = 0x0;
8898static const uint8_t PM_ESCAPE_FLAG_CONTROL = 0x1;
8899static const uint8_t PM_ESCAPE_FLAG_META = 0x2;
8900static const uint8_t PM_ESCAPE_FLAG_SINGLE = 0x4;
8901static const uint8_t PM_ESCAPE_FLAG_REGEXP = 0x8;
8902
8906static const bool ascii_printable_chars[] = {
8907 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
8908 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8909 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8910 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8911 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
8912 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 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, 0
8915};
8916
8917static PRISM_INLINE bool
8918char_is_ascii_printable(const uint8_t b) {
8919 return (b < 0x80) && ascii_printable_chars[b];
8920}
8921
8926static PRISM_INLINE uint8_t
8927escape_hexadecimal_digit(const uint8_t value) {
8928 return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9);
8929}
8930
8936static PRISM_INLINE uint32_t
8937escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location, const uint8_t flags) {
8938 uint32_t value = 0;
8939 for (size_t index = 0; index < length; index++) {
8940 if (index != 0) value <<= 4;
8941 value |= escape_hexadecimal_digit(string[index]);
8942 }
8943
8944 // Here we're going to verify that the value is actually a valid Unicode
8945 // codepoint and not a surrogate pair.
8946 if (value >= 0xD800 && value <= 0xDFFF) {
8947 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8948 // In regexp context, defer the error to regexp encoding
8949 // validation where we can produce a regexp-specific message.
8950 } else if (error_location != NULL) {
8951 pm_parser_err(parser, error_location->start, error_location->length, PM_ERR_ESCAPE_INVALID_UNICODE);
8952 } else {
8953 pm_parser_err(parser, U32(string - parser->start), U32(length), PM_ERR_ESCAPE_INVALID_UNICODE);
8954 }
8955 return 0xFFFD;
8956 }
8957
8958 return value;
8959}
8960
8964static PRISM_INLINE uint8_t
8965escape_byte(uint8_t value, const uint8_t flags) {
8966 if (flags & PM_ESCAPE_FLAG_CONTROL) value &= 0x9f;
8967 if (flags & PM_ESCAPE_FLAG_META) value |= 0x80;
8968 return value;
8969}
8970
8974static PRISM_INLINE void
8975escape_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) {
8976 // \u escape sequences in string-like structures implicitly change the
8977 // encoding to UTF-8 if they are >= 0x80 or if they are used in a character
8978 // literal.
8979 if (value >= 0x80 || flags & PM_ESCAPE_FLAG_SINGLE) {
8980 if (parser->explicit_encoding != NULL && parser->explicit_encoding != PM_ENCODING_UTF_8_ENTRY) {
8981 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8982 // In regexp context, suppress this error — the regexp encoding
8983 // validation will produce a more specific error message.
8984 } else {
8985 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(end - start), PM_ERR_MIXED_ENCODING, parser->explicit_encoding->name);
8986 }
8987 }
8988
8989 parser->explicit_encoding = PM_ENCODING_UTF_8_ENTRY;
8990 }
8991
8992 if (!pm_buffer_append_unicode_codepoint(buffer, value)) {
8993 if (flags & PM_ESCAPE_FLAG_REGEXP) {
8994 // In regexp context, defer the error to the regexp encoding
8995 // validation which produces a regexp-specific message.
8996 } else {
8997 pm_parser_err(parser, U32(start - parser->start), U32(end - start), PM_ERR_ESCAPE_INVALID_UNICODE);
8998 }
8999
9000 pm_buffer_append_byte(buffer, 0xEF);
9001 pm_buffer_append_byte(buffer, 0xBF);
9002 pm_buffer_append_byte(buffer, 0xBD);
9003 }
9004}
9005
9010static PRISM_INLINE void
9011escape_write_byte_encoded(pm_parser_t *parser, pm_buffer_t *buffer, const uint8_t flags, uint8_t byte) {
9012 if (byte >= 0x80) {
9013 if (parser->explicit_encoding != NULL && parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY && parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
9014 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9015 // In regexp context, suppress this error — the regexp encoding
9016 // validation will produce a more specific error message.
9017 } else {
9018 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_MIXED_ENCODING, parser->encoding->name);
9019 }
9020 }
9021
9022 parser->explicit_encoding = parser->encoding;
9023 }
9024
9025 pm_buffer_append_byte(buffer, byte);
9026}
9027
9043static PRISM_INLINE void
9044escape_write_byte(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags, uint8_t byte) {
9045 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9046 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", byte);
9047 }
9048
9049 escape_write_byte_encoded(parser, buffer, flags, byte);
9050}
9051
9055static PRISM_INLINE void
9056escape_write_escape_encoded(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9057 size_t width;
9058 if (parser->encoding_changed) {
9059 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9060 } else {
9061 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9062 }
9063
9064 if (width == 1) {
9065 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);
9066 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(*parser->current.end++, flags));
9067 } else if (width > 1) {
9068 // Valid multibyte character. Just ignore escape.
9069 pm_buffer_t *b = (flags & PM_ESCAPE_FLAG_REGEXP) ? regular_expression_buffer : buffer;
9070 pm_buffer_append_bytes(b, parser->current.end, width);
9071 parser->current.end += width;
9072 } else {
9073 // Assume the next character wasn't meant to be part of this escape
9074 // sequence since it is invalid. Add an error and move on.
9075 parser->current.end++;
9076 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9077 }
9078}
9079
9085static void
9086escape_read_warn(pm_parser_t *parser, uint8_t flags, uint8_t flag, const char *type) {
9087#define FLAG(value) ((value & PM_ESCAPE_FLAG_CONTROL) ? "\\C-" : (value & PM_ESCAPE_FLAG_META) ? "\\M-" : "")
9088
9089 PM_PARSER_WARN_TOKEN_FORMAT(
9090 parser,
9091 &parser->current,
9092 PM_WARN_INVALID_CHARACTER,
9093 FLAG(flags),
9094 FLAG(flag),
9095 type
9096 );
9097
9098#undef FLAG
9099}
9100
9104static void
9105escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expression_buffer, uint8_t flags) {
9106 uint8_t peeked = peek(parser);
9107 switch (peeked) {
9108 case '\\': {
9109 parser->current.end++;
9110 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\\', flags));
9111 return;
9112 }
9113 case '\'': {
9114 parser->current.end++;
9115 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\'', flags));
9116 return;
9117 }
9118 case 'a': {
9119 parser->current.end++;
9120 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\a', flags));
9121 return;
9122 }
9123 case 'b': {
9124 parser->current.end++;
9125 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\b', flags));
9126 return;
9127 }
9128 case 'e': {
9129 parser->current.end++;
9130 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\033', flags));
9131 return;
9132 }
9133 case 'f': {
9134 parser->current.end++;
9135 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\f', flags));
9136 return;
9137 }
9138 case 'n': {
9139 parser->current.end++;
9140 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\n', flags));
9141 return;
9142 }
9143 case 'r': {
9144 parser->current.end++;
9145 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\r', flags));
9146 return;
9147 }
9148 case 's': {
9149 parser->current.end++;
9150 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(' ', flags));
9151 return;
9152 }
9153 case 't': {
9154 parser->current.end++;
9155 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\t', flags));
9156 return;
9157 }
9158 case 'v': {
9159 parser->current.end++;
9160 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte('\v', flags));
9161 return;
9162 }
9163 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': {
9164 uint8_t value = (uint8_t) (*parser->current.end - '0');
9165 parser->current.end++;
9166
9167 if (pm_char_is_octal_digit(peek(parser))) {
9168 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9169 parser->current.end++;
9170
9171 if (pm_char_is_octal_digit(peek(parser))) {
9172 value = ((uint8_t) (value << 3)) | ((uint8_t) (*parser->current.end - '0'));
9173 parser->current.end++;
9174 }
9175 }
9176
9177 value = escape_byte(value, flags);
9178 escape_write_byte(parser, buffer, regular_expression_buffer, flags, value);
9179 return;
9180 }
9181 case 'x': {
9182 const uint8_t *start = parser->current.end - 1;
9183
9184 parser->current.end++;
9185 uint8_t byte = peek(parser);
9186
9187 if (pm_char_is_hexadecimal_digit(byte)) {
9188 uint8_t value = escape_hexadecimal_digit(byte);
9189 parser->current.end++;
9190
9191 byte = peek(parser);
9192 if (pm_char_is_hexadecimal_digit(byte)) {
9193 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(byte));
9194 parser->current.end++;
9195 }
9196
9197 value = escape_byte(value, flags);
9198 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9199 if (flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) {
9200 pm_buffer_append_format(regular_expression_buffer, "\\x%02X", value);
9201 } else {
9202 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9203 }
9204 }
9205
9206 escape_write_byte_encoded(parser, buffer, flags, value);
9207 } else {
9208 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_HEXADECIMAL);
9209 }
9210
9211 return;
9212 }
9213 case 'u': {
9214 const uint8_t *start = parser->current.end - 1;
9215 parser->current.end++;
9216
9217 if (parser->current.end == parser->end) {
9218 const uint8_t *start = parser->current.end - 2;
9219 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9220 } else if (peek(parser) == '{') {
9221 const uint8_t *unicode_codepoints_start = parser->current.end - 2;
9222 parser->current.end++;
9223
9224 size_t whitespace;
9225 while (true) {
9226 if ((whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end)) > 0) {
9227 parser->current.end += whitespace;
9228 } else if (peek(parser) == '\\' && peek_offset(parser, 1) == 'n') {
9229 // This is super hacky, but it gets us nicer error
9230 // messages because we can still pass it off to the
9231 // regular expression engine even if we hit an
9232 // unterminated regular expression.
9233 parser->current.end += 2;
9234 } else {
9235 break;
9236 }
9237 }
9238
9239 const uint8_t *extra_codepoints_start = NULL;
9240 int codepoints_count = 0;
9241
9242 while ((parser->current.end < parser->end) && (*parser->current.end != '}')) {
9243 const uint8_t *unicode_start = parser->current.end;
9244 size_t hexadecimal_length = pm_strspn_hexadecimal_digit(parser->current.end, parser->end - parser->current.end);
9245
9246 if (hexadecimal_length > 6) {
9247 // \u{nnnn} character literal allows only 1-6 hexadecimal digits
9248 pm_parser_err(parser, U32(unicode_start - parser->start), U32(hexadecimal_length), PM_ERR_ESCAPE_INVALID_UNICODE_LONG);
9249 } else if (hexadecimal_length == 0) {
9250 // there are not hexadecimal characters
9251
9252 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9253 // If this is a regular expression, we are going to
9254 // let the regular expression engine handle this
9255 // error instead of us because we don't know at this
9256 // point if we're inside a comment in /x mode.
9257 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9258 } else {
9259 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE);
9260 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->current), 0, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9261 }
9262
9263 return;
9264 }
9265
9266 parser->current.end += hexadecimal_length;
9267 codepoints_count++;
9268 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count == 2) {
9269 extra_codepoints_start = unicode_start;
9270 }
9271
9272 uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL, flags);
9273 escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value);
9274
9275 parser->current.end += pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
9276 }
9277
9278 // ?\u{nnnn} character literal should contain only one codepoint
9279 // and cannot be like ?\u{nnnn mmmm}.
9280 if (flags & PM_ESCAPE_FLAG_SINGLE && codepoints_count > 1) {
9281 pm_parser_err(parser, U32(extra_codepoints_start - parser->start), U32(parser->current.end - 1 - extra_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL);
9282 }
9283
9284 if (parser->current.end == parser->end) {
9285 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);
9286 } else if (peek(parser) == '}') {
9287 parser->current.end++;
9288 } else {
9289 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9290 // If this is a regular expression, we are going to let
9291 // the regular expression engine handle this error
9292 // instead of us because we don't know at this point if
9293 // we're inside a comment in /x mode.
9294 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9295 } else {
9296 pm_parser_err(parser, U32(unicode_codepoints_start - parser->start), U32(parser->current.end - unicode_codepoints_start), PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
9297 }
9298 }
9299
9300 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9301 pm_buffer_append_bytes(regular_expression_buffer, unicode_codepoints_start, (size_t) (parser->current.end - unicode_codepoints_start));
9302 }
9303 } else {
9304 size_t length = pm_strspn_hexadecimal_digit(parser->current.end, MIN(parser->end - parser->current.end, 4));
9305
9306 if (length == 0) {
9307 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9308 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9309 } else {
9310 const uint8_t *start = parser->current.end - 2;
9311 PM_PARSER_ERR_FORMAT(parser, U32(start - parser->start), U32(parser->current.end - start), PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start);
9312 }
9313 } else if (length == 4) {
9314 uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL, flags);
9315
9316 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9317 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start));
9318 }
9319
9320 escape_write_unicode(parser, buffer, flags, start, parser->current.end + 4, value);
9321 parser->current.end += 4;
9322 } else {
9323 parser->current.end += length;
9324
9325 if (flags & PM_ESCAPE_FLAG_REGEXP) {
9326 // If this is a regular expression, we are going to let
9327 // the regular expression engine handle this error
9328 // instead of us.
9329 pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end - start));
9330 } else {
9331 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_UNICODE);
9332 }
9333 }
9334 }
9335
9336 return;
9337 }
9338 case 'c': {
9339 parser->current.end++;
9340 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9341 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9342 }
9343
9344 if (parser->current.end == parser->end) {
9345 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9346 return;
9347 }
9348
9349 uint8_t peeked = peek(parser);
9350 switch (peeked) {
9351 case '?': {
9352 parser->current.end++;
9353 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9354 return;
9355 }
9356 case '\\':
9357 parser->current.end++;
9358
9359 if (match(parser, 'u') || match(parser, 'U')) {
9360 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9361 return;
9362 }
9363
9364 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9365 return;
9366 case ' ':
9367 parser->current.end++;
9368 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9369 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9370 return;
9371 case '\t':
9372 parser->current.end++;
9373 escape_read_warn(parser, flags, 0, "\\t");
9374 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9375 return;
9376 default: {
9377 if (!char_is_ascii_printable(peeked)) {
9378 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9379 return;
9380 }
9381
9382 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);
9383 parser->current.end++;
9384 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9385 return;
9386 }
9387 }
9388 }
9389 case 'C': {
9390 parser->current.end++;
9391 if (flags & PM_ESCAPE_FLAG_CONTROL) {
9392 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
9393 }
9394
9395 if (peek(parser) != '-') {
9396 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9397 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9398 return;
9399 }
9400
9401 parser->current.end++;
9402 if (parser->current.end == parser->end) {
9403 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_CONTROL);
9404 return;
9405 }
9406
9407 uint8_t peeked = peek(parser);
9408 switch (peeked) {
9409 case '?': {
9410 parser->current.end++;
9411 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(0x7f, flags));
9412 return;
9413 }
9414 case '\\':
9415 parser->current.end++;
9416
9417 if (match(parser, 'u') || match(parser, 'U')) {
9418 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9419 return;
9420 }
9421
9422 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_CONTROL);
9423 return;
9424 case ' ':
9425 parser->current.end++;
9426 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_CONTROL, "\\s");
9427 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9428 return;
9429 case '\t':
9430 parser->current.end++;
9431 escape_read_warn(parser, flags, 0, "\\t");
9432 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9433 return;
9434 default: {
9435 if (!char_is_ascii_printable(peeked)) {
9436 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9437 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_CONTROL);
9438 return;
9439 }
9440
9441 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);
9442 parser->current.end++;
9443 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_CONTROL));
9444 return;
9445 }
9446 }
9447 }
9448 case 'M': {
9449 parser->current.end++;
9450 if (flags & PM_ESCAPE_FLAG_META) {
9451 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META_REPEAT);
9452 }
9453
9454 if (peek(parser) != '-') {
9455 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9456 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9457 return;
9458 }
9459
9460 parser->current.end++;
9461 if (parser->current.end == parser->end) {
9462 pm_parser_err_current(parser, PM_ERR_ESCAPE_INVALID_META);
9463 return;
9464 }
9465
9466 uint8_t peeked = peek(parser);
9467 switch (peeked) {
9468 case '\\':
9469 parser->current.end++;
9470
9471 if (match(parser, 'u') || match(parser, 'U')) {
9472 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current), PM_ERR_INVALID_ESCAPE_CHARACTER);
9473 return;
9474 }
9475
9476 escape_read(parser, buffer, regular_expression_buffer, flags | PM_ESCAPE_FLAG_META);
9477 return;
9478 case ' ':
9479 parser->current.end++;
9480 escape_read_warn(parser, flags, PM_ESCAPE_FLAG_META, "\\s");
9481 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9482 return;
9483 case '\t':
9484 parser->current.end++;
9485 escape_read_warn(parser, flags & ((uint8_t) ~PM_ESCAPE_FLAG_CONTROL), PM_ESCAPE_FLAG_META, "\\t");
9486 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9487 return;
9488 default:
9489 if (!char_is_ascii_printable(peeked)) {
9490 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9491 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9492 return;
9493 }
9494
9495 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);
9496 parser->current.end++;
9497 escape_write_byte(parser, buffer, regular_expression_buffer, flags, escape_byte(peeked, flags | PM_ESCAPE_FLAG_META));
9498 return;
9499 }
9500 }
9501 case '\r': {
9502 if (peek_offset(parser, 1) == '\n') {
9503 if (parser->heredoc_end == NULL) pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 2);
9504 parser->current.end += 2;
9505 escape_write_byte_encoded(parser, buffer, flags, escape_byte('\n', flags));
9506 return;
9507 }
9509 }
9510 default: {
9511 if ((flags & (PM_ESCAPE_FLAG_CONTROL | PM_ESCAPE_FLAG_META)) && !char_is_ascii_printable(peeked)) {
9512 size_t width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9513 pm_parser_err(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current) + U32(width), PM_ERR_ESCAPE_INVALID_META);
9514 return;
9515 }
9516 if (parser->current.end < parser->end) {
9517 escape_write_escape_encoded(parser, buffer, regular_expression_buffer, flags);
9518 } else {
9519 pm_parser_err_current(parser, PM_ERR_INVALID_ESCAPE_CHARACTER);
9520 }
9521 return;
9522 }
9523 }
9524}
9525
9551static pm_token_type_t
9552lex_question_mark(pm_parser_t *parser) {
9553 if (lex_state_end_p(parser)) {
9554 lex_state_set(parser, PM_LEX_STATE_BEG);
9555 return PM_TOKEN_QUESTION_MARK;
9556 }
9557
9558 /*
9559 * A literal takes its encoding from its own contents. Literals that push a
9560 * lex mode clear this in lex_mode_push_*; a character literal is lexed
9561 * inline, so it clears the encoding here.
9562 */
9563 parser->explicit_encoding = NULL;
9564
9565 if (parser->current.end >= parser->end) {
9566 pm_parser_err_current(parser, PM_ERR_INCOMPLETE_QUESTION_MARK);
9567 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9568 return PM_TOKEN_CHARACTER_LITERAL;
9569 }
9570
9571 if (pm_char_is_whitespace(*parser->current.end)) {
9572 lex_state_set(parser, PM_LEX_STATE_BEG);
9573 return PM_TOKEN_QUESTION_MARK;
9574 }
9575
9576 lex_state_set(parser, PM_LEX_STATE_BEG);
9577
9578 if (match(parser, '\\')) {
9579 lex_state_set(parser, PM_LEX_STATE_END);
9580
9581 pm_buffer_t buffer;
9582 pm_buffer_init(&buffer, 3);
9583
9584 escape_read(parser, &buffer, NULL, PM_ESCAPE_FLAG_SINGLE);
9585
9586 // Copy buffer data into the arena and free the heap buffer.
9587 void *arena_data = pm_arena_memdup(parser->arena, buffer.value, buffer.length, PRISM_ALIGNOF(uint8_t));
9588 pm_string_constant_init(&parser->current_string, (const char *) arena_data, buffer.length);
9589 pm_buffer_cleanup(&buffer);
9590
9591 return PM_TOKEN_CHARACTER_LITERAL;
9592 } else {
9593 size_t encoding_width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9594
9595 // Ternary operators can have a ? immediately followed by an identifier
9596 // which starts with an underscore. We check for this case here.
9597 if (
9598 !(parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end) || peek(parser) == '_') ||
9599 (
9600 (parser->current.end + encoding_width >= parser->end) ||
9601 !char_is_identifier(parser, parser->current.end + encoding_width, parser->end - (parser->current.end + encoding_width))
9602 )
9603 ) {
9604 lex_state_set(parser, PM_LEX_STATE_END);
9605 parser->current.end += encoding_width;
9606 pm_string_shared_init(&parser->current_string, parser->current.start + 1, parser->current.end);
9607 return PM_TOKEN_CHARACTER_LITERAL;
9608 }
9609 }
9610
9611 return PM_TOKEN_QUESTION_MARK;
9612}
9613
9618static pm_token_type_t
9619lex_at_variable(pm_parser_t *parser) {
9620 pm_token_type_t type = match(parser, '@') ? PM_TOKEN_CLASS_VARIABLE : PM_TOKEN_INSTANCE_VARIABLE;
9621 const uint8_t *end = parser->end;
9622
9623 size_t width;
9624 if ((width = char_is_identifier_start(parser, parser->current.end, end - parser->current.end)) > 0) {
9625 parser->current.end += width;
9626
9627 while ((width = char_is_identifier(parser, parser->current.end, end - parser->current.end)) > 0) {
9628 parser->current.end += width;
9629 }
9630 } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) {
9631 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
9632 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) {
9633 diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3;
9634 }
9635
9636 size_t width = parser->encoding->char_width(parser->current.end, end - parser->current.end);
9637 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, (int) ((parser->current.end + width) - parser->current.start), (const char *) parser->current.start);
9638 } else {
9639 pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_CLASS_VARIABLE_BARE : PM_ERR_INSTANCE_VARIABLE_BARE;
9640 pm_parser_err_token(parser, &parser->current, diag_id);
9641 }
9642
9643 // If we're lexing an embedded variable, then we need to pop back into the
9644 // parent lex context.
9645 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
9646 lex_mode_pop(parser);
9647 }
9648
9649 return type;
9650}
9651
9655static PRISM_INLINE void
9656parser_lex_callback(pm_parser_t *parser) {
9657 if (parser->lex_callback.callback) {
9658 parser->lex_callback.callback(parser, &parser->current, parser->lex_callback.data);
9659 }
9660}
9661
9666parser_comment(pm_parser_t *parser, pm_comment_type_t type) {
9667 pm_comment_t *comment = (pm_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_comment_t), PRISM_ALIGNOF(pm_comment_t));
9668
9669 *comment = (pm_comment_t) {
9670 .type = type,
9671 .location = TOK2LOC(parser, &parser->current)
9672 };
9673
9674 return comment;
9675}
9676
9682static pm_token_type_t
9683lex_embdoc(pm_parser_t *parser) {
9684 // First, lex out the EMBDOC_BEGIN token.
9685 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9686
9687 if (newline == NULL) {
9688 parser->current.end = parser->end;
9689 } else {
9690 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9691 parser->current.end = newline + 1;
9692 }
9693
9694 parser->current.type = PM_TOKEN_EMBDOC_BEGIN;
9695 parser_lex_callback(parser);
9696
9697 // Now, create a comment that is going to be attached to the parser.
9698 const uint8_t *comment_start = parser->current.start;
9699 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_EMBDOC);
9700
9701 // Now, loop until we find the end of the embedded documentation or the end
9702 // of the file.
9703 while (parser->current.end + 4 <= parser->end) {
9704 parser->current.start = parser->current.end;
9705
9706 // If we've hit the end of the embedded documentation then we'll return
9707 // that token here.
9708 if (
9709 (memcmp(parser->current.end, "=end", 4) == 0) &&
9710 (
9711 (parser->current.end + 4 == parser->end) || // end of file
9712 pm_char_is_whitespace(parser->current.end[4]) || // whitespace
9713 (parser->current.end[4] == '\0') || // NUL or end of script
9714 (parser->current.end[4] == '\004') || // ^D
9715 (parser->current.end[4] == '\032') // ^Z
9716 )
9717 ) {
9718 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9719
9720 if (newline == NULL) {
9721 parser->current.end = parser->end;
9722 } else {
9723 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9724 parser->current.end = newline + 1;
9725 }
9726
9727 parser->current.type = PM_TOKEN_EMBDOC_END;
9728 parser_lex_callback(parser);
9729
9730 comment->location.length = (uint32_t) (parser->current.end - comment_start);
9731 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9732
9733 return PM_TOKEN_EMBDOC_END;
9734 }
9735
9736 // Otherwise, we'll parse until the end of the line and return a line of
9737 // embedded documentation.
9738 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
9739
9740 if (newline == NULL) {
9741 parser->current.end = parser->end;
9742 } else {
9743 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
9744 parser->current.end = newline + 1;
9745 }
9746
9747 parser->current.type = PM_TOKEN_EMBDOC_LINE;
9748 parser_lex_callback(parser);
9749 }
9750
9751 pm_parser_err_current(parser, PM_ERR_EMBDOC_TERM);
9752
9753 comment->location.length = (uint32_t) (parser->current.end - comment_start);
9754 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
9755
9756 return PM_TOKEN_EOF;
9757}
9758
9764static PRISM_INLINE void
9765parser_lex_ignored_newline(pm_parser_t *parser) {
9766 parser->current.type = PM_TOKEN_IGNORED_NEWLINE;
9767 parser_lex_callback(parser);
9768}
9769
9779static PRISM_INLINE void
9780parser_flush_heredoc_end(pm_parser_t *parser) {
9781 assert(parser->heredoc_end <= parser->end);
9782 parser->next_start = parser->heredoc_end;
9783 parser->heredoc_end = NULL;
9784}
9785
9789static bool
9790parser_end_of_line_p(const pm_parser_t *parser) {
9791 const uint8_t *cursor = parser->current.end;
9792
9793 while (cursor < parser->end && *cursor != '\n' && *cursor != '#') {
9794 if (!pm_char_is_inline_whitespace(*cursor++)) return false;
9795 }
9796
9797 return true;
9798}
9799
9818typedef struct {
9824
9829 const uint8_t *cursor;
9831
9851
9855static PRISM_INLINE void
9856pm_token_buffer_push_byte(pm_token_buffer_t *token_buffer, uint8_t byte) {
9857 pm_buffer_append_byte(&token_buffer->buffer, byte);
9858}
9859
9860static PRISM_INLINE void
9861pm_regexp_token_buffer_push_byte(pm_regexp_token_buffer_t *token_buffer, uint8_t byte) {
9862 pm_buffer_append_byte(&token_buffer->regexp_buffer, byte);
9863}
9864
9868static PRISM_INLINE size_t
9869parser_char_width(const pm_parser_t *parser) {
9870 size_t width;
9871 if (parser->encoding_changed) {
9872 width = parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9873 } else {
9874 width = pm_encoding_utf_8_char_width(parser->current.end, parser->end - parser->current.end);
9875 }
9876
9877 // TODO: If the character is invalid in the given encoding, then we'll just
9878 // push one byte into the buffer. This should actually be an error.
9879 return (width == 0 ? 1 : width);
9880}
9881
9885static void
9886pm_token_buffer_push_escaped(pm_token_buffer_t *token_buffer, pm_parser_t *parser) {
9887 size_t width = parser_char_width(parser);
9888 pm_buffer_append_bytes(&token_buffer->buffer, parser->current.end, width);
9889 parser->current.end += width;
9890}
9891
9892static void
9893pm_regexp_token_buffer_push_escaped(pm_regexp_token_buffer_t *token_buffer, pm_parser_t *parser) {
9894 size_t width = parser_char_width(parser);
9895 const uint8_t *start = parser->current.end;
9896 pm_buffer_append_bytes(&token_buffer->base.buffer, start, width);
9897 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, width);
9898 parser->current.end += width;
9899}
9900
9907static PRISM_INLINE void
9908pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9909 // Copy buffer data into the arena and free the heap buffer.
9910 size_t len = pm_buffer_length(&token_buffer->buffer);
9911 void *arena_data = pm_arena_memdup(parser->arena, pm_buffer_value(&token_buffer->buffer), len, PRISM_ALIGNOF(uint8_t));
9912 pm_string_constant_init(&parser->current_string, (const char *) arena_data, len);
9913 pm_buffer_cleanup(&token_buffer->buffer);
9914}
9915
9916static PRISM_INLINE void
9917pm_regexp_token_buffer_copy(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9918 pm_token_buffer_copy(parser, &token_buffer->base);
9919 pm_buffer_cleanup(&token_buffer->regexp_buffer);
9920}
9921
9931static void
9932pm_token_buffer_flush(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9933 if (token_buffer->cursor == NULL) {
9934 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9935 } else {
9936 pm_buffer_append_bytes(&token_buffer->buffer, token_buffer->cursor, (size_t) (parser->current.end - token_buffer->cursor));
9937 pm_token_buffer_copy(parser, token_buffer);
9938 }
9939}
9940
9941static void
9942pm_regexp_token_buffer_flush(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9943 if (token_buffer->base.cursor == NULL) {
9944 pm_string_shared_init(&parser->current_string, parser->current.start, parser->current.end);
9945 } else {
9946 const uint8_t *cursor = token_buffer->base.cursor;
9947 size_t length = (size_t) (parser->current.end - cursor);
9948 pm_buffer_append_bytes(&token_buffer->base.buffer, cursor, length);
9949 pm_buffer_append_bytes(&token_buffer->regexp_buffer, cursor, length);
9950 pm_regexp_token_buffer_copy(parser, token_buffer);
9951 }
9952}
9953
9954#define PM_TOKEN_BUFFER_DEFAULT_SIZE 16
9955
9964static void
9965pm_token_buffer_escape(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9966 const uint8_t *start;
9967 if (token_buffer->cursor == NULL) {
9968 pm_buffer_init(&token_buffer->buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9969 start = parser->current.start;
9970 } else {
9971 start = token_buffer->cursor;
9972 }
9973
9974 const uint8_t *end = parser->current.end - 1;
9975 assert(end >= start);
9976 pm_buffer_append_bytes(&token_buffer->buffer, start, (size_t) (end - start));
9977
9978 token_buffer->cursor = end;
9979}
9980
9981static void
9982pm_regexp_token_buffer_escape(pm_parser_t *parser, pm_regexp_token_buffer_t *token_buffer) {
9983 const uint8_t *start;
9984 if (token_buffer->base.cursor == NULL) {
9985 pm_buffer_init(&token_buffer->base.buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9986 pm_buffer_init(&token_buffer->regexp_buffer, PM_TOKEN_BUFFER_DEFAULT_SIZE);
9987 start = parser->current.start;
9988 } else {
9989 start = token_buffer->base.cursor;
9990 }
9991
9992 const uint8_t *end = parser->current.end - 1;
9993 pm_buffer_append_bytes(&token_buffer->base.buffer, start, (size_t) (end - start));
9994 pm_buffer_append_bytes(&token_buffer->regexp_buffer, start, (size_t) (end - start));
9995
9996 token_buffer->base.cursor = end;
9997}
9998
9999#undef PM_TOKEN_BUFFER_DEFAULT_SIZE
10000
10005static PRISM_INLINE size_t
10006pm_heredoc_strspn_inline_whitespace(pm_parser_t *parser, const uint8_t **cursor, pm_heredoc_indent_t indent) {
10007 size_t whitespace = 0;
10008
10009 switch (indent) {
10010 case PM_HEREDOC_INDENT_NONE:
10011 // Do nothing, we can't match a terminator with
10012 // indentation and there's no need to calculate common
10013 // whitespace.
10014 break;
10015 case PM_HEREDOC_INDENT_DASH:
10016 // Skip past inline whitespace.
10017 *cursor += pm_strspn_inline_whitespace(*cursor, parser->end - *cursor);
10018 break;
10019 case PM_HEREDOC_INDENT_TILDE:
10020 // Skip past inline whitespace and calculate common
10021 // whitespace.
10022 while (*cursor < parser->end && pm_char_is_inline_whitespace(**cursor)) {
10023 if (**cursor == '\t') {
10024 whitespace = (whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
10025 } else {
10026 whitespace++;
10027 }
10028 (*cursor)++;
10029 }
10030
10031 break;
10032 }
10033
10034 return whitespace;
10035}
10036
10041static uint8_t
10042pm_lex_percent_delimiter(pm_parser_t *parser) {
10043 size_t eol_length = match_eol(parser);
10044
10045 if (eol_length) {
10046 if (parser->heredoc_end) {
10047 // If we have already lexed a heredoc, then the newline has already
10048 // been added to the list. In this case we want to just flush the
10049 // heredoc end.
10050 parser_flush_heredoc_end(parser);
10051 } else {
10052 // Otherwise, we'll add the newline to the list of newlines.
10053 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + U32(eol_length));
10054 }
10055
10056 uint8_t delimiter = *parser->current.end;
10057
10058 // If our delimiter is \r\n, we want to treat it as if it's \n.
10059 // For example, %\r\nfoo\r\n should be "foo"
10060 if (eol_length == 2) {
10061 delimiter = *(parser->current.end + 1);
10062 }
10063
10064 parser->current.end += eol_length;
10065 return delimiter;
10066 }
10067
10068 return *parser->current.end++;
10069}
10070
10075#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return
10076
10083static void
10084parser_lex(pm_parser_t *parser) {
10085 assert(parser->current.end <= parser->end);
10086 parser->previous = parser->current;
10087
10088 // This value mirrors cmd_state from CRuby.
10089 bool previous_command_start = parser->command_start;
10090 parser->command_start = false;
10091
10092 // This is used to communicate to the newline lexing function that we've
10093 // already seen a comment.
10094 bool lexed_comment = false;
10095
10096 // Here we cache the current value of the semantic token seen flag. This is
10097 // used to reset it in case we find a token that shouldn't flip this flag.
10098 unsigned int semantic_token_seen = parser->semantic_token_seen;
10099 parser->semantic_token_seen = true;
10100
10101 // We'll jump to this label when we are about to encounter an EOF.
10102 // If we still have lex_modes on the stack, we pop them so that cleanup
10103 // can happen. For example, we should still continue parsing after a heredoc
10104 // identifier, even if the heredoc body was syntax invalid.
10105 switch_lex_modes:
10106
10107 switch (parser->lex_modes.current->mode) {
10108 case PM_LEX_DEFAULT:
10109 case PM_LEX_EMBEXPR:
10110 case PM_LEX_EMBVAR:
10111
10112 // We have a specific named label here because we are going to jump back to
10113 // this location in the event that we have lexed a token that should not be
10114 // returned to the parser. This includes comments, ignored newlines, and
10115 // invalid tokens of some form.
10116 lex_next_token: {
10117 // If we have the special next_start pointer set, then we're going to jump
10118 // to that location and start lexing from there.
10119 if (parser->next_start != NULL) {
10120 parser->current.end = parser->next_start;
10121 parser->next_start = NULL;
10122 }
10123
10124 // This value mirrors space_seen from CRuby. It tracks whether or not
10125 // space has been eaten before the start of the next token.
10126 bool space_seen = false;
10127
10128 // First, we're going to skip past any whitespace at the front of the next
10129 // token. Skip runs of inline whitespace in bulk to avoid per-character
10130 // stores back to parser->current.end.
10131 bool chomping = true;
10132 while (chomping) {
10133 /* Skip the run of inline whitespace in bulk, then decide what
10134 * to do based on the first byte after it. Handling both in a
10135 * single pass avoids re-entering the scan when the run was
10136 * non-empty, which is the common case. */
10137 {
10138 static const uint8_t inline_whitespace[256] = {
10139 [' '] = 1, ['\t'] = 1, ['\f'] = 1, ['\v'] = 1
10140 };
10141 const uint8_t *scan = parser->current.end;
10142 while (scan < parser->end && inline_whitespace[*scan]) scan++;
10143 if (scan > parser->current.end) {
10144 parser->current.end = scan;
10145 space_seen = true;
10146 }
10147 if (scan >= parser->end) break;
10148 }
10149
10150 switch (*parser->current.end) {
10151 case '\r':
10152 if (match_eol_offset(parser, 1)) {
10153 chomping = false;
10154 } else {
10155 pm_parser_warn(parser, PM_TOKEN_END(parser, &parser->current), 1, PM_WARN_UNEXPECTED_CARRIAGE_RETURN);
10156 parser->current.end++;
10157 space_seen = true;
10158 }
10159 break;
10160 case '\\': {
10161 size_t eol_length = match_eol_offset(parser, 1);
10162 if (eol_length) {
10163 if (parser->heredoc_end) {
10164 parser->current.end = parser->heredoc_end;
10165 parser->heredoc_end = NULL;
10166 } else {
10167 parser->current.end += eol_length + 1;
10168 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10169 space_seen = true;
10170 }
10171 } else if (pm_char_is_inline_whitespace(*parser->current.end)) {
10172 parser->current.end += 2;
10173 } else {
10174 chomping = false;
10175 }
10176
10177 break;
10178 }
10179 default:
10180 chomping = false;
10181 break;
10182 }
10183 }
10184
10185 // Next, we'll set to start of this token to be the current end.
10186 parser->current.start = parser->current.end;
10187
10188 // We'll check if we're at the end of the file. If we are, then we
10189 // need to return the EOF token.
10190 if (parser->current.end >= parser->end) {
10191 // We may be missing closing tokens. We should pop modes one by one
10192 // to do the appropriate cleanup like moving next_start for heredocs.
10193 // Only when no mode is remaining will we actually emit the EOF token.
10194 if (parser->lex_modes.current->mode != PM_LEX_DEFAULT) {
10195 lex_mode_pop(parser);
10196 goto switch_lex_modes;
10197 }
10198
10199 // If we hit EOF, but the EOF came immediately after a newline,
10200 // set the start of the token to the newline. This way any EOF
10201 // errors will be reported as happening on that line rather than
10202 // a line after. For example "foo(\n" should report an error
10203 // on line 1 even though EOF technically occurs on line 2.
10204 if (parser->current.start > parser->start && (*(parser->current.start - 1) == '\n')) {
10205 parser->current.start -= 1;
10206 }
10207 LEX(PM_TOKEN_EOF);
10208 }
10209
10210 // Finally, we'll check the current character to determine the next
10211 // token.
10212 switch (*parser->current.end++) {
10213 case '\0': // NUL or end of script
10214 case '\004': // ^D
10215 case '\032': // ^Z
10216 parser->current.end--;
10217 LEX(PM_TOKEN_EOF);
10218
10219 case '#': { // comments
10220 const uint8_t *ending = next_newline(parser->current.end, parser->end - parser->current.end);
10221 parser->current.end = ending == NULL ? parser->end : ending;
10222
10223 // If we found a comment while lexing, then we're going to
10224 // add it to the list of comments in the file and keep
10225 // lexing.
10226 pm_comment_t *comment = parser_comment(parser, PM_COMMENT_INLINE);
10227 pm_list_append(&parser->comment_list, (pm_list_node_t *) comment);
10228
10229 parser->current.type = PM_TOKEN_COMMENT;
10230 parser_lex_callback(parser);
10231
10232 // Here, parse the comment to see if it's a magic comment
10233 // and potentially change state on the parser.
10234 if (!parser_lex_magic_comment(parser, semantic_token_seen) && (parser->current.start == parser->encoding_comment_start)) {
10235 ptrdiff_t length = parser->current.end - parser->current.start;
10236
10237 // If we didn't find a magic comment within the first
10238 // pass and we're at the start of the file, then we need
10239 // to do another pass to potentially find other patterns
10240 // for encoding comments.
10241 if (length >= 10 && !parser->encoding_locked) {
10242 parser_lex_magic_comment_encoding(parser);
10243 }
10244 }
10245
10246 /* The comment does not include its terminating newline,
10247 * which lexes through the newline handling below as its
10248 * own token. A comment that ends the file has no newline,
10249 * so the newline handling runs without one to emit. */
10250 if (ending == NULL) {
10251 lexed_comment = true;
10252 } else {
10253 parser->current.start = ending;
10254 parser->current.end = ending + 1;
10255 }
10256 }
10258 case '\r':
10259 case '\n': {
10260 parser->semantic_token_seen = semantic_token_seen & 0x1;
10261 size_t eol_length = match_eol_at(parser, parser->current.end - 1);
10262
10263 if (eol_length) {
10264 // The only way you can have carriage returns in this
10265 // particular loop is if you have a carriage return
10266 // followed by a newline. In that case we'll just skip
10267 // over the carriage return and continue lexing, in
10268 // order to make it so that the newline token
10269 // encapsulates both the carriage return and the
10270 // newline. Note that we need to check that we haven't
10271 // already lexed a comment here because that falls
10272 // through into here as well.
10273 if (!lexed_comment) {
10274 parser->current.end += eol_length - 1; // skip CR
10275 }
10276
10277 if (parser->heredoc_end == NULL) {
10278 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
10279 }
10280 }
10281
10282 if (parser->heredoc_end) {
10283 parser_flush_heredoc_end(parser);
10284 }
10285
10286 // If this is an ignored newline, then we can continue lexing after
10287 // calling the callback with the ignored newline token.
10288 switch (lex_state_ignored_p(parser)) {
10289 case PM_IGNORED_NEWLINE_NONE:
10290 break;
10291 case PM_IGNORED_NEWLINE_PATTERN:
10292 if (parser->pattern_matching_newlines || parser->in_keyword_arg) {
10293 if (!lexed_comment) {
10294 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10295 parser_lex_callback(parser);
10296 }
10297
10298 lex_state_set(parser, PM_LEX_STATE_BEG);
10299 parser->command_start = true;
10300 parser->current.type = PM_TOKEN_NEWLINE;
10301 return;
10302 }
10304 case PM_IGNORED_NEWLINE_ALL:
10305 if (!lexed_comment) parser_lex_ignored_newline(parser);
10306 lexed_comment = false;
10307 goto lex_next_token;
10308 }
10309
10310 // Here we need to look ahead and see if there is a call operator
10311 // (either . or &.) that starts the next line. If there is, then this
10312 // is going to become an ignored newline and we're going to instead
10313 // return the call operator.
10314 const uint8_t *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start;
10315 next_content += pm_strspn_inline_whitespace(next_content, parser->end - next_content);
10316
10317 if (next_content < parser->end) {
10318 // If we hit a comment after a newline, then we're going to check
10319 // if it's ignored or if it's followed by a method call ('.').
10320 // If it is, then we're going to call the
10321 // callback with an ignored newline and then continue lexing.
10322 // Otherwise we'll return a regular newline.
10323 if (next_content[0] == '#') {
10324 // Here we look for a "." or "&." following a "\n".
10325 const uint8_t *following = next_newline(next_content, parser->end - next_content);
10326
10327 while (following && (following + 1 < parser->end)) {
10328 following++;
10329 following += pm_strspn_inline_whitespace(following, parser->end - following);
10330
10331 // If this is not followed by a comment, then we can break out
10332 // of this loop.
10333 if (peek_at(parser, following) != '#') break;
10334
10335 // If there is a comment, then we need to find the end of the
10336 // comment and continue searching from there.
10337 following = next_newline(following, parser->end - following);
10338 }
10339
10340 // If the lex state was ignored, we will lex the
10341 // ignored newline.
10342 if (lex_state_ignored_p(parser)) {
10343 if (!lexed_comment) parser_lex_ignored_newline(parser);
10344 lexed_comment = false;
10345 goto lex_next_token;
10346 }
10347
10348 // If we hit a '.' or a '&.' we will lex the ignored
10349 // newline.
10350 if (following && (
10351 (peek_at(parser, following) == '.') ||
10352 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10353 )) {
10354 if (!lexed_comment) parser_lex_ignored_newline(parser);
10355 lexed_comment = false;
10356 goto lex_next_token;
10357 }
10358
10359
10360 // If we are parsing as CRuby 4.0 or later and we
10361 // hit a '&&' or a '||' then we will lex the ignored
10362 // newline.
10363 if (
10364 (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) &&
10365 following && (
10366 (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
10367 (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
10368 (
10369 peek_at(parser, following) == 'a' &&
10370 peek_at(parser, following + 1) == 'n' &&
10371 peek_at(parser, following + 2) == 'd' &&
10372 peek_at(parser, next_content + 3) != '!' &&
10373 peek_at(parser, next_content + 3) != '?' &&
10374 !char_is_identifier(parser, following + 3, parser->end - (following + 3))
10375 ) ||
10376 (
10377 peek_at(parser, following) == 'o' &&
10378 peek_at(parser, following + 1) == 'r' &&
10379 peek_at(parser, next_content + 2) != '!' &&
10380 peek_at(parser, next_content + 2) != '?' &&
10381 !char_is_identifier(parser, following + 2, parser->end - (following + 2))
10382 )
10383 )
10384 ) {
10385 if (!lexed_comment) parser_lex_ignored_newline(parser);
10386 lexed_comment = false;
10387 goto lex_next_token;
10388 }
10389 }
10390
10391 // If we hit a . after a newline, then we're in a call chain and
10392 // we need to return the call operator.
10393 if (next_content[0] == '.') {
10394 /* A beginless range on the next line means this
10395 * newline terminates the statement rather than
10396 * continuing a method chain. */
10397 if (peek_at(parser, next_content + 1) == '.') {
10398 if (!lexed_comment) {
10399 parser->current.type = PM_TOKEN_NEWLINE_TERMINATOR;
10400 parser_lex_callback(parser);
10401 }
10402
10403 lex_state_set(parser, PM_LEX_STATE_BEG);
10404 parser->command_start = true;
10405 parser->current.type = PM_TOKEN_NEWLINE;
10406 return;
10407 }
10408
10409 if (!lexed_comment) parser_lex_ignored_newline(parser);
10410 lex_state_set(parser, PM_LEX_STATE_DOT);
10411 parser->current.start = next_content;
10412 parser->current.end = next_content + 1;
10413 parser->next_start = NULL;
10414 LEX(PM_TOKEN_DOT);
10415 }
10416
10417 // If we hit a &. after a newline, then we're in a call chain and
10418 // we need to return the call operator.
10419 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '.') {
10420 if (!lexed_comment) parser_lex_ignored_newline(parser);
10421 lex_state_set(parser, PM_LEX_STATE_DOT);
10422 parser->current.start = next_content;
10423 parser->current.end = next_content + 2;
10424 parser->next_start = NULL;
10425 LEX(PM_TOKEN_AMPERSAND_DOT);
10426 }
10427
10428 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
10429 // If we hit an && then we are in a logical chain
10430 // and we need to return the logical operator.
10431 if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
10432 if (!lexed_comment) parser_lex_ignored_newline(parser);
10433 lex_state_set(parser, PM_LEX_STATE_BEG);
10434 parser->current.start = next_content;
10435 parser->current.end = next_content + 2;
10436 parser->next_start = NULL;
10437 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10438 }
10439
10440 // If we hit a || then we are in a logical chain and
10441 // we need to return the logical operator.
10442 if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
10443 if (!lexed_comment) parser_lex_ignored_newline(parser);
10444 lex_state_set(parser, PM_LEX_STATE_BEG);
10445 parser->current.start = next_content;
10446 parser->current.end = next_content + 2;
10447 parser->next_start = NULL;
10448 LEX(PM_TOKEN_PIPE_PIPE);
10449 }
10450
10451 // If we hit an 'and' then we are in a logical chain
10452 // and we need to return the logical operator.
10453 if (
10454 peek_at(parser, next_content) == 'a' &&
10455 peek_at(parser, next_content + 1) == 'n' &&
10456 peek_at(parser, next_content + 2) == 'd' &&
10457 peek_at(parser, next_content + 3) != '!' &&
10458 peek_at(parser, next_content + 3) != '?' &&
10459 !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
10460 ) {
10461 if (!lexed_comment) parser_lex_ignored_newline(parser);
10462 lex_state_set(parser, PM_LEX_STATE_BEG);
10463 parser->current.start = next_content;
10464 parser->current.end = next_content + 3;
10465 parser->next_start = NULL;
10466 parser->command_start = true;
10467 LEX(PM_TOKEN_KEYWORD_AND);
10468 }
10469
10470 // If we hit a 'or' then we are in a logical chain
10471 // and we need to return the logical operator.
10472 if (
10473 peek_at(parser, next_content) == 'o' &&
10474 peek_at(parser, next_content + 1) == 'r' &&
10475 peek_at(parser, next_content + 2) != '!' &&
10476 peek_at(parser, next_content + 2) != '?' &&
10477 !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
10478 ) {
10479 if (!lexed_comment) parser_lex_ignored_newline(parser);
10480 lex_state_set(parser, PM_LEX_STATE_BEG);
10481 parser->current.start = next_content;
10482 parser->current.end = next_content + 2;
10483 parser->next_start = NULL;
10484 parser->command_start = true;
10485 LEX(PM_TOKEN_KEYWORD_OR);
10486 }
10487 }
10488 }
10489
10490 // At this point we know this is a regular newline, and we can set the
10491 // necessary state and return the token.
10492 lex_state_set(parser, PM_LEX_STATE_BEG);
10493 parser->command_start = true;
10494 parser->current.type = PM_TOKEN_NEWLINE;
10495 if (!lexed_comment) parser_lex_callback(parser);
10496 return;
10497 }
10498
10499 // ,
10500 case ',':
10501 if ((parser->previous.type == PM_TOKEN_COMMA) && (parser->enclosure_nesting > 0)) {
10502 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
10503 }
10504
10505 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10506 LEX(PM_TOKEN_COMMA);
10507
10508 // (
10509 case '(': {
10510 /* A parenthesis scanned at the beginning of an expression
10511 * groups the expression it wraps, while one scanned in
10512 * argument position with a preceding space wraps a command
10513 * argument. Everything else opens an argument list. */
10514 pm_token_type_t type = PM_TOKEN_PARENTHESIS_LEFT;
10515
10516 if (lex_state_beg_p(parser)) {
10517 type = PM_TOKEN_PARENTHESIS_LEFT_GROUPING;
10518 } else if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (PM_LEX_STATE_END | PM_LEX_STATE_LABEL))) {
10519 type = PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES;
10520 }
10521
10522 parser->enclosure_nesting++;
10523 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10524 pm_enclosure_frame_push(parser);
10525 LEX(type);
10526 }
10527
10528 // )
10529 case ')':
10530 parser->enclosure_nesting--;
10531 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10532 pm_enclosure_frame_pop(parser);
10533 LEX(PM_TOKEN_PARENTHESIS_RIGHT);
10534
10535 // ;
10536 case ';':
10537 lex_state_set(parser, PM_LEX_STATE_BEG);
10538 parser->command_start = true;
10539 LEX(PM_TOKEN_SEMICOLON);
10540
10541 // [ [] []=
10542 case '[':
10543 parser->enclosure_nesting++;
10544 pm_token_type_t type = PM_TOKEN_BRACKET_LEFT;
10545
10546 if (lex_state_operator_p(parser)) {
10547 if (match(parser, ']')) {
10548 parser->enclosure_nesting--;
10549 lex_state_set(parser, PM_LEX_STATE_ARG);
10550 LEX(match(parser, '=') ? PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : PM_TOKEN_BRACKET_LEFT_RIGHT);
10551 }
10552
10553 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABEL);
10554 LEX(type);
10555 }
10556
10557 if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, PM_LEX_STATE_LABELED)))) {
10558 type = PM_TOKEN_BRACKET_LEFT_ARRAY;
10559 }
10560
10561 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10562 pm_enclosure_frame_push(parser);
10563 LEX(type);
10564
10565 // ]
10566 case ']':
10567 parser->enclosure_nesting--;
10568 lex_state_set(parser, PM_LEX_STATE_END);
10569 pm_enclosure_frame_pop(parser);
10570 LEX(PM_TOKEN_BRACKET_RIGHT);
10571
10572 // {
10573 case '{': {
10574 pm_token_type_t type = PM_TOKEN_BRACE_LEFT;
10575
10576 if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) {
10577 /* This { begins a lambda */
10578 parser->command_start = true;
10579 lex_state_set(parser, PM_LEX_STATE_BEG);
10580 type = PM_TOKEN_LAMBDA_BEGIN;
10581 } else if (lex_state_p(parser, PM_LEX_STATE_LABELED)) {
10582 /* This { begins a hash literal */
10583 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10584 type = PM_TOKEN_BRACE_LEFT_HASH;
10585 } else if (lex_state_p(parser, PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_END | PM_LEX_STATE_ENDFN)) {
10586 /* This { begins a block */
10587 parser->command_start = true;
10588 lex_state_set(parser, PM_LEX_STATE_BEG);
10589 } else if (lex_state_p(parser, PM_LEX_STATE_ENDARG)) {
10590 /* This { begins a block following a parenthesized
10591 * command argument */
10592 parser->command_start = true;
10593 lex_state_set(parser, PM_LEX_STATE_BEG);
10594 type = PM_TOKEN_BRACE_LEFT_ARGUMENT;
10595 } else {
10596 /* This { begins a hash literal */
10597 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
10598 type = PM_TOKEN_BRACE_LEFT_HASH;
10599 }
10600
10601 parser->enclosure_nesting++;
10602 parser->brace_nesting++;
10603 pm_enclosure_frame_push(parser);
10604
10605 LEX(type);
10606 }
10607
10608 // }
10609 case '}':
10610 parser->enclosure_nesting--;
10611 pm_enclosure_frame_pop(parser);
10612
10613 if ((parser->lex_modes.current->mode == PM_LEX_EMBEXPR) && (parser->brace_nesting == 0)) {
10614 lex_mode_pop(parser);
10615 LEX(PM_TOKEN_EMBEXPR_END);
10616 }
10617
10618 parser->brace_nesting--;
10619 lex_state_set(parser, PM_LEX_STATE_END);
10620 LEX(PM_TOKEN_BRACE_RIGHT);
10621
10622 // * ** **= *=
10623 case '*': {
10624 if (match(parser, '*')) {
10625 if (match(parser, '=')) {
10626 lex_state_set(parser, PM_LEX_STATE_BEG);
10627 LEX(PM_TOKEN_STAR_STAR_EQUAL);
10628 }
10629
10630 pm_token_type_t type = PM_TOKEN_STAR_STAR;
10631
10632 if (lex_state_spcarg_p(parser, space_seen)) {
10633 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR_STAR);
10634 type = PM_TOKEN_USTAR_STAR;
10635 } else if (lex_state_beg_p(parser)) {
10636 type = PM_TOKEN_USTAR_STAR;
10637 } else if (ambiguous_operator_p(parser, space_seen)) {
10638 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "**", "argument prefix");
10639 }
10640
10641 if (lex_state_operator_p(parser)) {
10642 lex_state_set(parser, PM_LEX_STATE_ARG);
10643 } else {
10644 lex_state_set(parser, PM_LEX_STATE_BEG);
10645 }
10646
10647 LEX(type);
10648 }
10649
10650 if (match(parser, '=')) {
10651 lex_state_set(parser, PM_LEX_STATE_BEG);
10652 LEX(PM_TOKEN_STAR_EQUAL);
10653 }
10654
10655 pm_token_type_t type = PM_TOKEN_STAR;
10656
10657 if (lex_state_spcarg_p(parser, space_seen)) {
10658 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_STAR);
10659 type = PM_TOKEN_USTAR;
10660 } else if (lex_state_beg_p(parser)) {
10661 type = PM_TOKEN_USTAR;
10662 } else if (ambiguous_operator_p(parser, space_seen)) {
10663 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "*", "argument prefix");
10664 }
10665
10666 if (lex_state_operator_p(parser)) {
10667 lex_state_set(parser, PM_LEX_STATE_ARG);
10668 } else {
10669 lex_state_set(parser, PM_LEX_STATE_BEG);
10670 }
10671
10672 LEX(type);
10673 }
10674
10675 // ! != !~ !@
10676 case '!':
10677 if (lex_state_operator_p(parser)) {
10678 lex_state_set(parser, PM_LEX_STATE_ARG);
10679 if (match(parser, '@')) {
10680 LEX(PM_TOKEN_BANG);
10681 }
10682 } else {
10683 lex_state_set(parser, PM_LEX_STATE_BEG);
10684 }
10685
10686 if (match(parser, '=')) {
10687 LEX(PM_TOKEN_BANG_EQUAL);
10688 }
10689
10690 if (match(parser, '~')) {
10691 LEX(PM_TOKEN_BANG_TILDE);
10692 }
10693
10694 LEX(PM_TOKEN_BANG);
10695
10696 // = => =~ == === =begin
10697 case '=':
10698 if (
10699 current_token_starts_line(parser) &&
10700 (parser->current.end + 5 <= parser->end) &&
10701 memcmp(parser->current.end, "begin", 5) == 0 &&
10702 (pm_char_is_whitespace(peek_offset(parser, 5)) || (peek_offset(parser, 5) == '\0'))
10703 ) {
10704 pm_token_type_t type = lex_embdoc(parser);
10705 if (type == PM_TOKEN_EOF) {
10706 LEX(type);
10707 }
10708
10709 goto lex_next_token;
10710 }
10711
10712 if (lex_state_operator_p(parser)) {
10713 lex_state_set(parser, PM_LEX_STATE_ARG);
10714 } else {
10715 lex_state_set(parser, PM_LEX_STATE_BEG);
10716 }
10717
10718 if (match(parser, '>')) {
10719 LEX(PM_TOKEN_EQUAL_GREATER);
10720 }
10721
10722 if (match(parser, '~')) {
10723 LEX(PM_TOKEN_EQUAL_TILDE);
10724 }
10725
10726 if (match(parser, '=')) {
10727 LEX(match(parser, '=') ? PM_TOKEN_EQUAL_EQUAL_EQUAL : PM_TOKEN_EQUAL_EQUAL);
10728 }
10729
10730 LEX(PM_TOKEN_EQUAL);
10731
10732 // < << <<= <= <=>
10733 case '<':
10734 if (match(parser, '<')) {
10735 if (
10736 !lex_state_p(parser, PM_LEX_STATE_DOT | PM_LEX_STATE_CLASS) &&
10737 !lex_state_end_p(parser) &&
10738 (!lex_state_p(parser, PM_LEX_STATE_ARG_ANY) || lex_state_p(parser, PM_LEX_STATE_LABELED) || space_seen)
10739 ) {
10740 const uint8_t *end = parser->current.end;
10741
10742 pm_heredoc_quote_t quote = PM_HEREDOC_QUOTE_NONE;
10743 pm_heredoc_indent_t indent = PM_HEREDOC_INDENT_NONE;
10744
10745 if (match(parser, '-')) {
10746 indent = PM_HEREDOC_INDENT_DASH;
10747 }
10748 else if (match(parser, '~')) {
10749 indent = PM_HEREDOC_INDENT_TILDE;
10750 }
10751
10752 if (match(parser, '`')) {
10753 quote = PM_HEREDOC_QUOTE_BACKTICK;
10754 }
10755 else if (match(parser, '"')) {
10756 quote = PM_HEREDOC_QUOTE_DOUBLE;
10757 }
10758 else if (match(parser, '\'')) {
10759 quote = PM_HEREDOC_QUOTE_SINGLE;
10760 }
10761
10762 const uint8_t *ident_start = parser->current.end;
10763 size_t width = 0;
10764
10765 if (parser->current.end >= parser->end) {
10766 parser->current.end = end;
10767 } else if (quote == PM_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) == 0) {
10768 parser->current.end = end;
10769 } else {
10770 if (quote == PM_HEREDOC_QUOTE_NONE) {
10771 parser->current.end += width;
10772
10773 while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end))) {
10774 parser->current.end += width;
10775 }
10776 } else {
10777 // If we have quotes, then we're going to go until we find the
10778 // end quote.
10779 while ((parser->current.end < parser->end) && quote != (pm_heredoc_quote_t) (*parser->current.end)) {
10780 if (*parser->current.end == '\r' || *parser->current.end == '\n') break;
10781 parser->current.end++;
10782 }
10783 }
10784
10785 size_t ident_length = (size_t) (parser->current.end - ident_start);
10786 bool ident_error = false;
10787
10788 if (quote != PM_HEREDOC_QUOTE_NONE && !match(parser, (uint8_t) quote)) {
10789 pm_parser_err(parser, U32(ident_start - parser->start), U32(ident_length), PM_ERR_HEREDOC_IDENTIFIER);
10790 ident_error = true;
10791 }
10792
10793 parser->explicit_encoding = NULL;
10794 lex_mode_push(parser, (pm_lex_mode_t) {
10795 .mode = PM_LEX_HEREDOC,
10796 .as.heredoc = {
10797 .base = {
10798 .ident_start = ident_start,
10799 .ident_length = ident_length,
10800 .quote = quote,
10801 .indent = indent
10802 },
10803 .next_start = parser->current.end,
10804 .common_whitespace = NULL,
10805 .line_continuation = false
10806 }
10807 });
10808
10809 if (parser->heredoc_end == NULL) {
10810 const uint8_t *body_start = next_newline(parser->current.end, parser->end - parser->current.end);
10811
10812 if (body_start == NULL) {
10813 // If there is no newline after the heredoc identifier, then
10814 // this is not a valid heredoc declaration. In this case we
10815 // will add an error, but we will still return a heredoc
10816 // start.
10817 if (!ident_error) pm_parser_err_heredoc_term(parser, ident_start, ident_length);
10818 body_start = parser->end;
10819 } else {
10820 // Otherwise, we want to indicate that the body of the
10821 // heredoc starts on the character after the next newline.
10822 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(body_start - parser->start + 1));
10823 body_start++;
10824 }
10825
10826 parser->next_start = body_start;
10827 } else {
10828 parser->next_start = parser->heredoc_end;
10829 }
10830
10831 LEX(PM_TOKEN_HEREDOC_START);
10832 }
10833 }
10834
10835 if (match(parser, '=')) {
10836 lex_state_set(parser, PM_LEX_STATE_BEG);
10837 LEX(PM_TOKEN_LESS_LESS_EQUAL);
10838 }
10839
10840 if (ambiguous_operator_p(parser, space_seen)) {
10841 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "<<", "here document");
10842 }
10843
10844 if (lex_state_operator_p(parser)) {
10845 lex_state_set(parser, PM_LEX_STATE_ARG);
10846 } else {
10847 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10848 lex_state_set(parser, PM_LEX_STATE_BEG);
10849 }
10850
10851 LEX(PM_TOKEN_LESS_LESS);
10852 }
10853
10854 if (lex_state_operator_p(parser)) {
10855 lex_state_set(parser, PM_LEX_STATE_ARG);
10856 } else {
10857 if (lex_state_p(parser, PM_LEX_STATE_CLASS)) parser->command_start = true;
10858 lex_state_set(parser, PM_LEX_STATE_BEG);
10859 }
10860
10861 if (match(parser, '=')) {
10862 if (match(parser, '>')) {
10863 LEX(PM_TOKEN_LESS_EQUAL_GREATER);
10864 }
10865
10866 LEX(PM_TOKEN_LESS_EQUAL);
10867 }
10868
10869 LEX(PM_TOKEN_LESS);
10870
10871 // > >> >>= >=
10872 case '>':
10873 if (match(parser, '>')) {
10874 if (lex_state_operator_p(parser)) {
10875 lex_state_set(parser, PM_LEX_STATE_ARG);
10876 } else {
10877 lex_state_set(parser, PM_LEX_STATE_BEG);
10878 }
10879 LEX(match(parser, '=') ? PM_TOKEN_GREATER_GREATER_EQUAL : PM_TOKEN_GREATER_GREATER);
10880 }
10881
10882 if (lex_state_operator_p(parser)) {
10883 lex_state_set(parser, PM_LEX_STATE_ARG);
10884 } else {
10885 lex_state_set(parser, PM_LEX_STATE_BEG);
10886 }
10887
10888 LEX(match(parser, '=') ? PM_TOKEN_GREATER_EQUAL : PM_TOKEN_GREATER);
10889
10890 // double-quoted string literal
10891 case '"': {
10892 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10893 lex_mode_push_string(parser, true, label_allowed, '\0', '"');
10894 LEX(PM_TOKEN_STRING_BEGIN);
10895 }
10896
10897 // xstring literal
10898 case '`': {
10899 if (lex_state_p(parser, PM_LEX_STATE_FNAME)) {
10900 lex_state_set(parser, PM_LEX_STATE_ENDFN);
10901 LEX(PM_TOKEN_BACKTICK);
10902 }
10903
10904 if (lex_state_p(parser, PM_LEX_STATE_DOT)) {
10905 if (previous_command_start) {
10906 lex_state_set(parser, PM_LEX_STATE_CMDARG);
10907 } else {
10908 lex_state_set(parser, PM_LEX_STATE_ARG);
10909 }
10910
10911 LEX(PM_TOKEN_BACKTICK);
10912 }
10913
10914 lex_mode_push_string(parser, true, false, '\0', '`');
10915 LEX(PM_TOKEN_XSTRING_BEGIN);
10916 }
10917
10918 // single-quoted string literal
10919 case '\'': {
10920 bool label_allowed = (lex_state_p(parser, PM_LEX_STATE_LABEL | PM_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser);
10921 lex_mode_push_string(parser, false, label_allowed, '\0', '\'');
10922 LEX(PM_TOKEN_STRING_BEGIN);
10923 }
10924
10925 // ? character literal
10926 case '?':
10927 LEX(lex_question_mark(parser));
10928
10929 // & && &&= &=
10930 case '&': {
10931 if (match(parser, '&')) {
10932 lex_state_set(parser, PM_LEX_STATE_BEG);
10933
10934 if (match(parser, '=')) {
10935 LEX(PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
10936 }
10937
10938 LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10939 }
10940
10941 if (match(parser, '=')) {
10942 lex_state_set(parser, PM_LEX_STATE_BEG);
10943 LEX(PM_TOKEN_AMPERSAND_EQUAL);
10944 }
10945
10946 if (match(parser, '.')) {
10947 lex_state_set(parser, PM_LEX_STATE_DOT);
10948 LEX(PM_TOKEN_AMPERSAND_DOT);
10949 }
10950
10951 pm_token_type_t type = PM_TOKEN_AMPERSAND;
10952 if (lex_state_spcarg_p(parser, space_seen)) {
10953 if ((peek(parser) != ':') || (peek_offset(parser, 1) == '\0')) {
10954 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
10955 } else {
10956 const uint8_t delim = peek_offset(parser, 1);
10957
10958 if ((delim != '\'') && (delim != '"') && !char_is_identifier(parser, parser->current.end + 1, parser->end - (parser->current.end + 1))) {
10959 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_PREFIX_AMPERSAND);
10960 }
10961 }
10962
10963 type = PM_TOKEN_UAMPERSAND;
10964 } else if (lex_state_beg_p(parser)) {
10965 type = PM_TOKEN_UAMPERSAND;
10966 } else if (ambiguous_operator_p(parser, space_seen)) {
10967 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "&", "argument prefix");
10968 }
10969
10970 if (lex_state_operator_p(parser)) {
10971 lex_state_set(parser, PM_LEX_STATE_ARG);
10972 } else {
10973 lex_state_set(parser, PM_LEX_STATE_BEG);
10974 }
10975
10976 LEX(type);
10977 }
10978
10979 // | || ||= |=
10980 case '|':
10981 if (match(parser, '|')) {
10982 if (match(parser, '=')) {
10983 lex_state_set(parser, PM_LEX_STATE_BEG);
10984 LEX(PM_TOKEN_PIPE_PIPE_EQUAL);
10985 }
10986
10987 if (lex_state_p(parser, PM_LEX_STATE_BEG)) {
10988 parser->current.end--;
10989 LEX(PM_TOKEN_PIPE);
10990 }
10991
10992 lex_state_set(parser, PM_LEX_STATE_BEG);
10993 LEX(PM_TOKEN_PIPE_PIPE);
10994 }
10995
10996 if (match(parser, '=')) {
10997 lex_state_set(parser, PM_LEX_STATE_BEG);
10998 LEX(PM_TOKEN_PIPE_EQUAL);
10999 }
11000
11001 if (lex_state_operator_p(parser)) {
11002 lex_state_set(parser, PM_LEX_STATE_ARG);
11003 } else {
11004 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
11005 }
11006
11007 LEX(PM_TOKEN_PIPE);
11008
11009 // + += +@
11010 case '+': {
11011 if (lex_state_operator_p(parser)) {
11012 lex_state_set(parser, PM_LEX_STATE_ARG);
11013
11014 if (match(parser, '@')) {
11015 LEX(PM_TOKEN_UPLUS);
11016 }
11017
11018 LEX(PM_TOKEN_PLUS);
11019 }
11020
11021 if (match(parser, '=')) {
11022 lex_state_set(parser, PM_LEX_STATE_BEG);
11023 LEX(PM_TOKEN_PLUS_EQUAL);
11024 }
11025
11026 if (
11027 lex_state_beg_p(parser) ||
11028 (lex_state_spcarg_p(parser, space_seen) ? (pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS), true) : false)
11029 ) {
11030 lex_state_set(parser, PM_LEX_STATE_BEG);
11031
11032 if (pm_char_is_decimal_digit(peek(parser))) {
11033 parser->current.end++;
11034 pm_token_type_t type = lex_numeric(parser);
11035 lex_state_set(parser, PM_LEX_STATE_END);
11036 LEX(type);
11037 }
11038
11039 LEX(PM_TOKEN_UPLUS);
11040 }
11041
11042 if (ambiguous_operator_p(parser, space_seen)) {
11043 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "+", "unary operator");
11044 }
11045
11046 lex_state_set(parser, PM_LEX_STATE_BEG);
11047 LEX(PM_TOKEN_PLUS);
11048 }
11049
11050 // - -= -@
11051 case '-': {
11052 if (lex_state_operator_p(parser)) {
11053 lex_state_set(parser, PM_LEX_STATE_ARG);
11054
11055 if (match(parser, '@')) {
11056 LEX(PM_TOKEN_UMINUS);
11057 }
11058
11059 LEX(PM_TOKEN_MINUS);
11060 }
11061
11062 if (match(parser, '=')) {
11063 lex_state_set(parser, PM_LEX_STATE_BEG);
11064 LEX(PM_TOKEN_MINUS_EQUAL);
11065 }
11066
11067 if (match(parser, '>')) {
11068 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11069 LEX(PM_TOKEN_MINUS_GREATER);
11070 }
11071
11072 bool spcarg = lex_state_spcarg_p(parser, space_seen);
11073 bool is_beg = lex_state_beg_p(parser);
11074 if (!is_beg && spcarg) {
11075 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS);
11076 }
11077
11078 if (is_beg || spcarg) {
11079 lex_state_set(parser, PM_LEX_STATE_BEG);
11080 LEX(pm_char_is_decimal_digit(peek(parser)) ? PM_TOKEN_UMINUS_NUM : PM_TOKEN_UMINUS);
11081 }
11082
11083 if (ambiguous_operator_p(parser, space_seen)) {
11084 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "-", "unary operator");
11085 }
11086
11087 lex_state_set(parser, PM_LEX_STATE_BEG);
11088 LEX(PM_TOKEN_MINUS);
11089 }
11090
11091 // . .. ...
11092 case '.': {
11093 bool beg_p = lex_state_beg_p(parser);
11094
11095 if (match(parser, '.')) {
11096 if (match(parser, '.')) {
11097 // If we're _not_ inside a range within default parameters
11098 if (!context_p(parser, PM_CONTEXT_DEFAULT_PARAMS) && context_p(parser, PM_CONTEXT_DEF_PARAMS)) {
11099 if (lex_state_p(parser, PM_LEX_STATE_END)) {
11100 lex_state_set(parser, PM_LEX_STATE_BEG);
11101 } else {
11102 lex_state_set(parser, PM_LEX_STATE_ENDARG);
11103 }
11104 LEX(PM_TOKEN_UDOT_DOT_DOT);
11105 }
11106
11107 if (parser->enclosure_nesting == 0 && parser_end_of_line_p(parser)) {
11108 pm_parser_warn_token(parser, &parser->current, PM_WARN_DOT_DOT_DOT_EOL);
11109 }
11110
11111 lex_state_set(parser, PM_LEX_STATE_BEG);
11112 LEX(beg_p ? PM_TOKEN_UDOT_DOT_DOT : PM_TOKEN_DOT_DOT_DOT);
11113 }
11114
11115 lex_state_set(parser, PM_LEX_STATE_BEG);
11116 LEX(beg_p ? PM_TOKEN_UDOT_DOT : PM_TOKEN_DOT_DOT);
11117 }
11118
11119 lex_state_set(parser, PM_LEX_STATE_DOT);
11120 LEX(PM_TOKEN_DOT);
11121 }
11122
11123 // integer
11124 case '0':
11125 case '1':
11126 case '2':
11127 case '3':
11128 case '4':
11129 case '5':
11130 case '6':
11131 case '7':
11132 case '8':
11133 case '9': {
11134 pm_token_type_t type = lex_numeric(parser);
11135 lex_state_set(parser, PM_LEX_STATE_END);
11136 LEX(type);
11137 }
11138
11139 // :: symbol
11140 case ':':
11141 if (match(parser, ':')) {
11142 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)) {
11143 lex_state_set(parser, PM_LEX_STATE_BEG);
11144 LEX(PM_TOKEN_UCOLON_COLON);
11145 }
11146
11147 lex_state_set(parser, PM_LEX_STATE_DOT);
11148 LEX(PM_TOKEN_COLON_COLON);
11149 }
11150
11151 if (lex_state_end_p(parser) || pm_char_is_whitespace(peek(parser)) || peek(parser) == '#') {
11152 lex_state_set(parser, PM_LEX_STATE_BEG);
11153 LEX(PM_TOKEN_COLON);
11154 }
11155
11156 if (peek(parser) == '"' || peek(parser) == '\'') {
11157 lex_mode_push_string(parser, peek(parser) == '"', false, '\0', *parser->current.end);
11158 parser->current.end++;
11159 } else {
11160 /*
11161 * A quoted symbol clears its encoding by pushing a lex
11162 * mode above. A bare symbol is lexed inline, so it
11163 * clears the encoding here.
11164 */
11165 parser->explicit_encoding = NULL;
11166 }
11167
11168 lex_state_set(parser, PM_LEX_STATE_FNAME);
11169 LEX(PM_TOKEN_SYMBOL_BEGIN);
11170
11171 // / /=
11172 case '/':
11173 if (lex_state_beg_p(parser)) {
11174 lex_mode_push_regexp(parser, '\0', '/');
11175 LEX(PM_TOKEN_REGEXP_BEGIN);
11176 }
11177
11178 if (match(parser, '=')) {
11179 lex_state_set(parser, PM_LEX_STATE_BEG);
11180 LEX(PM_TOKEN_SLASH_EQUAL);
11181 }
11182
11183 if (lex_state_spcarg_p(parser, space_seen)) {
11184 // https://bugs.ruby-lang.org/issues/21994
11185 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_4_0) {
11186 pm_parser_warn_token(parser, &parser->current, PM_WARN_AMBIGUOUS_SLASH);
11187 }
11188 lex_mode_push_regexp(parser, '\0', '/');
11189 LEX(PM_TOKEN_REGEXP_BEGIN);
11190 }
11191
11192 if (ambiguous_operator_p(parser, space_seen)) {
11193 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "/", "regexp literal");
11194 }
11195
11196 if (lex_state_operator_p(parser)) {
11197 lex_state_set(parser, PM_LEX_STATE_ARG);
11198 } else {
11199 lex_state_set(parser, PM_LEX_STATE_BEG);
11200 }
11201
11202 LEX(PM_TOKEN_SLASH);
11203
11204 // ^ ^=
11205 case '^':
11206 if (lex_state_operator_p(parser)) {
11207 lex_state_set(parser, PM_LEX_STATE_ARG);
11208 } else {
11209 lex_state_set(parser, PM_LEX_STATE_BEG);
11210 }
11211 LEX(match(parser, '=') ? PM_TOKEN_CARET_EQUAL : PM_TOKEN_CARET);
11212
11213 // ~ ~@
11214 case '~':
11215 if (lex_state_operator_p(parser)) {
11216 (void) match(parser, '@');
11217 lex_state_set(parser, PM_LEX_STATE_ARG);
11218 } else {
11219 lex_state_set(parser, PM_LEX_STATE_BEG);
11220 }
11221
11222 LEX(PM_TOKEN_TILDE);
11223
11224 // % %= %i %I %q %Q %w %W
11225 case '%': {
11226 // If there is no subsequent character then we have an
11227 // invalid token. We're going to say it's the percent
11228 // operator because we don't want to move into the string
11229 // lex mode unnecessarily.
11230 if ((lex_state_beg_p(parser) || lex_state_arg_p(parser)) && (parser->current.end >= parser->end)) {
11231 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT_EOF);
11232 LEX(PM_TOKEN_PERCENT);
11233 }
11234
11235 if (!lex_state_beg_p(parser) && match(parser, '=')) {
11236 lex_state_set(parser, PM_LEX_STATE_BEG);
11237 LEX(PM_TOKEN_PERCENT_EQUAL);
11238 } else if (
11239 lex_state_beg_p(parser) ||
11240 (lex_state_p(parser, PM_LEX_STATE_FITEM) && (peek(parser) == 's')) ||
11241 lex_state_spcarg_p(parser, space_seen)
11242 ) {
11243 if (!parser->encoding->alnum_char(parser->current.end, parser->end - parser->current.end)) {
11244 if (*parser->current.end >= 0x80) {
11245 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11246 goto lex_next_token;
11247 }
11248
11249 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11250 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11251 LEX(PM_TOKEN_STRING_BEGIN);
11252 }
11253
11254 // Delimiters for %-literals cannot be alphanumeric. We
11255 // validate that here.
11256 uint8_t delimiter = peek_offset(parser, 1);
11257 if (delimiter >= 0x80 || parser->encoding->alnum_char(&delimiter, 1)) {
11258 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11259 goto lex_next_token;
11260 }
11261
11262 switch (peek(parser)) {
11263 case 'i': {
11264 parser->current.end++;
11265
11266 if (parser->current.end < parser->end) {
11267 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11268 } else {
11269 lex_mode_push_list_eof(parser);
11270 }
11271
11272 LEX(PM_TOKEN_PERCENT_LOWER_I);
11273 }
11274 case 'I': {
11275 parser->current.end++;
11276
11277 if (parser->current.end < parser->end) {
11278 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11279 } else {
11280 lex_mode_push_list_eof(parser);
11281 }
11282
11283 LEX(PM_TOKEN_PERCENT_UPPER_I);
11284 }
11285 case 'r': {
11286 parser->current.end++;
11287
11288 if (parser->current.end < parser->end) {
11289 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11290 lex_mode_push_regexp(parser, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11291 } else {
11292 lex_mode_push_regexp(parser, '\0', '\0');
11293 }
11294
11295 LEX(PM_TOKEN_REGEXP_BEGIN);
11296 }
11297 case 'q': {
11298 parser->current.end++;
11299
11300 if (parser->current.end < parser->end) {
11301 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11302 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11303 } else {
11304 lex_mode_push_string_eof(parser);
11305 }
11306
11307 LEX(PM_TOKEN_STRING_BEGIN);
11308 }
11309 case 'Q': {
11310 parser->current.end++;
11311
11312 if (parser->current.end < parser->end) {
11313 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11314 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11315 } else {
11316 lex_mode_push_string_eof(parser);
11317 }
11318
11319 LEX(PM_TOKEN_STRING_BEGIN);
11320 }
11321 case 's': {
11322 parser->current.end++;
11323
11324 if (parser->current.end < parser->end) {
11325 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11326 lex_mode_push_string(parser, false, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11327 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
11328 } else {
11329 lex_mode_push_string_eof(parser);
11330 }
11331
11332 LEX(PM_TOKEN_SYMBOL_BEGIN);
11333 }
11334 case 'w': {
11335 parser->current.end++;
11336
11337 if (parser->current.end < parser->end) {
11338 lex_mode_push_list(parser, false, pm_lex_percent_delimiter(parser));
11339 } else {
11340 lex_mode_push_list_eof(parser);
11341 }
11342
11343 LEX(PM_TOKEN_PERCENT_LOWER_W);
11344 }
11345 case 'W': {
11346 parser->current.end++;
11347
11348 if (parser->current.end < parser->end) {
11349 lex_mode_push_list(parser, true, pm_lex_percent_delimiter(parser));
11350 } else {
11351 lex_mode_push_list_eof(parser);
11352 }
11353
11354 LEX(PM_TOKEN_PERCENT_UPPER_W);
11355 }
11356 case 'x': {
11357 parser->current.end++;
11358
11359 if (parser->current.end < parser->end) {
11360 const uint8_t delimiter = pm_lex_percent_delimiter(parser);
11361 lex_mode_push_string(parser, true, false, lex_mode_incrementor(delimiter), lex_mode_terminator(delimiter));
11362 } else {
11363 lex_mode_push_string_eof(parser);
11364 }
11365
11366 LEX(PM_TOKEN_PERCENT_LOWER_X);
11367 }
11368 default:
11369 // If we get to this point, then we have a % that is completely
11370 // unparsable. In this case we'll just drop it from the parser
11371 // and skip past it and hope that the next token is something
11372 // that we can parse.
11373 pm_parser_err_current(parser, PM_ERR_INVALID_PERCENT);
11374 goto lex_next_token;
11375 }
11376 }
11377
11378 if (ambiguous_operator_p(parser, space_seen)) {
11379 PM_PARSER_WARN_TOKEN_FORMAT(parser, &parser->current, PM_WARN_AMBIGUOUS_BINARY_OPERATOR, "%", "string literal");
11380 }
11381
11382 lex_state_set(parser, lex_state_operator_p(parser) ? PM_LEX_STATE_ARG : PM_LEX_STATE_BEG);
11383 LEX(PM_TOKEN_PERCENT);
11384 }
11385
11386 // global variable
11387 case '$': {
11388 pm_token_type_t type = lex_global_variable(parser);
11389
11390 // If we're lexing an embedded variable, then we need to pop back into
11391 // the parent lex context.
11392 if (parser->lex_modes.current->mode == PM_LEX_EMBVAR) {
11393 lex_mode_pop(parser);
11394 }
11395
11396 lex_state_set(parser, PM_LEX_STATE_END);
11397 LEX(type);
11398 }
11399
11400 // instance variable, class variable
11401 case '@':
11402 lex_state_set(parser, parser->lex_state & PM_LEX_STATE_FNAME ? PM_LEX_STATE_ENDFN : PM_LEX_STATE_END);
11403 LEX(lex_at_variable(parser));
11404
11405 default: {
11406 if (*parser->current.start != '_') {
11407 size_t width = char_is_identifier_start(parser, parser->current.start, parser->end - parser->current.start);
11408
11409 // If this isn't the beginning of an identifier, then
11410 // it's an invalid token as we've exhausted all of the
11411 // other options. We'll skip past it and return the next
11412 // token after adding an appropriate error message.
11413 if (!width) {
11414 if (*parser->current.start >= 0x80) {
11415 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_MULTIBYTE_CHARACTER, *parser->current.start);
11416 } else if (*parser->current.start == '\\') {
11417 switch (peek_at(parser, parser->current.start + 1)) {
11418 case ' ':
11419 parser->current.end++;
11420 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped space");
11421 break;
11422 case '\f':
11423 parser->current.end++;
11424 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped form feed");
11425 break;
11426 case '\t':
11427 parser->current.end++;
11428 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped horizontal tab");
11429 break;
11430 case '\v':
11431 parser->current.end++;
11432 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped vertical tab");
11433 break;
11434 case '\r':
11435 if (peek_at(parser, parser->current.start + 2) != '\n') {
11436 parser->current.end++;
11437 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "escaped carriage return");
11438 break;
11439 }
11441 default:
11442 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, "backslash");
11443 break;
11444 }
11445 } else if (char_is_ascii_printable(*parser->current.start)) {
11446 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_PRINTABLE_CHARACTER, *parser->current.start);
11447 } else {
11448 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_INVALID_CHARACTER, *parser->current.start);
11449 }
11450
11451 goto lex_next_token;
11452 }
11453
11454 parser->current.end = parser->current.start + width;
11455 }
11456
11457 pm_token_type_t type = lex_identifier(parser, previous_command_start);
11458
11459 // If we've hit a __END__ and it was at the start of the
11460 // line or the start of the file and it is followed by
11461 // either a \n or a \r\n, then this is the last token of the
11462 // file.
11463 if (
11464 ((parser->current.end - parser->current.start) == 7) &&
11465 current_token_starts_line(parser) &&
11466 (memcmp(parser->current.start, "__END__", 7) == 0) &&
11467 (parser->current.end == parser->end || match_eol(parser))
11468 ) {
11469 // Since we know we're about to add an __END__ comment,
11470 // we know we need to add all of the newlines to get the
11471 // correct column information for it.
11472 const uint8_t *cursor = parser->current.end;
11473 while ((cursor = next_newline(cursor, parser->end - cursor)) != NULL) {
11474 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(++cursor - parser->start));
11475 }
11476
11477 parser->current.end = parser->end;
11478 parser->current.type = PM_TOKEN___END__;
11479 parser_lex_callback(parser);
11480
11481 parser->data_loc.start = PM_TOKEN_START(parser, &parser->current);
11482 parser->data_loc.length = PM_TOKEN_LENGTH(&parser->current);
11483
11484 LEX(PM_TOKEN_EOF);
11485 }
11486
11487 pm_lex_state_t last_state = parser->lex_state;
11488
11489 if (type == PM_TOKEN_IDENTIFIER || type == PM_TOKEN_CONSTANT || type == PM_TOKEN_METHOD_NAME) {
11490 if (lex_state_p(parser, PM_LEX_STATE_BEG_ANY | PM_LEX_STATE_ARG_ANY | PM_LEX_STATE_DOT)) {
11491 if (previous_command_start) {
11492 lex_state_set(parser, PM_LEX_STATE_CMDARG);
11493 } else {
11494 lex_state_set(parser, PM_LEX_STATE_ARG);
11495 }
11496 } else if (parser->lex_state == PM_LEX_STATE_FNAME) {
11497 lex_state_set(parser, PM_LEX_STATE_ENDFN);
11498 } else {
11499 lex_state_set(parser, PM_LEX_STATE_END);
11500 }
11501 }
11502
11503 if (
11504 !(last_state & (PM_LEX_STATE_DOT | PM_LEX_STATE_FNAME)) &&
11505 (type == PM_TOKEN_IDENTIFIER) &&
11506 ((pm_parser_local_depth(parser, &parser->current) != -1) ||
11507 pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)))
11508 ) {
11509 lex_state_set(parser, PM_LEX_STATE_END | PM_LEX_STATE_LABEL);
11510 }
11511
11512 LEX(type);
11513 }
11514 }
11515 }
11516 case PM_LEX_LIST: {
11517 if (parser->next_start != NULL) {
11518 parser->current.end = parser->next_start;
11519 parser->next_start = NULL;
11520 }
11521
11522 // First we'll set the beginning of the token.
11523 parser->current.start = parser->current.end;
11524
11525 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11526
11527 // If there's any whitespace at the start of the list, then we're
11528 // going to trim it off the beginning and create a new token.
11529 size_t whitespace;
11530
11531 if (parser->heredoc_end) {
11532 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11533 if (peek_offset(parser, (ptrdiff_t)whitespace) == '\n') {
11534 whitespace += 1;
11535 }
11536 } else if (lex_mode->as.list.terminator == '\n') {
11537 // When the list delimiter is a newline (e.g. `%w` followed by a
11538 // newline), the newline is the terminator rather than a word
11539 // separator. We only trim inline whitespace here so that the
11540 // terminating newline is left for the terminator handling below.
11541 whitespace = pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end);
11542 } else {
11543 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));
11544 }
11545
11546 if (whitespace > 0) {
11547 parser->current.end += whitespace;
11548 if (peek_offset(parser, -1) == '\n') {
11549 // mutates next_start
11550 parser_flush_heredoc_end(parser);
11551 }
11552 LEX(PM_TOKEN_WORDS_SEP);
11553 }
11554
11555 // We'll check if we're at the end of the file. If we are, then we
11556 // need to return the EOF token.
11557 if (parser->current.end >= parser->end) {
11558 LEX(PM_TOKEN_EOF);
11559 }
11560
11561 // Here we'll get a list of the places where strpbrk should break,
11562 // and then find the first one.
11563 const uint8_t *breakpoints = lex_mode->as.list.breakpoints;
11564 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11565
11566 // If we haven't found an escape yet, then this buffer will be
11567 // unallocated since we can refer directly to the source string.
11568 pm_token_buffer_t token_buffer = { 0 };
11569
11570 while (breakpoint != NULL) {
11571 // If we hit whitespace, then we must have received content by
11572 // now, so we can return an element of the list. A whitespace
11573 // character that is also the terminator (e.g. a newline
11574 // delimiter) is handled by the terminator check below, not here.
11575 if (pm_char_is_whitespace(*breakpoint) && *breakpoint != lex_mode->as.list.terminator) {
11576 parser->current.end = breakpoint;
11577 pm_token_buffer_flush(parser, &token_buffer);
11578 LEX(PM_TOKEN_STRING_CONTENT);
11579 }
11580
11581 // If we hit the terminator, we need to check which token to
11582 // return.
11583 if (*breakpoint == lex_mode->as.list.terminator) {
11584 // If this terminator doesn't actually close the list, then
11585 // we need to continue on past it.
11586 if (lex_mode->as.list.nesting > 0) {
11587 parser->current.end = breakpoint + 1;
11588 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11589 lex_mode->as.list.nesting--;
11590 continue;
11591 }
11592
11593 // If we've hit the terminator and we've already skipped
11594 // past content, then we can return a list node.
11595 if (breakpoint > parser->current.start) {
11596 parser->current.end = breakpoint;
11597 pm_token_buffer_flush(parser, &token_buffer);
11598 LEX(PM_TOKEN_STRING_CONTENT);
11599 }
11600
11601 // Otherwise, switch back to the default state and return
11602 // the end of the list.
11603 parser->current.end = breakpoint + 1;
11604
11605 // If the terminator is a newline (i.e. the list delimiter
11606 // was a newline), then we need to record it so that line
11607 // numbers after the list remain accurate.
11608 if (*breakpoint == '\n') {
11609 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11610 }
11611
11612 lex_mode_pop(parser);
11613 lex_state_set(parser, PM_LEX_STATE_END);
11614 LEX(PM_TOKEN_STRING_END);
11615 }
11616
11617 // If we hit a null byte, skip directly past it.
11618 if (*breakpoint == '\0') {
11619 breakpoint = pm_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1), true);
11620 continue;
11621 }
11622
11623 // If we hit escapes, then we need to treat the next token
11624 // literally. In this case we'll skip past the next character
11625 // and find the next breakpoint.
11626 if (*breakpoint == '\\') {
11627 parser->current.end = breakpoint + 1;
11628
11629 // If we've hit the end of the file, then break out of the
11630 // loop by setting the breakpoint to NULL.
11631 if (parser->current.end == parser->end) {
11632 breakpoint = NULL;
11633 continue;
11634 }
11635
11636 pm_token_buffer_escape(parser, &token_buffer);
11637 uint8_t peeked = peek(parser);
11638
11639 switch (peeked) {
11640 case ' ':
11641 case '\f':
11642 case '\t':
11643 case '\v':
11644 case '\\':
11645 pm_token_buffer_push_byte(&token_buffer, peeked);
11646 parser->current.end++;
11647 break;
11648 case '\r':
11649 parser->current.end++;
11650 if (peek(parser) != '\n') {
11651 pm_token_buffer_push_byte(&token_buffer, '\r');
11652 break;
11653 }
11655 case '\n':
11656 pm_token_buffer_push_byte(&token_buffer, '\n');
11657
11658 if (parser->heredoc_end) {
11659 // ... if we are on the same line as a heredoc,
11660 // flush the heredoc and continue parsing after
11661 // heredoc_end.
11662 parser_flush_heredoc_end(parser);
11663 pm_token_buffer_copy(parser, &token_buffer);
11664 LEX(PM_TOKEN_STRING_CONTENT);
11665 } else {
11666 // ... else track the newline.
11667 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11668 }
11669
11670 parser->current.end++;
11671 break;
11672 default:
11673 if (peeked == lex_mode->as.list.incrementor || peeked == lex_mode->as.list.terminator) {
11674 pm_token_buffer_push_byte(&token_buffer, peeked);
11675 parser->current.end++;
11676 } else if (lex_mode->as.list.interpolation) {
11677 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
11678 } else {
11679 pm_token_buffer_push_byte(&token_buffer, '\\');
11680 pm_token_buffer_push_escaped(&token_buffer, parser);
11681 }
11682
11683 break;
11684 }
11685
11686 token_buffer.cursor = parser->current.end;
11687 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11688 continue;
11689 }
11690
11691 // If we hit a #, then we will attempt to lex interpolation.
11692 if (*breakpoint == '#') {
11693 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11694
11695 if (!type) {
11696 // If we haven't returned at this point then we had something
11697 // that looked like an interpolated class or instance variable
11698 // like "#@" but wasn't actually. In this case we'll just skip
11699 // to the next breakpoint.
11700 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11701 continue;
11702 }
11703
11704 if (type == PM_TOKEN_STRING_CONTENT) {
11705 pm_token_buffer_flush(parser, &token_buffer);
11706 }
11707
11708 LEX(type);
11709 }
11710
11711 // If we've hit the incrementor, then we need to skip past it
11712 // and find the next breakpoint.
11713 assert(*breakpoint == lex_mode->as.list.incrementor);
11714 parser->current.end = breakpoint + 1;
11715 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
11716 lex_mode->as.list.nesting++;
11717 continue;
11718 }
11719
11720 if (parser->current.end > parser->current.start) {
11721 pm_token_buffer_flush(parser, &token_buffer);
11722 LEX(PM_TOKEN_STRING_CONTENT);
11723 }
11724
11725 // If we were unable to find a breakpoint, then this token hits the
11726 // end of the file.
11727 parser->current.end = parser->end;
11728 pm_token_buffer_flush(parser, &token_buffer);
11729 LEX(PM_TOKEN_STRING_CONTENT);
11730 }
11731 case PM_LEX_REGEXP: {
11732 // First, we'll set to start of this token to be the current end.
11733 if (parser->next_start == NULL) {
11734 parser->current.start = parser->current.end;
11735 } else {
11736 parser->current.start = parser->next_start;
11737 parser->current.end = parser->next_start;
11738 parser->next_start = NULL;
11739 }
11740
11741 // We'll check if we're at the end of the file. If we are, then we
11742 // need to return the EOF token.
11743 if (parser->current.end >= parser->end) {
11744 LEX(PM_TOKEN_EOF);
11745 }
11746
11747 // Get a reference to the current mode.
11748 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11749
11750 // These are the places where we need to split up the content of the
11751 // regular expression. We'll use strpbrk to find the first of these
11752 // characters.
11753 const uint8_t *breakpoints = lex_mode->as.regexp.breakpoints;
11754 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11755 pm_regexp_token_buffer_t token_buffer = { 0 };
11756
11757 while (breakpoint != NULL) {
11758 uint8_t term = lex_mode->as.regexp.terminator;
11759 bool is_terminator = (*breakpoint == term);
11760
11761 // If the terminator is newline, we need to consider \r\n _also_ a newline
11762 // For example: `%\nfoo\r\n`
11763 // The string should be "foo", not "foo\r"
11764 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
11765 if (term == '\n') {
11766 is_terminator = true;
11767 }
11768
11769 // If the terminator is a CR, but we see a CRLF, we need to
11770 // treat the CRLF as a newline, meaning this is _not_ the
11771 // terminator
11772 if (term == '\r') {
11773 is_terminator = false;
11774 }
11775 }
11776
11777 // If we hit the terminator, we need to determine what kind of
11778 // token to return.
11779 if (is_terminator) {
11780 if (lex_mode->as.regexp.nesting > 0) {
11781 parser->current.end = breakpoint + 1;
11782 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11783 lex_mode->as.regexp.nesting--;
11784 continue;
11785 }
11786
11787 // Here we've hit the terminator. If we have already consumed
11788 // content then we need to return that content as string content
11789 // first.
11790 if (breakpoint > parser->current.start) {
11791 parser->current.end = breakpoint;
11792 pm_regexp_token_buffer_flush(parser, &token_buffer);
11793 LEX(PM_TOKEN_STRING_CONTENT);
11794 }
11795
11796 // Check here if we need to track the newline.
11797 size_t eol_length = match_eol_at(parser, breakpoint);
11798 if (eol_length) {
11799 parser->current.end = breakpoint + eol_length;
11800
11801 // Track the newline if we're not in a heredoc that
11802 // would have already have added the newline to the
11803 // list.
11804 if (parser->heredoc_end == NULL) {
11805 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
11806 }
11807 } else {
11808 parser->current.end = breakpoint + 1;
11809 }
11810
11811 // Since we've hit the terminator of the regular expression,
11812 // we now need to parse the options.
11813 parser->current.end += pm_strspn_regexp_option(parser->current.end, parser->end - parser->current.end);
11814
11815 lex_mode_pop(parser);
11816 lex_state_set(parser, PM_LEX_STATE_END);
11817 LEX(PM_TOKEN_REGEXP_END);
11818 }
11819
11820 // If we've hit the incrementor, then we need to skip past it
11821 // and find the next breakpoint.
11822 if (*breakpoint && *breakpoint == lex_mode->as.regexp.incrementor) {
11823 parser->current.end = breakpoint + 1;
11824 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11825 lex_mode->as.regexp.nesting++;
11826 continue;
11827 }
11828
11829 switch (*breakpoint) {
11830 case '\0':
11831 // If we hit a null byte, skip directly past it.
11832 parser->current.end = breakpoint + 1;
11833 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11834 break;
11835 case '\r':
11836 if (peek_at(parser, breakpoint + 1) != '\n') {
11837 parser->current.end = breakpoint + 1;
11838 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11839 break;
11840 }
11841
11842 breakpoint++;
11843 parser->current.end = breakpoint;
11844 pm_regexp_token_buffer_escape(parser, &token_buffer);
11845 token_buffer.base.cursor = breakpoint;
11846
11848 case '\n':
11849 // If we've hit a newline, then we need to track that in
11850 // the list of newlines.
11851 if (parser->heredoc_end == NULL) {
11852 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
11853 parser->current.end = breakpoint + 1;
11854 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11855 break;
11856 }
11857
11858 parser->current.end = breakpoint + 1;
11859 parser_flush_heredoc_end(parser);
11860 pm_regexp_token_buffer_flush(parser, &token_buffer);
11861 LEX(PM_TOKEN_STRING_CONTENT);
11862 case '\\': {
11863 // If we hit escapes, then we need to treat the next
11864 // token literally. In this case we'll skip past the
11865 // next character and find the next breakpoint.
11866 parser->current.end = breakpoint + 1;
11867
11868 // If we've hit the end of the file, then break out of
11869 // the loop by setting the breakpoint to NULL.
11870 if (parser->current.end == parser->end) {
11871 breakpoint = NULL;
11872 break;
11873 }
11874
11875 pm_regexp_token_buffer_escape(parser, &token_buffer);
11876 uint8_t peeked = peek(parser);
11877
11878 switch (peeked) {
11879 case '\r':
11880 parser->current.end++;
11881 if (peek(parser) != '\n') {
11882 if (lex_mode->as.regexp.terminator != '\r') {
11883 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11884 }
11885 pm_regexp_token_buffer_push_byte(&token_buffer, '\r');
11886 pm_token_buffer_push_byte(&token_buffer.base, '\r');
11887 break;
11888 }
11890 case '\n':
11891 if (parser->heredoc_end) {
11892 // ... if we are on the same line as a heredoc,
11893 // flush the heredoc and continue parsing after
11894 // heredoc_end.
11895 parser_flush_heredoc_end(parser);
11896 pm_regexp_token_buffer_copy(parser, &token_buffer);
11897 LEX(PM_TOKEN_STRING_CONTENT);
11898 } else {
11899 // ... else track the newline.
11900 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
11901 }
11902
11903 parser->current.end++;
11904 break;
11905 case 'c':
11906 case 'C':
11907 case 'M':
11908 case 'u':
11909 case 'x':
11910 escape_read(parser, &token_buffer.regexp_buffer, &token_buffer.base.buffer, PM_ESCAPE_FLAG_REGEXP);
11911 break;
11912 default:
11913 if (lex_mode->as.regexp.terminator == peeked) {
11914 // Some characters when they are used as the
11915 // terminator also receive an escape. They are
11916 // enumerated here.
11917 switch (peeked) {
11918 case '$': case ')': case '*': case '+':
11919 case '.': case '>': case '?': case ']':
11920 case '^': case '|': case '}':
11921 pm_token_buffer_push_byte(&token_buffer.base, '\\');
11922 break;
11923 default:
11924 break;
11925 }
11926
11927 pm_regexp_token_buffer_push_byte(&token_buffer, peeked);
11928 pm_token_buffer_push_byte(&token_buffer.base, peeked);
11929 parser->current.end++;
11930 break;
11931 }
11932
11933 if (peeked < 0x80) pm_token_buffer_push_byte(&token_buffer.base, '\\');
11934 pm_regexp_token_buffer_push_escaped(&token_buffer, parser);
11935 break;
11936 }
11937
11938 token_buffer.base.cursor = parser->current.end;
11939 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11940 break;
11941 }
11942 case '#': {
11943 // If we hit a #, then we will attempt to lex
11944 // interpolation.
11945 pm_token_type_t type = lex_interpolation(parser, breakpoint);
11946
11947 if (!type) {
11948 // If we haven't returned at this point then we had
11949 // something that looked like an interpolated class or
11950 // instance variable like "#@" but wasn't actually. In
11951 // this case we'll just skip to the next breakpoint.
11952 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, false);
11953 break;
11954 }
11955
11956 if (type == PM_TOKEN_STRING_CONTENT) {
11957 pm_regexp_token_buffer_flush(parser, &token_buffer);
11958 }
11959
11960 LEX(type);
11961 }
11962 default:
11963 assert(false && "unreachable");
11964 break;
11965 }
11966 }
11967
11968 if (parser->current.end > parser->current.start) {
11969 pm_regexp_token_buffer_flush(parser, &token_buffer);
11970 LEX(PM_TOKEN_STRING_CONTENT);
11971 }
11972
11973 // If we were unable to find a breakpoint, then this token hits the
11974 // end of the file.
11975 parser->current.end = parser->end;
11976 pm_regexp_token_buffer_flush(parser, &token_buffer);
11977 LEX(PM_TOKEN_STRING_CONTENT);
11978 }
11979 case PM_LEX_STRING: {
11980 // First, we'll set to start of this token to be the current end.
11981 if (parser->next_start == NULL) {
11982 parser->current.start = parser->current.end;
11983 } else {
11984 parser->current.start = parser->next_start;
11985 parser->current.end = parser->next_start;
11986 parser->next_start = NULL;
11987 }
11988
11989 // We'll check if we're at the end of the file. If we are, then we need to
11990 // return the EOF token.
11991 if (parser->current.end >= parser->end) {
11992 LEX(PM_TOKEN_EOF);
11993 }
11994
11995 // These are the places where we need to split up the content of the
11996 // string. We'll use strpbrk to find the first of these characters.
11997 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
11998 const uint8_t *breakpoints = lex_mode->as.string.breakpoints;
11999 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12000
12001 // If we haven't found an escape yet, then this buffer will be
12002 // unallocated since we can refer directly to the source string.
12003 pm_token_buffer_t token_buffer = { 0 };
12004
12005 while (breakpoint != NULL) {
12006 // If we hit the incrementor, then we'll increment then nesting and
12007 // continue lexing.
12008 if (lex_mode->as.string.incrementor != '\0' && *breakpoint == lex_mode->as.string.incrementor) {
12009 lex_mode->as.string.nesting++;
12010 parser->current.end = breakpoint + 1;
12011 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12012 continue;
12013 }
12014
12015 uint8_t term = lex_mode->as.string.terminator;
12016 bool is_terminator = (*breakpoint == term);
12017
12018 // If the terminator is newline, we need to consider \r\n _also_ a newline
12019 // For example: `%r\nfoo\r\n`
12020 // The string should be /foo/, not /foo\r/
12021 if (*breakpoint == '\r' && peek_at(parser, breakpoint + 1) == '\n') {
12022 if (term == '\n') {
12023 is_terminator = true;
12024 }
12025
12026 // If the terminator is a CR, but we see a CRLF, we need to
12027 // treat the CRLF as a newline, meaning this is _not_ the
12028 // terminator
12029 if (term == '\r') {
12030 is_terminator = false;
12031 }
12032 }
12033
12034 // Note that we have to check the terminator here first because we could
12035 // potentially be parsing a % string that has a # character as the
12036 // terminator.
12037 if (is_terminator) {
12038 // If this terminator doesn't actually close the string, then we need
12039 // to continue on past it.
12040 if (lex_mode->as.string.nesting > 0) {
12041 parser->current.end = breakpoint + 1;
12042 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12043 lex_mode->as.string.nesting--;
12044 continue;
12045 }
12046
12047 // Here we've hit the terminator. If we have already consumed content
12048 // then we need to return that content as string content first.
12049 if (breakpoint > parser->current.start) {
12050 parser->current.end = breakpoint;
12051 pm_token_buffer_flush(parser, &token_buffer);
12052 LEX(PM_TOKEN_STRING_CONTENT);
12053 }
12054
12055 // Otherwise we need to switch back to the parent lex mode and
12056 // return the end of the string.
12057 size_t eol_length = match_eol_at(parser, breakpoint);
12058 if (eol_length) {
12059 parser->current.end = breakpoint + eol_length;
12060
12061 // Track the newline if we're not in a heredoc that
12062 // would have already have added the newline to the
12063 // list.
12064 if (parser->heredoc_end == NULL) {
12065 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current));
12066 }
12067 } else {
12068 parser->current.end = breakpoint + 1;
12069 }
12070
12071 if (lex_mode->as.string.label_allowed && (peek(parser) == ':') && (peek_offset(parser, 1) != ':')) {
12072 parser->current.end++;
12073 lex_state_set(parser, PM_LEX_STATE_ARG | PM_LEX_STATE_LABELED);
12074 lex_mode_pop(parser);
12075 LEX(PM_TOKEN_LABEL_END);
12076 }
12077
12078 // When the delimiter itself is a newline, we won't
12079 // get a chance to flush heredocs in the usual places since
12080 // the newline is already consumed.
12081 if (term == '\n' && parser->heredoc_end) {
12082 parser_flush_heredoc_end(parser);
12083 }
12084
12085 lex_state_set(parser, PM_LEX_STATE_END);
12086 lex_mode_pop(parser);
12087 LEX(PM_TOKEN_STRING_END);
12088 }
12089
12090 switch (*breakpoint) {
12091 case '\0':
12092 // Skip directly past the null character.
12093 parser->current.end = breakpoint + 1;
12094 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12095 break;
12096 case '\r':
12097 if (peek_at(parser, breakpoint + 1) != '\n') {
12098 parser->current.end = breakpoint + 1;
12099 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12100 break;
12101 }
12102
12103 // If we hit a \r\n sequence, then we need to treat it
12104 // as a newline.
12105 breakpoint++;
12106 parser->current.end = breakpoint;
12107 pm_token_buffer_escape(parser, &token_buffer);
12108 token_buffer.cursor = breakpoint;
12109
12111 case '\n':
12112 // When we hit a newline, we need to flush any potential
12113 // heredocs. Note that this has to happen after we check
12114 // for the terminator in case the terminator is a
12115 // newline character.
12116 if (parser->heredoc_end == NULL) {
12117 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12118 parser->current.end = breakpoint + 1;
12119 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12120 break;
12121 }
12122
12123 parser->current.end = breakpoint + 1;
12124 parser_flush_heredoc_end(parser);
12125 pm_token_buffer_flush(parser, &token_buffer);
12126 LEX(PM_TOKEN_STRING_CONTENT);
12127 case '\\': {
12128 // Here we hit escapes.
12129 parser->current.end = breakpoint + 1;
12130
12131 // If we've hit the end of the file, then break out of
12132 // the loop by setting the breakpoint to NULL.
12133 if (parser->current.end == parser->end) {
12134 breakpoint = NULL;
12135 continue;
12136 }
12137
12138 pm_token_buffer_escape(parser, &token_buffer);
12139 uint8_t peeked = peek(parser);
12140
12141 switch (peeked) {
12142 case '\\':
12143 pm_token_buffer_push_byte(&token_buffer, '\\');
12144 parser->current.end++;
12145 break;
12146 case '\r':
12147 parser->current.end++;
12148 if (peek(parser) != '\n') {
12149 if (!lex_mode->as.string.interpolation) {
12150 pm_token_buffer_push_byte(&token_buffer, '\\');
12151 }
12152 pm_token_buffer_push_byte(&token_buffer, '\r');
12153 break;
12154 }
12156 case '\n':
12157 if (!lex_mode->as.string.interpolation) {
12158 pm_token_buffer_push_byte(&token_buffer, '\\');
12159 pm_token_buffer_push_byte(&token_buffer, '\n');
12160 }
12161
12162 if (parser->heredoc_end) {
12163 // ... if we are on the same line as a heredoc,
12164 // flush the heredoc and continue parsing after
12165 // heredoc_end.
12166 parser_flush_heredoc_end(parser);
12167 pm_token_buffer_copy(parser, &token_buffer);
12168 LEX(PM_TOKEN_STRING_CONTENT);
12169 } else {
12170 // ... else track the newline.
12171 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, PM_TOKEN_END(parser, &parser->current) + 1);
12172 }
12173
12174 parser->current.end++;
12175 break;
12176 default:
12177 if (lex_mode->as.string.incrementor != '\0' && peeked == lex_mode->as.string.incrementor) {
12178 pm_token_buffer_push_byte(&token_buffer, peeked);
12179 parser->current.end++;
12180 } else if (lex_mode->as.string.terminator != '\0' && peeked == lex_mode->as.string.terminator) {
12181 pm_token_buffer_push_byte(&token_buffer, peeked);
12182 parser->current.end++;
12183 } else if (lex_mode->as.string.interpolation) {
12184 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12185 } else {
12186 pm_token_buffer_push_byte(&token_buffer, '\\');
12187 pm_token_buffer_push_escaped(&token_buffer, parser);
12188 }
12189
12190 break;
12191 }
12192
12193 token_buffer.cursor = parser->current.end;
12194 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12195 break;
12196 }
12197 case '#': {
12198 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12199
12200 if (!type) {
12201 // If we haven't returned at this point then we had something that
12202 // looked like an interpolated class or instance variable like "#@"
12203 // but wasn't actually. In this case we'll just skip to the next
12204 // breakpoint.
12205 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12206 break;
12207 }
12208
12209 if (type == PM_TOKEN_STRING_CONTENT) {
12210 pm_token_buffer_flush(parser, &token_buffer);
12211 }
12212
12213 LEX(type);
12214 }
12215 default:
12216 assert(false && "unreachable");
12217 }
12218 }
12219
12220 if (parser->current.end > parser->current.start) {
12221 pm_token_buffer_flush(parser, &token_buffer);
12222 LEX(PM_TOKEN_STRING_CONTENT);
12223 }
12224
12225 // If we've hit the end of the string, then this is an unterminated
12226 // string. In that case we'll return a string content token.
12227 parser->current.end = parser->end;
12228 pm_token_buffer_flush(parser, &token_buffer);
12229 LEX(PM_TOKEN_STRING_CONTENT);
12230 }
12231 case PM_LEX_HEREDOC: {
12232 // First, we'll set to start of this token.
12233 if (parser->next_start == NULL) {
12234 parser->current.start = parser->current.end;
12235 } else {
12236 parser->current.start = parser->next_start;
12237 parser->current.end = parser->next_start;
12238 parser->heredoc_end = NULL;
12239 parser->next_start = NULL;
12240 }
12241
12242 // Now let's grab the information about the identifier off of the
12243 // current lex mode.
12244 pm_lex_mode_t *lex_mode = parser->lex_modes.current;
12245 pm_heredoc_lex_mode_t *heredoc_lex_mode = &lex_mode->as.heredoc.base;
12246
12247 bool line_continuation = lex_mode->as.heredoc.line_continuation;
12248 lex_mode->as.heredoc.line_continuation = false;
12249
12250 // We'll check if we're at the end of the file. If we are, then we
12251 // will add an error (because we weren't able to find the
12252 // terminator) but still continue parsing so that content after the
12253 // declaration of the heredoc can be parsed.
12254 if (parser->current.end >= parser->end) {
12255 pm_parser_err_heredoc_term(parser, heredoc_lex_mode->ident_start, heredoc_lex_mode->ident_length);
12256 parser->next_start = lex_mode->as.heredoc.next_start;
12257 parser->heredoc_end = parser->current.end;
12258 lex_state_set(parser, PM_LEX_STATE_END);
12259 lex_mode_pop(parser);
12260 LEX(PM_TOKEN_HEREDOC_END);
12261 }
12262
12263 const uint8_t *ident_start = heredoc_lex_mode->ident_start;
12264 size_t ident_length = heredoc_lex_mode->ident_length;
12265
12266 // If we are immediately following a newline and we have hit the
12267 // terminator, then we need to return the ending of the heredoc.
12268 if (current_token_starts_line(parser)) {
12269 const uint8_t *start = parser->current.start;
12270
12271 if (!line_continuation && (start + ident_length <= parser->end)) {
12272 const uint8_t *newline = next_newline(start, parser->end - start);
12273 const uint8_t *ident_end = newline;
12274 const uint8_t *terminator_end = newline;
12275
12276 if (newline == NULL) {
12277 terminator_end = parser->end;
12278 ident_end = parser->end;
12279 } else {
12280 terminator_end++;
12281 if (newline[-1] == '\r') {
12282 ident_end--; // Remove \r
12283 }
12284 }
12285
12286 const uint8_t *terminator_start = ident_end - ident_length;
12287 const uint8_t *cursor = start;
12288
12289 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12290 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12291 cursor++;
12292 }
12293 }
12294
12295 if (
12296 (cursor == terminator_start) &&
12297 (memcmp(terminator_start, ident_start, ident_length) == 0)
12298 ) {
12299 if (newline != NULL) {
12300 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
12301 }
12302
12303 parser->current.end = terminator_end;
12304 if (*lex_mode->as.heredoc.next_start == '\\') {
12305 parser->next_start = NULL;
12306 } else {
12307 parser->next_start = lex_mode->as.heredoc.next_start;
12308 parser->heredoc_end = parser->current.end;
12309 }
12310
12311 lex_state_set(parser, PM_LEX_STATE_END);
12312 lex_mode_pop(parser);
12313 LEX(PM_TOKEN_HEREDOC_END);
12314 }
12315 }
12316
12317 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, heredoc_lex_mode->indent);
12318 if (
12319 heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE &&
12320 lex_mode->as.heredoc.common_whitespace != NULL &&
12321 (*lex_mode->as.heredoc.common_whitespace > whitespace) &&
12322 peek_at(parser, start) != '\n'
12323 ) {
12324 *lex_mode->as.heredoc.common_whitespace = whitespace;
12325 }
12326 }
12327
12328 // Otherwise we'll be parsing string content. These are the places
12329 // where we need to split up the content of the heredoc. We'll use
12330 // strpbrk to find the first of these characters.
12331 uint8_t breakpoints[PM_STRPBRK_CACHE_SIZE] = "\r\n\\#";
12332
12333 pm_heredoc_quote_t quote = heredoc_lex_mode->quote;
12334 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12335 breakpoints[3] = '\0';
12336 }
12337
12338 const uint8_t *breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12339 pm_token_buffer_t token_buffer = { 0 };
12340 bool was_line_continuation = false;
12341
12342 while (breakpoint != NULL) {
12343 switch (*breakpoint) {
12344 case '\0':
12345 // Skip directly past the null character.
12346 parser->current.end = breakpoint + 1;
12347 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12348 break;
12349 case '\r':
12350 parser->current.end = breakpoint + 1;
12351
12352 if (peek_at(parser, breakpoint + 1) != '\n') {
12353 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12354 break;
12355 }
12356
12357 // If we hit a \r\n sequence, then we want to replace it
12358 // with a single \n character in the final string.
12359 breakpoint++;
12360 pm_token_buffer_escape(parser, &token_buffer);
12361 token_buffer.cursor = breakpoint;
12362
12364 case '\n': {
12365 if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) {
12366 parser_flush_heredoc_end(parser);
12367 parser->current.end = breakpoint + 1;
12368 pm_token_buffer_flush(parser, &token_buffer);
12369 LEX(PM_TOKEN_STRING_CONTENT);
12370 }
12371
12372 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(breakpoint - parser->start + 1));
12373
12374 // If we have a - or ~ heredoc, then we can match after
12375 // some leading whitespace.
12376 const uint8_t *start = breakpoint + 1;
12377
12378 if (!was_line_continuation && (start + ident_length <= parser->end)) {
12379 // We want to match the terminator starting from the end of the line in case
12380 // there is whitespace in the ident such as <<-' DOC' or <<~' DOC'.
12381 const uint8_t *newline = next_newline(start, parser->end - start);
12382
12383 if (newline == NULL) {
12384 newline = parser->end;
12385 } else if (newline[-1] == '\r') {
12386 newline--; // Remove \r
12387 }
12388
12389 // Start of a possible terminator.
12390 const uint8_t *terminator_start = newline - ident_length;
12391
12392 // Cursor to check for the leading whitespace. We skip the
12393 // leading whitespace if we have a - or ~ heredoc.
12394 const uint8_t *cursor = start;
12395
12396 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_DASH || heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12397 while (cursor < terminator_start && pm_char_is_inline_whitespace(*cursor)) {
12398 cursor++;
12399 }
12400 }
12401
12402 if (
12403 cursor == terminator_start &&
12404 (memcmp(terminator_start, ident_start, ident_length) == 0)
12405 ) {
12406 parser->current.end = breakpoint + 1;
12407 pm_token_buffer_flush(parser, &token_buffer);
12408 LEX(PM_TOKEN_STRING_CONTENT);
12409 }
12410 }
12411
12412 size_t whitespace = pm_heredoc_strspn_inline_whitespace(parser, &start, lex_mode->as.heredoc.base.indent);
12413
12414 // If we have hit a newline that is followed by a valid
12415 // terminator, then we need to return the content of the
12416 // heredoc here as string content. Then, the next time a
12417 // token is lexed, it will match again and return the
12418 // end of the heredoc.
12419 if (lex_mode->as.heredoc.base.indent == PM_HEREDOC_INDENT_TILDE) {
12420 if ((lex_mode->as.heredoc.common_whitespace != NULL) && (*lex_mode->as.heredoc.common_whitespace > whitespace) && peek_at(parser, start) != '\n') {
12421 *lex_mode->as.heredoc.common_whitespace = whitespace;
12422 }
12423
12424 parser->current.end = breakpoint + 1;
12425 pm_token_buffer_flush(parser, &token_buffer);
12426 LEX(PM_TOKEN_STRING_CONTENT);
12427 }
12428
12429 // Otherwise we hit a newline and it wasn't followed by
12430 // a terminator, so we can continue parsing.
12431 parser->current.end = breakpoint + 1;
12432 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12433 break;
12434 }
12435 case '\\': {
12436 // If we hit an escape, then we need to skip past
12437 // however many characters the escape takes up. However
12438 // it's important that if \n or \r\n are escaped, we
12439 // stop looping before the newline and not after the
12440 // newline so that we can still potentially find the
12441 // terminator of the heredoc.
12442 parser->current.end = breakpoint + 1;
12443
12444 // If we've hit the end of the file, then break out of
12445 // the loop by setting the breakpoint to NULL.
12446 if (parser->current.end == parser->end) {
12447 breakpoint = NULL;
12448 continue;
12449 }
12450
12451 pm_token_buffer_escape(parser, &token_buffer);
12452 uint8_t peeked = peek(parser);
12453
12454 if (quote == PM_HEREDOC_QUOTE_SINGLE) {
12455 switch (peeked) {
12456 case '\r':
12457 parser->current.end++;
12458 if (peek(parser) != '\n') {
12459 pm_token_buffer_push_byte(&token_buffer, '\\');
12460 pm_token_buffer_push_byte(&token_buffer, '\r');
12461 break;
12462 }
12464 case '\n':
12465 pm_token_buffer_push_byte(&token_buffer, '\\');
12466 pm_token_buffer_push_byte(&token_buffer, '\n');
12467 token_buffer.cursor = parser->current.end + 1;
12468 breakpoint = parser->current.end;
12469 continue;
12470 default:
12471 pm_token_buffer_push_byte(&token_buffer, '\\');
12472 pm_token_buffer_push_escaped(&token_buffer, parser);
12473 break;
12474 }
12475 } else {
12476 switch (peeked) {
12477 case '\r':
12478 parser->current.end++;
12479 if (peek(parser) != '\n') {
12480 pm_token_buffer_push_byte(&token_buffer, '\r');
12481 break;
12482 }
12484 case '\n':
12485 // If we are in a tilde here, we should
12486 // break out of the loop and return the
12487 // string content.
12488 if (heredoc_lex_mode->indent == PM_HEREDOC_INDENT_TILDE) {
12489 const uint8_t *end = parser->current.end;
12490
12491 if (parser->heredoc_end == NULL) {
12492 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(end - parser->start + 1));
12493 }
12494
12495 // Here we want the buffer to only
12496 // include up to the backslash.
12497 parser->current.end = breakpoint;
12498 pm_token_buffer_flush(parser, &token_buffer);
12499
12500 // Now we can advance the end of the
12501 // token past the newline.
12502 parser->current.end = end + 1;
12503 lex_mode->as.heredoc.line_continuation = true;
12504 LEX(PM_TOKEN_STRING_CONTENT);
12505 }
12506
12507 was_line_continuation = true;
12508 token_buffer.cursor = parser->current.end + 1;
12509 breakpoint = parser->current.end;
12510 continue;
12511 default:
12512 escape_read(parser, &token_buffer.buffer, NULL, PM_ESCAPE_FLAG_NONE);
12513 break;
12514 }
12515 }
12516
12517 token_buffer.cursor = parser->current.end;
12518 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12519 break;
12520 }
12521 case '#': {
12522 pm_token_type_t type = lex_interpolation(parser, breakpoint);
12523
12524 if (!type) {
12525 // If we haven't returned at this point then we had
12526 // something that looked like an interpolated class
12527 // or instance variable like "#@" but wasn't
12528 // actually. In this case we'll just skip to the
12529 // next breakpoint.
12530 breakpoint = pm_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end, true);
12531 break;
12532 }
12533
12534 if (type == PM_TOKEN_STRING_CONTENT) {
12535 pm_token_buffer_flush(parser, &token_buffer);
12536 }
12537
12538 LEX(type);
12539 }
12540 default:
12541 assert(false && "unreachable");
12542 }
12543
12544 was_line_continuation = false;
12545 }
12546
12547 if (parser->current.end > parser->current.start) {
12548 parser->current.end = parser->end;
12549 pm_token_buffer_flush(parser, &token_buffer);
12550 LEX(PM_TOKEN_STRING_CONTENT);
12551 }
12552
12553 // If we've hit the end of the string, then this is an unterminated
12554 // heredoc. In that case we'll return a string content token.
12555 parser->current.end = parser->end;
12556 pm_token_buffer_flush(parser, &token_buffer);
12557 LEX(PM_TOKEN_STRING_CONTENT);
12558 }
12559 }
12560
12561 assert(false && "unreachable");
12562}
12563
12564#undef LEX
12565
12566/******************************************************************************/
12567/* Parse functions */
12568/******************************************************************************/
12569
12578typedef enum {
12579 PM_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator
12580 PM_BINDING_POWER_STATEMENT = 2,
12581 PM_BINDING_POWER_MODIFIER_RESCUE = 4, // rescue
12582 PM_BINDING_POWER_MODIFIER = 6, // if unless until while
12583 PM_BINDING_POWER_COMPOSITION = 8, // and or
12584 PM_BINDING_POWER_NOT = 10, // not
12585 PM_BINDING_POWER_MATCH = 12, // => in
12586 PM_BINDING_POWER_DEFINED = 14, // defined?
12587 PM_BINDING_POWER_MULTI_ASSIGNMENT = 16, // =
12588 PM_BINDING_POWER_ASSIGNMENT = 18, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **=
12589 PM_BINDING_POWER_TERNARY = 20, // ?:
12590 PM_BINDING_POWER_RANGE = 22, // .. ...
12591 PM_BINDING_POWER_LOGICAL_OR = 24, // ||
12592 PM_BINDING_POWER_LOGICAL_AND = 26, // &&
12593 PM_BINDING_POWER_EQUALITY = 28, // <=> == === != =~ !~
12594 PM_BINDING_POWER_COMPARISON = 30, // > >= < <=
12595 PM_BINDING_POWER_BITWISE_OR = 32, // | ^
12596 PM_BINDING_POWER_BITWISE_AND = 34, // &
12597 PM_BINDING_POWER_SHIFT = 36, // << >>
12598 PM_BINDING_POWER_TERM = 38, // + -
12599 PM_BINDING_POWER_FACTOR = 40, // * / %
12600 PM_BINDING_POWER_UMINUS = 42, // -@
12601 PM_BINDING_POWER_EXPONENT = 44, // **
12602 PM_BINDING_POWER_UNARY = 46, // ! ~ +@
12603 PM_BINDING_POWER_INDEX = 48, // [] []=
12604 PM_BINDING_POWER_CALL = 50, // :: .
12605 PM_BINDING_POWER_MAX = 52
12606} pm_binding_power_t;
12607
12612typedef struct {
12614 pm_binding_power_t left;
12615
12617 pm_binding_power_t right;
12618
12621
12628
12629#define BINDING_POWER_ASSIGNMENT { PM_BINDING_POWER_UNARY, PM_BINDING_POWER_ASSIGNMENT, true, false }
12630#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, false }
12631#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true, false }
12632#define NON_ASSOCIATIVE(precedence) { precedence, precedence + 1, true, true }
12633#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false, false }
12634
12635pm_binding_powers_t pm_binding_powers[PM_TOKEN_MAXIMUM] = {
12636 // rescue
12637 [PM_TOKEN_KEYWORD_RESCUE_MODIFIER] = { PM_BINDING_POWER_MODIFIER_RESCUE, PM_BINDING_POWER_COMPOSITION, true, false },
12638
12639 // if unless until while
12640 [PM_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12641 [PM_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12642 [PM_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12643 [PM_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_MODIFIER),
12644
12645 // and or
12646 [PM_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12647 [PM_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPOSITION),
12648
12649 // => in
12650 [PM_TOKEN_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12651 [PM_TOKEN_KEYWORD_IN] = NON_ASSOCIATIVE(PM_BINDING_POWER_MATCH),
12652
12653 // &&= &= ^= = >>= <<= -= %= |= ||= += /= *= **=
12654 [PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12655 [PM_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT,
12656 [PM_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT,
12657 [PM_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT,
12658 [PM_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT,
12659 [PM_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12660 [PM_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12661 [PM_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT,
12662 [PM_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12663 [PM_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT,
12664 [PM_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT,
12665 [PM_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT,
12666 [PM_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12667 [PM_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT,
12668
12669 // ?:
12670 [PM_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_TERNARY),
12671
12672 // .. ...
12673 [PM_TOKEN_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12674 [PM_TOKEN_DOT_DOT_DOT] = NON_ASSOCIATIVE(PM_BINDING_POWER_RANGE),
12675 [PM_TOKEN_UDOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12676 [PM_TOKEN_UDOT_DOT_DOT] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_LOGICAL_OR),
12677
12678 // ||
12679 [PM_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_OR),
12680
12681 // &&
12682 [PM_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_LOGICAL_AND),
12683
12684 // != !~ == === =~ <=>
12685 [PM_TOKEN_BANG_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12686 [PM_TOKEN_BANG_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12687 [PM_TOKEN_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12688 [PM_TOKEN_EQUAL_EQUAL_EQUAL] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12689 [PM_TOKEN_EQUAL_TILDE] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12690 [PM_TOKEN_LESS_EQUAL_GREATER] = NON_ASSOCIATIVE(PM_BINDING_POWER_EQUALITY),
12691
12692 // > >= < <=
12693 [PM_TOKEN_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12694 [PM_TOKEN_GREATER_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12695 [PM_TOKEN_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12696 [PM_TOKEN_LESS_EQUAL] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_COMPARISON),
12697
12698 // ^ |
12699 [PM_TOKEN_CARET] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12700 [PM_TOKEN_PIPE] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_OR),
12701
12702 // &
12703 [PM_TOKEN_AMPERSAND] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_BITWISE_AND),
12704
12705 // >> <<
12706 [PM_TOKEN_GREATER_GREATER] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12707 [PM_TOKEN_LESS_LESS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_SHIFT),
12708
12709 // - +
12710 [PM_TOKEN_MINUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12711 [PM_TOKEN_PLUS] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_TERM),
12712
12713 // % / *
12714 [PM_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12715 [PM_TOKEN_SLASH] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12716 [PM_TOKEN_STAR] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_FACTOR),
12717 [PM_TOKEN_USTAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_FACTOR),
12718
12719 // -@
12720 [PM_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UMINUS),
12721 [PM_TOKEN_UMINUS_NUM] = { PM_BINDING_POWER_UMINUS, PM_BINDING_POWER_MAX, false, false },
12722
12723 // **
12724 [PM_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_EXPONENT),
12725 [PM_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12726
12727 // ! ~ +@
12728 [PM_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12729 [PM_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12730 [PM_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(PM_BINDING_POWER_UNARY),
12731
12732 // [
12733 [PM_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(PM_BINDING_POWER_INDEX),
12734
12735 // :: . &.
12736 [PM_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12737 [PM_TOKEN_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL),
12738 [PM_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(PM_BINDING_POWER_CALL)
12739};
12740
12741#undef BINDING_POWER_ASSIGNMENT
12742#undef LEFT_ASSOCIATIVE
12743#undef RIGHT_ASSOCIATIVE
12744#undef RIGHT_ASSOCIATIVE_UNARY
12745
12749static PRISM_INLINE bool
12750match1(const pm_parser_t *parser, pm_token_type_t type) {
12751 return parser->current.type == type;
12752}
12753
12757static PRISM_INLINE bool
12758match2(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12759 return match1(parser, type1) || match1(parser, type2);
12760}
12761
12765static PRISM_INLINE bool
12766match3(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3) {
12767 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3);
12768}
12769
12773static PRISM_INLINE bool
12774match4(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) {
12775 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4);
12776}
12777
12781static PRISM_INLINE bool
12782match5(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) {
12783 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5);
12784}
12785
12789static PRISM_INLINE bool
12790match6(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) {
12791 return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6);
12792}
12793
12797static PRISM_INLINE bool
12798match8(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) {
12799 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);
12800}
12801
12808static bool
12809accept1(pm_parser_t *parser, pm_token_type_t type) {
12810 if (match1(parser, type)) {
12811 parser_lex(parser);
12812 return true;
12813 }
12814 return false;
12815}
12816
12821static PRISM_INLINE bool
12822accept2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2) {
12823 if (match2(parser, type1, type2)) {
12824 parser_lex(parser);
12825 return true;
12826 }
12827 return false;
12828}
12829
12841static void
12842expect1(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id) {
12843 if (accept1(parser, type)) return;
12844
12845 const uint8_t *location = parser->previous.end;
12846 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12847
12848 parser->previous.start = location;
12849 parser->previous.type = 0;
12850}
12851
12856static void
12857expect2(pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_diagnostic_id_t diag_id) {
12858 if (accept2(parser, type1, type2)) return;
12859
12860 const uint8_t *location = parser->previous.end;
12861 pm_parser_err(parser, U32(location - parser->start), 0, diag_id);
12862
12863 parser->previous.start = location;
12864 parser->previous.type = 0;
12865}
12866
12871static void
12872expect1_heredoc_term(pm_parser_t *parser, const uint8_t *ident_start, size_t ident_length) {
12873 if (match1(parser, PM_TOKEN_HEREDOC_END)) {
12874 parser_lex(parser);
12875 } else {
12876 pm_parser_err_heredoc_term(parser, ident_start, ident_length);
12877 parser->previous.start = parser->previous.end;
12878 parser->previous.type = 0;
12879 }
12880}
12881
12888static void
12889expect1_opening(pm_parser_t *parser, pm_token_type_t type, pm_diagnostic_id_t diag_id, const pm_token_t *opening) {
12890 if (accept1(parser, type)) return;
12891
12892 const uint8_t *start = opening->start;
12893 pm_parser_err(parser, U32(start - parser->start), U32(opening->end - start), diag_id);
12894
12895 parser->previous.start = parser->previous.end;
12896 parser->previous.type = 0;
12897}
12898
12900#define PM_PARSE_ACCEPTS_COMMAND_CALL ((uint8_t) 0x1)
12901#define PM_PARSE_ACCEPTS_LABEL ((uint8_t) 0x2)
12902#define PM_PARSE_ACCEPTS_DO_BLOCK ((uint8_t) 0x4)
12903#define PM_PARSE_IN_ENDLESS_DEF ((uint8_t) 0x8)
12904
12914#define PM_PARSE_ACCEPTS_STATEMENT ((uint8_t) 0x10)
12915
12916static pm_node_t *
12917parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
12918
12923static pm_node_t *
12924parse_value_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
12925 pm_node_t *node = parse_expression(parser, binding_power, flags, diag_id, depth);
12926 pm_assert_value_expression(parser, node);
12927 return node;
12928}
12929
12948static PRISM_INLINE bool
12949token_begins_expression_p(pm_token_type_t type) {
12950 switch (type) {
12951 case PM_TOKEN_EQUAL_GREATER:
12952 case PM_TOKEN_KEYWORD_IN:
12953 // We need to special case this because it is a binary operator that
12954 // should not be marked as beginning an expression.
12955 return false;
12956 case PM_TOKEN_BRACE_RIGHT:
12957 case PM_TOKEN_BRACKET_RIGHT:
12958 case PM_TOKEN_COLON:
12959 case PM_TOKEN_COMMA:
12960 case PM_TOKEN_EMBEXPR_END:
12961 case PM_TOKEN_EOF:
12962 case PM_TOKEN_LAMBDA_BEGIN:
12963 case PM_TOKEN_KEYWORD_DO:
12964 case PM_TOKEN_KEYWORD_DO_BLOCK:
12965 case PM_TOKEN_KEYWORD_DO_LAMBDA:
12966 case PM_TOKEN_KEYWORD_DO_LOOP:
12967 case PM_TOKEN_KEYWORD_END:
12968 case PM_TOKEN_KEYWORD_ELSE:
12969 case PM_TOKEN_KEYWORD_ELSIF:
12970 case PM_TOKEN_KEYWORD_ENSURE:
12971 case PM_TOKEN_KEYWORD_THEN:
12972 case PM_TOKEN_KEYWORD_RESCUE:
12973 case PM_TOKEN_KEYWORD_WHEN:
12974 case PM_TOKEN_NEWLINE:
12975 case PM_TOKEN_PARENTHESIS_RIGHT:
12976 case PM_TOKEN_SEMICOLON:
12977 // The reason we need this short-circuit is because we're using the
12978 // binding powers table to tell us if the subsequent token could
12979 // potentially be the start of an expression. If there _is_ a binding
12980 // power for one of these tokens, then we should remove it from this list
12981 // and let it be handled by the default case below.
12982 assert(pm_binding_powers[type].left == PM_BINDING_POWER_UNSET);
12983 return false;
12984 case PM_TOKEN_UAMPERSAND:
12985 // This is a special case because this unary operator cannot appear
12986 // as a general operator, it only appears in certain circumstances.
12987 return false;
12988 case PM_TOKEN_UCOLON_COLON:
12989 case PM_TOKEN_UMINUS:
12990 case PM_TOKEN_UMINUS_NUM:
12991 case PM_TOKEN_UPLUS:
12992 case PM_TOKEN_BANG:
12993 case PM_TOKEN_TILDE:
12994 case PM_TOKEN_UDOT_DOT:
12995 case PM_TOKEN_UDOT_DOT_DOT:
12996 // These unary tokens actually do have binding power associated with them
12997 // so that we can correctly place them into the precedence order. But we
12998 // want them to be marked as beginning an expression, so we need to
12999 // special case them here.
13000 return true;
13001 default:
13002 return pm_binding_powers[type].left == PM_BINDING_POWER_UNSET;
13003 }
13004}
13005
13021static PRISM_INLINE bool
13022token_begins_pattern_p(pm_token_type_t type) {
13023 return (
13024 token_begins_expression_p(type) ||
13025 type == PM_TOKEN_USTAR ||
13026 type == PM_TOKEN_USTAR_STAR ||
13027 type == PM_TOKEN_CARET
13028 );
13029}
13030
13035static pm_node_t *
13036parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
13037 if (accept1(parser, PM_TOKEN_USTAR)) {
13038 pm_token_t operator = parser->previous;
13039 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));
13040 return UP(pm_splat_node_create(parser, &operator, expression));
13041 }
13042
13043 return parse_value_expression(parser, binding_power, flags, diag_id, depth);
13044}
13045
13046static bool
13047pm_node_unreference_each(const pm_node_t *node, void *data) {
13048 switch (PM_NODE_TYPE(node)) {
13049 /* When we are about to destroy a set of nodes that could potentially
13050 * contain block exits for the current scope, we need to check if they
13051 * are contained in the list of block exits and remove them if they are.
13052 */
13053 case PM_BREAK_NODE:
13054 case PM_NEXT_NODE:
13055 case PM_REDO_NODE: {
13056 pm_parser_t *parser = (pm_parser_t *) data;
13057 size_t index = 0;
13058
13059 while (index < parser->current_block_exits->size) {
13060 pm_node_t *block_exit = parser->current_block_exits->nodes[index];
13061
13062 if (block_exit == node) {
13063 if (index + 1 < parser->current_block_exits->size) {
13064 memmove(
13065 &parser->current_block_exits->nodes[index],
13066 &parser->current_block_exits->nodes[index + 1],
13067 (parser->current_block_exits->size - index - 1) * sizeof(pm_node_t *)
13068 );
13069 }
13070 parser->current_block_exits->size--;
13071
13072 /* Note returning true here because these nodes could have
13073 * arguments that are themselves block exits. */
13074 return true;
13075 }
13076
13077 index++;
13078 }
13079
13080 return true;
13081 }
13082 /* When an implicit local variable is written to or targeted, it becomes
13083 * a regular, named local variable. This branch removes it from the list
13084 * of implicit parameters when that happens. */
13085 case PM_LOCAL_VARIABLE_READ_NODE:
13086 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13087 pm_parser_t *parser = (pm_parser_t *) data;
13088 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
13089
13090 for (size_t index = 0; index < implicit_parameters->size; index++) {
13091 if (implicit_parameters->nodes[index] == node) {
13092 /* If the node is not the last one in the list, we need to
13093 * shift the remaining nodes down to fill the gap. This is
13094 * extremely unlikely to happen. */
13095 if (index != implicit_parameters->size - 1) {
13096 memmove(&implicit_parameters->nodes[index], &implicit_parameters->nodes[index + 1], (implicit_parameters->size - index - 1) * sizeof(pm_node_t *));
13097 }
13098
13099 implicit_parameters->size--;
13100 break;
13101 }
13102 }
13103
13104 return false;
13105 }
13106 default:
13107 return true;
13108 }
13109}
13110
13116static void
13117pm_node_unreference(pm_parser_t *parser, const pm_node_t *node) {
13118 pm_visit_node(node, pm_node_unreference_each, parser);
13119}
13120
13125static void
13126parse_write_name(pm_parser_t *parser, pm_constant_id_t *name_field) {
13127 // The method name needs to change. If we previously had
13128 // foo, we now need foo=. In this case we'll allocate a new
13129 // owned string, copy the previous method name in, and
13130 // append an =.
13131 pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, *name_field);
13132 size_t length = constant->length;
13133 uint8_t *name = (uint8_t *) pm_arena_alloc(parser->arena, length + 1, 1);
13134
13135 memcpy(name, constant->start, length);
13136 name[length] = '=';
13137
13138 *name_field = pm_constant_pool_insert_owned(&parser->metadata_arena, &parser->constant_pool, name, length + 1);
13139}
13140
13147static pm_node_t *
13148parse_unwriteable_target(pm_parser_t *parser, pm_node_t *target) {
13149 switch (PM_NODE_TYPE(target)) {
13150 case PM_SOURCE_ENCODING_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13151 case PM_FALSE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13152 case PM_SOURCE_FILE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13153 case PM_SOURCE_LINE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13154 case PM_NIL_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13155 case PM_SELF_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13156 case PM_TRUE_NODE: pm_parser_err_node(parser, target, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13157 default: break;
13158 }
13159
13160 pm_constant_id_t name = pm_parser_constant_id_raw(parser, parser->start + PM_NODE_START(target), parser->start + PM_NODE_END(target));
13161 pm_local_variable_target_node_t *result = pm_local_variable_target_node_create(parser, &target->location, name, 0);
13162
13163 return UP(result);
13164}
13165
13174static pm_node_t *
13175parse_target(pm_parser_t *parser, pm_node_t *target, bool multiple, bool splat_parent) {
13176 switch (PM_NODE_TYPE(target)) {
13177 case PM_ERROR_RECOVERY_NODE:
13178 return target;
13179 case PM_SOURCE_ENCODING_NODE:
13180 case PM_FALSE_NODE:
13181 case PM_SOURCE_FILE_NODE:
13182 case PM_SOURCE_LINE_NODE:
13183 case PM_NIL_NODE:
13184 case PM_SELF_NODE:
13185 case PM_TRUE_NODE: {
13186 // In these special cases, we have specific error messages and we
13187 // will replace them with local variable writes.
13188 return parse_unwriteable_target(parser, target);
13189 }
13190 case PM_CLASS_VARIABLE_READ_NODE:
13192 target->type = PM_CLASS_VARIABLE_TARGET_NODE;
13193 return target;
13194 case PM_CONSTANT_PATH_NODE:
13195 if (context_def_p(parser)) {
13196 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13197 }
13198
13200 target->type = PM_CONSTANT_PATH_TARGET_NODE;
13201
13202 return target;
13203 case PM_CONSTANT_READ_NODE:
13204 if (context_def_p(parser)) {
13205 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_IN_METHOD);
13206 }
13207
13208 assert(sizeof(pm_constant_target_node_t) == sizeof(pm_constant_read_node_t));
13209 target->type = PM_CONSTANT_TARGET_NODE;
13210
13211 return target;
13212 case PM_BACK_REFERENCE_READ_NODE:
13213 case PM_NUMBERED_REFERENCE_READ_NODE:
13214 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13215 return UP(pm_error_recovery_node_create_unexpected(parser, target));
13216 case PM_GLOBAL_VARIABLE_READ_NODE:
13218 target->type = PM_GLOBAL_VARIABLE_TARGET_NODE;
13219 return target;
13220 case PM_LOCAL_VARIABLE_READ_NODE: {
13221 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13222 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(target));
13223 pm_node_unreference(parser, target);
13224 }
13225
13226 const pm_local_variable_read_node_t *cast = (const pm_local_variable_read_node_t *) target;
13227 uint32_t name = cast->name;
13228 uint32_t depth = cast->depth;
13229 pm_locals_unread(&pm_parser_scope_find(parser, depth)->locals, name);
13230
13232 target->type = PM_LOCAL_VARIABLE_TARGET_NODE;
13233
13234 return target;
13235 }
13236 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13237 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13238 pm_node_t *node = UP(pm_local_variable_target_node_create(parser, &target->location, name, 0));
13239
13240 pm_node_unreference(parser, target);
13241
13242 return node;
13243 }
13244 case PM_INSTANCE_VARIABLE_READ_NODE:
13246 target->type = PM_INSTANCE_VARIABLE_TARGET_NODE;
13247 return target;
13248 case PM_MULTI_TARGET_NODE:
13249 if (splat_parent) {
13250 // Multi target is not accepted in all positions. If this is one
13251 // of them, then we need to add an error.
13252 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13253 }
13254
13255 return target;
13256 case PM_SPLAT_NODE: {
13257 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13258
13259 if (splat->expression != NULL) {
13260 splat->expression = parse_target(parser, splat->expression, multiple, true);
13261 }
13262
13263 return UP(splat);
13264 }
13265 case PM_CALL_NODE: {
13266 pm_call_node_t *call = (pm_call_node_t *) target;
13267
13268 // If we have no arguments to the call node and we need this to be a
13269 // target then this is either a method call or a local variable
13270 // write.
13271 if (
13272 (call->message_loc.length > 0) &&
13273 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13274 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13275 (call->opening_loc.length == 0) &&
13276 (call->arguments == NULL) &&
13277 (call->block == NULL)
13278 ) {
13279 if (call->receiver == NULL) {
13280 // When we get here, we have a local variable write, because it
13281 // was previously marked as a method call but now we have an =.
13282 // This looks like:
13283 //
13284 // foo = 1
13285 //
13286 // When it was parsed in the prefix position, foo was seen as a
13287 // method call with no receiver and no arguments. Now we have an
13288 // =, so we know it's a local variable write.
13289 pm_location_t message_loc = call->message_loc;
13290 pm_constant_id_t name = pm_parser_local_add_location(parser, &message_loc, 0);
13291
13292 return UP(pm_local_variable_target_node_create(parser, &message_loc, name, 0));
13293 }
13294
13295 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)) {
13296 if (multiple && PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_SAFE_NAVIGATION)) {
13297 pm_parser_err_node(parser, (const pm_node_t *) call, PM_ERR_UNEXPECTED_SAFE_NAVIGATION);
13298 }
13299
13300 parse_write_name(parser, &call->name);
13301 return UP(pm_call_target_node_create(parser, call));
13302 }
13303 }
13304
13305 // If there is no call operator and the message is "[]" then this is
13306 // an aref expression, and we can transform it into an aset
13307 // expression.
13308 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13309 return UP(pm_index_target_node_create(parser, call));
13310 }
13311 }
13313 default:
13314 // In this case we have a node that we don't know how to convert
13315 // into a target. We need to treat it as an error. For now, we'll
13316 // mark it as an error and just skip right past it.
13317 pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
13318 return target;
13319 }
13320}
13321
13326static pm_node_t *
13327parse_target_validate(pm_parser_t *parser, pm_node_t *target, bool multiple) {
13328 pm_node_t *result = parse_target(parser, target, multiple, false);
13329
13330 // Ensure that we have one of an =, an 'in' in for indexes, and a ')' in
13331 // parens after the targets.
13332 if (
13333 !match1(parser, PM_TOKEN_EQUAL) &&
13334 !(context_p(parser, PM_CONTEXT_FOR_INDEX) && match1(parser, PM_TOKEN_KEYWORD_IN)) &&
13335 !(context_p(parser, PM_CONTEXT_PARENS) && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT))
13336 ) {
13337 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13338 }
13339
13340 return result;
13341}
13342
13347static pm_node_t *
13348parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) {
13349 pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser);
13350
13351 if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) {
13352 return UP(pm_shareable_constant_node_create(parser, write, shareable_constant));
13353 }
13354
13355 return write;
13356}
13357
13361static pm_node_t *
13362parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_node_t *value) {
13363 switch (PM_NODE_TYPE(target)) {
13364 case PM_ERROR_RECOVERY_NODE:
13365 return target;
13366 case PM_CLASS_VARIABLE_READ_NODE: {
13367 pm_class_variable_write_node_t *node = pm_class_variable_write_node_create(parser, (pm_class_variable_read_node_t *) target, operator, value);
13368 return UP(node);
13369 }
13370 case PM_CONSTANT_PATH_NODE: {
13371 pm_node_t *node = UP(pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value));
13372
13373 if (context_def_p(parser)) {
13374 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13375 }
13376
13377 return parse_shareable_constant_write(parser, node);
13378 }
13379 case PM_CONSTANT_READ_NODE: {
13380 pm_node_t *node = UP(pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value));
13381
13382 if (context_def_p(parser)) {
13383 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
13384 }
13385
13386 return parse_shareable_constant_write(parser, node);
13387 }
13388 case PM_BACK_REFERENCE_READ_NODE:
13389 case PM_NUMBERED_REFERENCE_READ_NODE:
13390 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, target, PM_ERR_WRITE_TARGET_READONLY);
13392 case PM_GLOBAL_VARIABLE_READ_NODE: {
13393 pm_global_variable_write_node_t *node = pm_global_variable_write_node_create(parser, target, operator, value);
13394 return UP(node);
13395 }
13396 case PM_LOCAL_VARIABLE_READ_NODE: {
13398
13399 pm_location_t location = target->location;
13400 pm_constant_id_t name = local_read->name;
13401 uint32_t depth = local_read->depth;
13402 pm_scope_t *scope = pm_parser_scope_find(parser, depth);
13403
13404 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(target), PM_NODE_LENGTH(target))) {
13405 pm_diagnostic_id_t diag_id = (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) ? PM_ERR_EXPRESSION_NOT_WRITABLE_NUMBERED : PM_ERR_PARAMETER_NUMBERED_RESERVED;
13406 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(target), PM_NODE_LENGTH(target), diag_id, parser->start + PM_NODE_START(target));
13407 pm_node_unreference(parser, target);
13408 }
13409
13410 pm_locals_unread(&scope->locals, name);
13411
13412 return UP(pm_local_variable_write_node_create(parser, name, depth, value, &location, operator));
13413 }
13414 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
13415 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
13416 pm_node_t *node = UP(pm_local_variable_write_node_create(parser, name, 0, value, &target->location, operator));
13417
13418 pm_node_unreference(parser, target);
13419
13420 return node;
13421 }
13422 case PM_INSTANCE_VARIABLE_READ_NODE: {
13423 pm_node_t *write_node = UP(pm_instance_variable_write_node_create(parser, (pm_instance_variable_read_node_t *) target, operator, value));
13424 return write_node;
13425 }
13426 case PM_MULTI_TARGET_NODE:
13427 return UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) target, operator, value));
13428 case PM_SPLAT_NODE: {
13429 pm_splat_node_t *splat = (pm_splat_node_t *) target;
13430
13431 if (splat->expression != NULL) {
13432 splat->expression = parse_write(parser, splat->expression, operator, value);
13433 }
13434
13435 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
13436 pm_multi_target_node_targets_append(parser, multi_target, UP(splat));
13437
13438 return UP(pm_multi_write_node_create(parser, multi_target, operator, value));
13439 }
13440 case PM_CALL_NODE: {
13441 pm_call_node_t *call = (pm_call_node_t *) target;
13442
13443 // If we have no arguments to the call node and we need this to be a
13444 // target then this is either a method call or a local variable
13445 // write.
13446 if (
13447 (call->message_loc.length > 0) &&
13448 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '!') &&
13449 (parser->start[call->message_loc.start + call->message_loc.length - 1] != '?') &&
13450 (call->opening_loc.length == 0) &&
13451 (call->arguments == NULL) &&
13452 (call->block == NULL)
13453 ) {
13454 if (call->receiver == NULL) {
13455 // When we get here, we have a local variable write, because it
13456 // was previously marked as a method call but now we have an =.
13457 // This looks like:
13458 //
13459 // foo = 1
13460 //
13461 // When it was parsed in the prefix position, foo was seen as a
13462 // method call with no receiver and no arguments. Now we have an
13463 // =, so we know it's a local variable write.
13464 pm_location_t message_loc = call->message_loc;
13465
13466 pm_refute_numbered_parameter(parser, message_loc.start, message_loc.length);
13467 pm_parser_local_add_location(parser, &message_loc, 0);
13468
13469 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));
13470 target = UP(pm_local_variable_write_node_create(parser, constant_id, 0, value, &message_loc, operator));
13471
13472 return target;
13473 }
13474
13475 if (char_is_identifier_start(parser, parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)) {
13476 // When we get here, we have a method call, because it was
13477 // previously marked as a method call but now we have an =. This
13478 // looks like:
13479 //
13480 // foo.bar = 1
13481 //
13482 // When it was parsed in the prefix position, foo.bar was seen as a
13483 // method call with no arguments. Now we have an =, so we know it's
13484 // a method call with an argument. In this case we will create the
13485 // arguments node, parse the argument, and add it to the list.
13486 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
13487 call->arguments = arguments;
13488
13489 pm_arguments_node_arguments_append(parser->arena, arguments, value);
13490 PM_NODE_LENGTH_SET_NODE(call, arguments);
13491 call->equal_loc = TOK2LOC(parser, operator);
13492
13493 parse_write_name(parser, &call->name);
13494 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13495
13496 return UP(call);
13497 }
13498 }
13499
13500 // If there is no call operator and the message is "[]" then this is
13501 // an aref expression, and we can transform it into an aset
13502 // expression.
13503 if (PM_NODE_FLAG_P(call, PM_CALL_NODE_FLAGS_INDEX)) {
13504 if (call->arguments == NULL) {
13505 call->arguments = pm_arguments_node_create(parser);
13506 }
13507
13508 pm_arguments_node_arguments_append(parser->arena, call->arguments, value);
13509 PM_NODE_LENGTH_SET_NODE(target, value);
13510
13511 // Replace the name with "[]=".
13512 call->name = pm_parser_constant_id_constant(parser, "[]=", 3);
13513 call->equal_loc = TOK2LOC(parser, operator);
13514
13515 // Ensure that the arguments for []= don't contain keywords
13516 pm_index_arguments_check(parser, call->arguments, call->block);
13517 pm_node_flag_set(UP(call), PM_CALL_NODE_FLAGS_ATTRIBUTE_WRITE | pm_implicit_array_write_flags(value, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY));
13518
13519 return target;
13520 }
13521
13522 // If there are arguments on the call node, then it can't be a
13523 // method call ending with = or a local variable write, so it must
13524 // be a syntax error. In this case we'll fall through to our default
13525 // handling. We need to free the value that we parsed because there
13526 // is no way for us to attach it to the tree at this point.
13527 //
13528 // Since it is possible for the value to contain an implicit
13529 // parameter somewhere in its subtree, we need to walk it and remove
13530 // any implicit parameters from the list of implicit parameters for
13531 // the current scope.
13532 pm_node_unreference(parser, value);
13533 }
13535 default:
13536 // In this case we have a node that we don't know how to convert into a
13537 // target. We need to treat it as an error. For now, we'll mark it as an
13538 // error and just skip right past it.
13539 pm_parser_err_token(parser, operator, PM_ERR_WRITE_TARGET_UNEXPECTED);
13540 return target;
13541 }
13542}
13543
13550static pm_node_t *
13551parse_unwriteable_write(pm_parser_t *parser, pm_node_t *target, const pm_token_t *equals, pm_node_t *value) {
13552 switch (PM_NODE_TYPE(target)) {
13553 case PM_SOURCE_ENCODING_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_ENCODING); break;
13554 case PM_FALSE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FALSE); break;
13555 case PM_SOURCE_FILE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_FILE); break;
13556 case PM_SOURCE_LINE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_LINE); break;
13557 case PM_NIL_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_NIL); break;
13558 case PM_SELF_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_SELF); break;
13559 case PM_TRUE_NODE: pm_parser_err_token(parser, equals, PM_ERR_EXPRESSION_NOT_WRITABLE_TRUE); break;
13560 default: break;
13561 }
13562
13563 pm_constant_id_t name = pm_parser_local_add_location(parser, &target->location, 1);
13564 pm_local_variable_write_node_t *result = pm_local_variable_write_node_create(parser, name, 0, value, &target->location, equals);
13565
13566 return UP(result);
13567}
13568
13579static pm_node_t *
13580parse_targets(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13581 bool has_rest = PM_NODE_TYPE_P(first_target, PM_SPLAT_NODE);
13582
13583 pm_multi_target_node_t *result = pm_multi_target_node_create(parser);
13584 pm_multi_target_node_targets_append(parser, result, parse_target(parser, first_target, true, false));
13585
13586 while (accept1(parser, PM_TOKEN_COMMA)) {
13587 if (accept1(parser, PM_TOKEN_USTAR)) {
13588 // Here we have a splat operator. It can have a name or be
13589 // anonymous. It can be the final target or be in the middle if
13590 // there haven't been any others yet.
13591 if (has_rest) {
13592 pm_parser_err_previous(parser, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
13593 }
13594
13595 pm_token_t star_operator = parser->previous;
13596 pm_node_t *name = NULL;
13597
13598 if (token_begins_expression_p(parser->current.type)) {
13599 name = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
13600 name = parse_target(parser, name, true, true);
13601 }
13602
13603 pm_node_t *splat = UP(pm_splat_node_create(parser, &star_operator, name));
13604 pm_multi_target_node_targets_append(parser, result, splat);
13605 has_rest = true;
13606 } else if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
13607 context_push(parser, PM_CONTEXT_MULTI_TARGET);
13608 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13609 target = parse_target(parser, target, true, false);
13610
13611 pm_multi_target_node_targets_append(parser, result, target);
13612 context_pop(parser);
13613 } else if (token_begins_expression_p(parser->current.type)) {
13614 pm_node_t *target = parse_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
13615 target = parse_target(parser, target, true, false);
13616
13617 pm_multi_target_node_targets_append(parser, result, target);
13618 } else if (!match1(parser, PM_TOKEN_EOF)) {
13619 // If we get here, then we have a trailing , in a multi target node.
13620 // We'll add an implicit rest node to represent this.
13621 pm_node_t *rest = UP(pm_implicit_rest_node_create(parser, &parser->previous));
13622 pm_multi_target_node_targets_append(parser, result, rest);
13623 break;
13624 }
13625 }
13626
13627 return UP(result);
13628}
13629
13634static pm_node_t *
13635parse_targets_validate(pm_parser_t *parser, pm_node_t *first_target, pm_binding_power_t binding_power, uint16_t depth) {
13636 pm_node_t *result = parse_targets(parser, first_target, binding_power, depth);
13637
13638 // If we're inside parentheses, then we allow a newline before the
13639 // closing parenthesis or equals sign. Outside of parentheses, a newline
13640 // is not allowed (e.g., `a, b\n= 1, 2` is not valid).
13641 if (context_p(parser, PM_CONTEXT_PARENS) || context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
13642 accept1(parser, PM_TOKEN_NEWLINE);
13643 }
13644
13645 // Ensure that we have either an = or a ) after the targets.
13646 if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
13647 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
13648 }
13649
13650 return result;
13651}
13652
13656static pm_statements_node_t *
13657parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
13658 // First, skip past any optional terminators that might be at the beginning
13659 // of the statements.
13660 while (accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE));
13661
13662 // If we have a terminator, then we can just return NULL.
13663 if (context_terminator(context, &parser->current)) return NULL;
13664
13665 pm_statements_node_t *statements = pm_statements_node_create(parser);
13666
13667 // At this point we know we have at least one statement, and that it
13668 // immediately follows the current token.
13669 context_push(parser, context);
13670
13671 while (true) {
13672 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));
13673 pm_statements_node_body_append(parser, statements, node, true);
13674
13675 // If we're recovering from a syntax error, then we need to stop parsing
13676 // the statements now.
13677 if (parser->recovering) {
13678 // If this is the level of context where the recovery has happened,
13679 // then we can mark the parser as done recovering.
13680 if (context_terminator(context, &parser->current)) parser->recovering = false;
13681 break;
13682 }
13683
13684 // If we have a terminator, then we will parse all consecutive
13685 // terminators and then continue parsing the statements list.
13686 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
13687 // If we have a terminator, then we will continue parsing the
13688 // statements list.
13689 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13690 if (context_terminator(context, &parser->current)) break;
13691
13692 // Now we can continue parsing the list of statements.
13693 continue;
13694 }
13695
13696 // At this point we have a list of statements that are not terminated by
13697 // a newline or semicolon. At this point we need to check if we're at
13698 // the end of the statements list. If we are, then we should break out
13699 // of the loop.
13700 if (context_terminator(context, &parser->current)) break;
13701
13702 // At this point, we have a syntax error, because the statement was not
13703 // terminated by a newline or semicolon, and we're not at the end of the
13704 // statements list. Ideally we should scan forward to determine if we
13705 // should insert a missing terminator or break out of parsing the
13706 // statements list at this point.
13707 //
13708 // We don't have that yet, so instead we'll do a more naive approach. If
13709 // we were unable to parse an expression, then we will skip past this
13710 // token and continue parsing the statements list. Otherwise we'll add
13711 // an error and continue parsing the statements list.
13712 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) {
13713 parser_lex(parser);
13714
13715 // If we are at the end of the file, then we need to stop parsing
13716 // the statements entirely at this point. Mark the parser as
13717 // recovering, as we know that EOF closes the top-level context, and
13718 // then break out of the loop.
13719 if (match1(parser, PM_TOKEN_EOF)) {
13720 parser->recovering = true;
13721 break;
13722 }
13723
13724 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
13725 if (context_terminator(context, &parser->current)) break;
13726 } else if (!accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_EOF)) {
13727 // This is an inlined version of accept1 because the error that we
13728 // want to add has varargs. If this happens again, we should
13729 // probably extract a helper function.
13730 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
13731 parser->previous.start = parser->previous.end;
13732 parser->previous.type = 0;
13733 }
13734 }
13735
13736 context_pop(parser);
13737
13738 bool last_value = true;
13739 switch (context) {
13740 case PM_CONTEXT_BEGIN_ENSURE:
13741 case PM_CONTEXT_DEF_ENSURE:
13742 last_value = false;
13743 break;
13744 default:
13745 break;
13746 }
13747 pm_void_statements_check(parser, statements, last_value);
13748
13749 return statements;
13750}
13751
13755static void
13756pm_hash_key_duplicated_warn(pm_parser_t *parser, const pm_node_t *duplicated, const pm_node_t *node) {
13757 pm_buffer_t buffer = { 0 };
13758 pm_static_literal_inspect(&buffer, &parser->line_offsets, parser->start, parser->start_line, parser->encoding, duplicated);
13759
13760 pm_diagnostic_list_append_format(
13761 &parser->metadata_arena,
13762 &parser->warning_list,
13763 duplicated->location.start,
13764 duplicated->location.length,
13765 PM_WARN_DUPLICATED_HASH_KEY,
13766 (int) pm_buffer_length(&buffer),
13767 pm_buffer_value(&buffer),
13768 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line
13769 );
13770
13771 pm_buffer_cleanup(&buffer);
13772}
13773
13778static void
13779pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13780 const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, true);
13781
13782 if (duplicated != NULL) {
13783 pm_hash_key_duplicated_warn(parser, duplicated, node);
13784 }
13785}
13786
13794static void
13795pm_hash_key_static_literals_merge(pm_parser_t *parser, pm_static_literals_t *literals, const pm_hash_node_t *hash, uint32_t boundary) {
13796 const pm_node_list_t *elements = &hash->elements;
13797
13798 for (size_t index = 0; index < elements->size; index++) {
13799 pm_node_t *element = elements->nodes[index];
13800
13801 switch (PM_NODE_TYPE(element)) {
13802 case PM_ASSOC_NODE: {
13803 pm_node_t *key = ((pm_assoc_node_t *) element)->key;
13804 const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, key, true);
13805
13806 if (duplicated != NULL && PM_NODE_START(duplicated) < boundary) {
13807 pm_hash_key_duplicated_warn(parser, duplicated, key);
13808 }
13809
13810 break;
13811 }
13812 case PM_ASSOC_SPLAT_NODE: {
13813 const pm_node_t *value = ((pm_assoc_splat_node_t *) element)->value;
13814
13815 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
13816 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, boundary);
13817 }
13818
13819 break;
13820 }
13821 default:
13822 break;
13823 }
13824 }
13825}
13826
13831static void
13832pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13833 pm_node_t *previous;
13834
13835 if ((previous = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, false)) != NULL) {
13836 pm_diagnostic_list_append_format(
13837 &parser->metadata_arena,
13838 &parser->warning_list,
13839 PM_NODE_START(node),
13840 PM_NODE_LENGTH(node),
13841 PM_WARN_DUPLICATED_WHEN_CLAUSE,
13842 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(node), parser->start_line).line,
13843 pm_line_offset_list_line_column(&parser->line_offsets, PM_NODE_START(previous), parser->start_line).line
13844 );
13845 }
13846}
13847
13851static bool
13852parse_assocs(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node, uint16_t depth) {
13853 assert(PM_NODE_TYPE_P(node, PM_HASH_NODE) || PM_NODE_TYPE_P(node, PM_KEYWORD_HASH_NODE));
13854 bool contains_keyword_splat = false;
13855
13856 while (true) {
13857 pm_node_t *element;
13858
13859 switch (parser->current.type) {
13860 case PM_TOKEN_USTAR_STAR: {
13861 parser_lex(parser);
13862 pm_token_t operator = parser->previous;
13863 pm_node_t *value = NULL;
13864
13865 if (token_begins_expression_p(parser->current.type)) {
13866 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));
13867
13868 /* If the splatted value is itself a hash literal, its keys
13869 * become part of this hash for the duplicate key warning. */
13870 if (value != NULL && PM_NODE_TYPE_P(value, PM_HASH_NODE)) {
13871 pm_hash_key_static_literals_merge(parser, literals, (const pm_hash_node_t *) value, PM_NODE_START(value));
13872 }
13873 } else {
13874 pm_parser_scope_forwarding_keywords_check(parser, &operator);
13875 }
13876
13877 element = UP(pm_assoc_splat_node_create(parser, value, &operator));
13878 contains_keyword_splat = true;
13879 break;
13880 }
13881 case PM_TOKEN_LABEL: {
13882 pm_token_t label = parser->current;
13883 parser_lex(parser);
13884
13885 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &label));
13886 pm_hash_key_static_literals_add(parser, literals, key);
13887
13888 pm_node_t *value = NULL;
13889
13890 if (token_begins_expression_p(parser->current.type)) {
13891 value = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_HASH_EXPRESSION_AFTER_LABEL, (uint16_t) (depth + 1));
13892 } else {
13893 if (parser->encoding->isupper_char(label.start, (label.end - 1) - label.start)) {
13894 pm_token_t constant = { .type = PM_TOKEN_CONSTANT, .start = label.start, .end = label.end - 1 };
13895 value = UP(pm_constant_read_node_create(parser, &constant));
13896 } else {
13897 int depth = -1;
13898 pm_token_t identifier = { .type = PM_TOKEN_IDENTIFIER, .start = label.start, .end = label.end - 1 };
13899
13900 if (identifier.end[-1] == '!' || identifier.end[-1] == '?') {
13901 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &identifier, PM_ERR_INVALID_LOCAL_VARIABLE_READ);
13902 } else {
13903 depth = pm_parser_local_depth(parser, &identifier);
13904 }
13905
13906 if (depth == -1) {
13907 value = UP(pm_call_node_variable_call_create(parser, &identifier));
13908 } else {
13909 value = UP(pm_local_variable_read_node_create(parser, &identifier, (uint32_t) depth));
13910 }
13911 }
13912
13913 value->location.length++;
13914 value = UP(pm_implicit_node_create(parser, value));
13915 }
13916
13917 element = UP(pm_assoc_node_create(parser, key, NULL, value));
13918 break;
13919 }
13920 default: {
13921 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));
13922
13923 // Hash keys that are strings are automatically frozen. We will
13924 // mark that here.
13925 if (PM_NODE_TYPE_P(key, PM_STRING_NODE)) {
13926 pm_node_flag_set(key, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
13927 }
13928
13929 pm_hash_key_static_literals_add(parser, literals, key);
13930
13931 pm_token_t operator = { 0 };
13932 if (!pm_symbol_node_label_p(parser, key)) {
13933 expect1(parser, PM_TOKEN_EQUAL_GREATER, PM_ERR_HASH_ROCKET);
13934 operator = parser->previous;
13935 }
13936
13937 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));
13938 element = UP(pm_assoc_node_create(parser, key, NTOK2PTR(operator), value));
13939 break;
13940 }
13941 }
13942
13943 if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
13944 pm_hash_node_elements_append(parser->arena, (pm_hash_node_t *) node, element);
13945 } else {
13946 pm_keyword_hash_node_elements_append(parser->arena, (pm_keyword_hash_node_t *) node, element);
13947 }
13948
13949 // If there's no comma after the element, then we're done.
13950 if (!accept1(parser, PM_TOKEN_COMMA)) break;
13951
13952 // If the next element starts with a label or a **, then we know we have
13953 // another element in the hash, so we'll continue parsing.
13954 if (match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)) continue;
13955
13956 // Otherwise we need to check if the subsequent token begins an expression.
13957 // If it does, then we'll continue parsing.
13958 if (token_begins_expression_p(parser->current.type)) continue;
13959
13960 // Otherwise by default we will exit out of this loop.
13961 break;
13962 }
13963
13964 return contains_keyword_splat;
13965}
13966
13967static PRISM_INLINE bool
13968argument_allowed_for_bare_hash(pm_parser_t *parser, pm_node_t *argument) {
13969 if (pm_symbol_node_label_p(parser, argument)) {
13970 return true;
13971 }
13972
13973 switch (PM_NODE_TYPE(argument)) {
13974 case PM_CALL_NODE: {
13975 pm_call_node_t *cast = (pm_call_node_t *) argument;
13976 if (cast->opening_loc.length == 0 && cast->arguments != NULL) {
13977 if (PM_NODE_FLAG_P(cast->arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS | PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
13978 return false;
13979 }
13980 if (cast->block != NULL) {
13981 return false;
13982 }
13983 }
13984 break;
13985 }
13986 default: break;
13987 }
13988 return accept1(parser, PM_TOKEN_EQUAL_GREATER);
13989}
13990
13994static PRISM_INLINE void
13995parse_arguments_append(pm_parser_t *parser, pm_arguments_t *arguments, pm_node_t *argument) {
13996 if (arguments->arguments == NULL) {
13997 arguments->arguments = pm_arguments_node_create(parser);
13998 }
13999
14000 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, argument);
14001}
14002
14007static PRISM_INLINE bool
14008pm_call_node_command_p(const pm_call_node_t *node) {
14009 return (
14010 (node->opening_loc.length == 0) &&
14011 (node->block == NULL || PM_NODE_TYPE_P(node->block, PM_BLOCK_ARGUMENT_NODE)) &&
14012 (node->arguments != NULL || node->block != NULL)
14013 );
14014}
14015
14025static bool
14026pm_constant_path_command_call_p(const pm_parser_t *parser, const pm_call_node_t *call) {
14027 return (
14028 call->receiver != NULL &&
14029 call->opening_loc.length == 0 &&
14030 call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE) &&
14031 call->call_operator_loc.length > 0 &&
14032 parser->start[call->call_operator_loc.start] == ':' &&
14033 call->message_loc.length > 0 &&
14034 parser->encoding->isupper_char(parser->start + call->message_loc.start, (ptrdiff_t) call->message_loc.length)
14035 );
14036}
14037
14043static bool
14044pm_command_call_value_p(const pm_parser_t *parser, const pm_node_t *node) {
14045 switch (PM_NODE_TYPE(node)) {
14046 case PM_CALL_NODE: {
14047 const pm_call_node_t *call = (const pm_call_node_t *) node;
14048
14049 /* Command-style calls (e.g., foo bar, obj.foo bar). Attribute
14050 * writes (e.g., a.b = 1) are not commands. */
14051 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)) {
14052 return true;
14053 }
14054
14055 /* A constant-path command with a brace block, e.g. `Foo::Bar { }`. */
14056 if (pm_constant_path_command_call_p(parser, call)) {
14057 return true;
14058 }
14059
14060 /* A `!` or `not` prefix wrapping a command call (e.g., `!foo bar`,
14061 * `not foo bar`) is also a command-call value. */
14062 if (call->receiver != NULL && call->arguments == NULL && call->opening_loc.length == 0 && call->call_operator_loc.length == 0) {
14063 return pm_command_call_value_p(parser, call->receiver);
14064 }
14065
14066 return false;
14067 }
14068 case PM_SUPER_NODE: {
14069 /* A command-style super (no parens). A super carrying a do-block is
14070 * a block call (it permits chaining), so it is excluded here and
14071 * handled by pm_block_call_p instead. */
14072 const pm_super_node_t *cast = (const pm_super_node_t *) node;
14073 return cast->lparen_loc.length == 0 &&
14074 (cast->arguments != NULL || cast->block != NULL) &&
14075 !(cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE));
14076 }
14077 case PM_YIELD_NODE: {
14078 const pm_yield_node_t *cast = (const pm_yield_node_t *) node;
14079 return cast->lparen_loc.length == 0 && cast->arguments != NULL;
14080 }
14081 case PM_RESCUE_MODIFIER_NODE:
14082 return pm_command_call_value_p(parser, ((const pm_rescue_modifier_node_t *) node)->expression);
14083 case PM_DEF_NODE: {
14084 const pm_def_node_t *cast = (const pm_def_node_t *) node;
14085 if (cast->equal_loc.length > 0 && cast->body != NULL) {
14086 const pm_node_t *body = cast->body;
14087 if (PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE)) {
14088 body = ((const pm_statements_node_t *) body)->body.nodes[((const pm_statements_node_t *) body)->body.size - 1];
14089 }
14090 return pm_command_call_value_p(parser, body);
14091 }
14092 return false;
14093 }
14094 default:
14095 return false;
14096 }
14097}
14098
14105static bool
14106pm_block_call_p(const pm_node_t *node) {
14107 while (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
14108 const pm_call_node_t *call = (const pm_call_node_t *) node;
14109
14110 /* Root: a command (no parentheses) carrying command arguments and a
14111 * block (brace or do), e.g. `foo bar do end`, `foo bar { }`. The
14112 * no-parentheses requirement is what distinguishes a command root from
14113 * a method call root like `foo.bar(1) { }`, which is a primary value
14114 * and may be used as an argument.
14115 */
14116 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL && PM_NODE_TYPE_P(call->block, PM_BLOCK_NODE)) {
14117 return true;
14118 }
14119
14120 /* Walk up the receiver chain of a `.`/`::`/`&.` call (e.g.,
14121 * `foo bar do end.baz(1)`). Parentheses on the chained call are allowed
14122 * here -- in parse.y a `block_call` can be extended by
14123 * `call_op2 operation2 opt_paren_args` and remains a block call.
14124 */
14125 if (call->call_operator_loc.length > 0 && call->receiver != NULL) {
14126 node = call->receiver;
14127 continue;
14128 }
14129
14130 return false;
14131 }
14132
14133 /* A `super` with command arguments and a do-block is also a block-call root
14134 * (parse.y: `command do_block`, where the command is `keyword_super
14135 * command_args`). `super do end` with no arguments is a forwarding super
14136 * (a primary value) and is handled elsewhere.
14137 */
14138 if (PM_NODE_TYPE_P(node, PM_SUPER_NODE)) {
14139 const pm_super_node_t *super = (const pm_super_node_t *) node;
14140 return super->lparen_loc.length == 0 && super->block != NULL && PM_NODE_TYPE_P(super->block, PM_BLOCK_NODE);
14141 }
14142
14143 return false;
14144}
14145
14149static void
14150parse_arguments(pm_parser_t *parser, pm_arguments_t *arguments, bool accepts_forwarding, pm_token_type_t terminator, uint8_t flags, uint16_t depth) {
14151 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
14152
14153 // First we need to check if the next token is one that could be the start
14154 // of an argument. If it's not, then we can just return.
14155 if (
14156 match2(parser, terminator, PM_TOKEN_EOF) ||
14157 (binding_power != PM_BINDING_POWER_UNSET && binding_power < PM_BINDING_POWER_RANGE) ||
14158 context_terminator(parser->current_context->context, &parser->current)
14159 ) {
14160 return;
14161 }
14162
14163 bool parsed_first_argument = false;
14164 bool parsed_bare_hash = false;
14165 bool parsed_block_argument = false;
14166 bool parsed_forwarding_arguments = false;
14167
14168 while (!match1(parser, PM_TOKEN_EOF)) {
14169 if (parsed_forwarding_arguments) {
14170 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_FORWARDING_ELLIPSES);
14171 }
14172
14173 pm_node_t *argument = NULL;
14174
14175 switch (parser->current.type) {
14176 case PM_TOKEN_USTAR_STAR:
14177 case PM_TOKEN_LABEL: {
14178 if (parsed_bare_hash) {
14179 pm_parser_err_current(parser, PM_ERR_ARGUMENT_BARE_HASH);
14180 }
14181
14182 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
14183 argument = UP(hash);
14184
14185 pm_static_literals_t hash_keys = { 0 };
14186 bool contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(hash), (uint16_t) (depth + 1));
14187
14188 parse_arguments_append(parser, arguments, argument);
14189
14190 pm_node_flags_t node_flags = PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14191 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14192 pm_node_flag_set(UP(arguments->arguments), node_flags);
14193
14194 pm_static_literals_free(&hash_keys);
14195 parsed_bare_hash = true;
14196
14197 break;
14198 }
14199 case PM_TOKEN_UAMPERSAND: {
14200 parser_lex(parser);
14201 pm_token_t operator = parser->previous;
14202 pm_node_t *expression = NULL;
14203
14204 if (token_begins_expression_p(parser->current.type)) {
14205 expression = parse_value_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_ARGUMENT, (uint16_t) (depth + 1));
14206 } else {
14207 pm_parser_scope_forwarding_block_check(parser, &operator);
14208 }
14209
14210 argument = UP(pm_block_argument_node_create(parser, &operator, expression));
14211 if (parsed_block_argument) {
14212 parse_arguments_append(parser, arguments, argument);
14213 } else {
14214 arguments->block = argument;
14215 }
14216
14217 if (match1(parser, PM_TOKEN_COMMA)) {
14218 pm_parser_err_current(parser, PM_ERR_ARGUMENT_AFTER_BLOCK);
14219 }
14220
14221 parsed_block_argument = true;
14222 break;
14223 }
14224 case PM_TOKEN_USTAR: {
14225 parser_lex(parser);
14226 pm_token_t operator = parser->previous;
14227
14228 if (match4(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_SEMICOLON, PM_TOKEN_BRACKET_RIGHT)) {
14229 pm_parser_scope_forwarding_positionals_check(parser, &operator);
14230 argument = UP(pm_splat_node_create(parser, &operator, NULL));
14231 if (parsed_bare_hash) {
14232 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT);
14233 }
14234 } else {
14235 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));
14236
14237 if (parsed_bare_hash) {
14238 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);
14239 }
14240
14241 argument = UP(pm_splat_node_create(parser, &operator, expression));
14242 }
14243
14244 parse_arguments_append(parser, arguments, argument);
14245 break;
14246 }
14247 case PM_TOKEN_UDOT_DOT_DOT: {
14248 if (accepts_forwarding) {
14249 parser_lex(parser);
14250
14251 if (token_begins_expression_p(parser->current.type)) {
14252 // If the token begins an expression then this ... was
14253 // not actually argument forwarding but was instead a
14254 // range.
14255 pm_token_t operator = parser->previous;
14256 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));
14257
14258 // If we parse a range, we need to validate that we
14259 // didn't accidentally violate the nonassoc rules of the
14260 // ... operator.
14261 if (PM_NODE_TYPE_P(right, PM_RANGE_NODE)) {
14262 pm_range_node_t *range = (pm_range_node_t *) right;
14263 pm_parser_err(parser, range->operator_loc.start, range->operator_loc.length, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
14264 }
14265
14266 argument = UP(pm_range_node_create(parser, NULL, &operator, right));
14267 } else {
14268 pm_parser_scope_forwarding_all_check(parser, &parser->previous);
14269 if (parsed_first_argument && terminator == PM_TOKEN_EOF) {
14270 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORWARDING_UNBOUND);
14271 }
14272
14273 argument = UP(pm_forwarding_arguments_node_create(parser, &parser->previous));
14274 parse_arguments_append(parser, arguments, argument);
14275 pm_node_flag_set(UP(arguments->arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_FORWARDING);
14276 arguments->has_forwarding = true;
14277 parsed_forwarding_arguments = true;
14278 break;
14279 }
14280 }
14281 }
14283 default: {
14284 if (argument == NULL) {
14285 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));
14286 }
14287
14288 bool contains_keywords = false;
14289 bool contains_keyword_splat = false;
14290
14291 if (argument_allowed_for_bare_hash(parser, argument)) {
14292 if (parsed_bare_hash) {
14293 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_BARE_HASH);
14294 }
14295
14296 /* A hash key must be an argument (`arg`). A command call or
14297 * block call (e.g. `Foo::Bar { } => v`, `foo bar do end =>
14298 * v`) is not an argument, so reject it as a key. Plain
14299 * command calls never reach here as a key because they
14300 * absorb the `=>` into their own arguments first.
14301 */
14302 if (pm_command_call_value_p(parser, argument) || pm_block_call_p(argument)) {
14303 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->previous.type));
14304 }
14305
14306 pm_token_t operator = { 0 };
14307 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
14308 operator = parser->previous;
14309 }
14310
14311 pm_keyword_hash_node_t *bare_hash = pm_keyword_hash_node_create(parser);
14312 contains_keywords = true;
14313
14314 // Create the set of static literals for this hash.
14315 pm_static_literals_t hash_keys = { 0 };
14316 pm_hash_key_static_literals_add(parser, &hash_keys, argument);
14317
14318 // Finish parsing the one we are part way through.
14319 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));
14320 argument = UP(pm_assoc_node_create(parser, argument, NTOK2PTR(operator), value));
14321
14322 pm_keyword_hash_node_elements_append(parser->arena, bare_hash, argument);
14323 argument = UP(bare_hash);
14324
14325 // Then parse more if we have a comma
14326 if (accept1(parser, PM_TOKEN_COMMA) && (
14327 token_begins_expression_p(parser->current.type) ||
14328 match2(parser, PM_TOKEN_USTAR_STAR, PM_TOKEN_LABEL)
14329 )) {
14330 contains_keyword_splat = parse_assocs(parser, &hash_keys, UP(bare_hash), (uint16_t) (depth + 1));
14331 }
14332
14333 pm_static_literals_free(&hash_keys);
14334 parsed_bare_hash = true;
14335 }
14336
14337 parse_arguments_append(parser, arguments, argument);
14338
14339 pm_node_flags_t node_flags = 0;
14340 if (contains_keywords) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS;
14341 if (contains_keyword_splat) node_flags |= PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORD_SPLAT;
14342 pm_node_flag_set(UP(arguments->arguments), node_flags);
14343
14344 break;
14345 }
14346 }
14347
14348 parsed_first_argument = true;
14349
14350 // If parsing the argument failed, we need to stop parsing arguments.
14351 if (PM_NODE_TYPE_P(argument, PM_ERROR_RECOVERY_NODE) || parser->recovering) break;
14352
14353 // If the terminator of these arguments is not EOF, then we have a
14354 // specific token we're looking for. In that case we can accept a
14355 // newline here because it is not functioning as a statement terminator.
14356 bool accepted_newline = false;
14357 if (terminator != PM_TOKEN_EOF) {
14358 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14359 }
14360
14361 if (parser->previous.type == PM_TOKEN_COMMA && parsed_bare_hash) {
14362 // If we previously were on a comma and we just parsed a bare hash,
14363 // then we want to continue parsing arguments. This is because the
14364 // comma was grabbed up by the hash parser.
14365 } else if (accept1(parser, PM_TOKEN_COMMA)) {
14366 // If there was a comma, then we need to check if we also accepted a
14367 // newline. If we did, then this is a syntax error.
14368 if (accepted_newline) {
14369 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14370 }
14371
14372 // If this is a command call and an argument takes a block,
14373 // there can be no further arguments. For example,
14374 // `foo(bar 1 do end, 2)` should be rejected.
14375 if (PM_NODE_TYPE_P(argument, PM_CALL_NODE)) {
14376 pm_call_node_t *call = (pm_call_node_t *) argument;
14377 if (call->opening_loc.length == 0 && call->arguments != NULL && call->block != NULL) {
14378 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14379 break;
14380 }
14381 }
14382 } else {
14383 // If there is no comma at the end of the argument list then we're
14384 // done parsing arguments and can break out of this loop.
14385 break;
14386 }
14387
14388 // If we hit the terminator, then that means we have a trailing comma so
14389 // we can accept that output as well.
14390 if (match1(parser, terminator)) {
14391 // A forwarding `...` argument must be the last argument and cannot
14392 // be followed by a trailing comma, e.g. `foo(...,)`. A comma
14393 // followed by another argument is already rejected at the top of
14394 // this loop, so the only case left to reject here is the trailing
14395 // one.
14396 if (parsed_forwarding_arguments) {
14397 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14398 }
14399
14400 break;
14401 }
14402 }
14403}
14404
14416parse_required_destructured_parameter(pm_parser_t *parser) {
14417 expect1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING, PM_ERR_EXPECT_LPAREN_REQ_PARAMETER);
14418
14419 pm_multi_target_node_t *node = pm_multi_target_node_create(parser);
14420 pm_multi_target_node_opening_set(parser, node, &parser->previous);
14421
14422 do {
14423 pm_node_t *param;
14424
14425 // If we get here then we have a trailing comma, which isn't allowed in
14426 // the grammar. In other places, multi targets _do_ allow trailing
14427 // commas, so here we'll assume this is a mistake of the user not
14428 // knowing it's not allowed here.
14429 if (node->lefts.size > 0 && match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
14430 param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14431 pm_multi_target_node_targets_append(parser, node, param);
14432 pm_parser_err_current(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14433 break;
14434 }
14435
14436 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
14437 param = UP(parse_required_destructured_parameter(parser));
14438 } else if (accept1(parser, PM_TOKEN_USTAR)) {
14439 pm_token_t star = parser->previous;
14440 pm_node_t *value = NULL;
14441
14442 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14443 pm_token_t name = parser->previous;
14444 value = UP(pm_required_parameter_node_create(parser, &name));
14445 if (pm_parser_parameter_name_check(parser, &name)) {
14446 pm_node_flag_set_repeated_parameter(value);
14447 }
14448 pm_parser_local_add_token(parser, &name, 1);
14449 }
14450
14451 param = UP(pm_splat_node_create(parser, &star, value));
14452 } else {
14453 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EXPECT_IDENT_REQ_PARAMETER);
14454 pm_token_t name = parser->previous;
14455
14456 param = UP(pm_required_parameter_node_create(parser, &name));
14457 if (pm_parser_parameter_name_check(parser, &name)) {
14458 pm_node_flag_set_repeated_parameter(param);
14459 }
14460 pm_parser_local_add_token(parser, &name, 1);
14461 }
14462
14463 pm_multi_target_node_targets_append(parser, node, param);
14464 } while (accept1(parser, PM_TOKEN_COMMA));
14465
14466 accept1(parser, PM_TOKEN_NEWLINE);
14467 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN_REQ_PARAMETER);
14468 pm_multi_target_node_closing_set(parser, node, &parser->previous);
14469
14470 return node;
14471}
14472
14477typedef enum {
14478 PM_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state
14479 PM_PARAMETERS_ORDER_NOTHING_AFTER = 1,
14480 PM_PARAMETERS_ORDER_KEYWORDS_REST,
14481 PM_PARAMETERS_ORDER_KEYWORDS,
14482 PM_PARAMETERS_ORDER_REST,
14483 PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14484 PM_PARAMETERS_ORDER_OPTIONAL,
14485 PM_PARAMETERS_ORDER_NAMED,
14486 PM_PARAMETERS_ORDER_NONE,
14487} pm_parameters_order_t;
14488
14492static pm_parameters_order_t parameters_ordering[PM_TOKEN_MAXIMUM] = {
14493 [0] = PM_PARAMETERS_NO_CHANGE,
14494 [PM_TOKEN_UAMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14495 [PM_TOKEN_AMPERSAND] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14496 [PM_TOKEN_UDOT_DOT_DOT] = PM_PARAMETERS_ORDER_NOTHING_AFTER,
14497 [PM_TOKEN_IDENTIFIER] = PM_PARAMETERS_ORDER_NAMED,
14498 [PM_TOKEN_PARENTHESIS_LEFT_GROUPING] = PM_PARAMETERS_ORDER_NAMED,
14499 [PM_TOKEN_EQUAL] = PM_PARAMETERS_ORDER_OPTIONAL,
14500 [PM_TOKEN_LABEL] = PM_PARAMETERS_ORDER_KEYWORDS,
14501 [PM_TOKEN_USTAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14502 [PM_TOKEN_STAR] = PM_PARAMETERS_ORDER_AFTER_OPTIONAL,
14503 [PM_TOKEN_USTAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST,
14504 [PM_TOKEN_STAR_STAR] = PM_PARAMETERS_ORDER_KEYWORDS_REST
14505};
14506
14514static bool
14515update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_order_t *current) {
14516 pm_parameters_order_t state = parameters_ordering[token->type];
14517 if (state == PM_PARAMETERS_NO_CHANGE) return true;
14518
14519 // If we see another ordered argument after a optional argument
14520 // we only continue parsing ordered arguments until we stop seeing ordered arguments.
14521 if (*current == PM_PARAMETERS_ORDER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14522 *current = PM_PARAMETERS_ORDER_AFTER_OPTIONAL;
14523 return true;
14524 } else if (*current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL && state == PM_PARAMETERS_ORDER_NAMED) {
14525 return true;
14526 }
14527
14528 if (token->type == PM_TOKEN_USTAR && *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14529 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_STAR);
14530 return false;
14531 } else if (token->type == PM_TOKEN_UDOT_DOT_DOT && (*current >= PM_PARAMETERS_ORDER_KEYWORDS_REST && *current <= PM_PARAMETERS_ORDER_AFTER_OPTIONAL)) {
14532 pm_parser_err_token(parser, token, *current == PM_PARAMETERS_ORDER_AFTER_OPTIONAL ? PM_ERR_PARAMETER_FORWARDING_AFTER_REST : PM_ERR_PARAMETER_ORDER);
14533 return false;
14534 } else if (*current == PM_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) {
14535 // We know what transition we failed on, so we can provide a better error here.
14536 pm_parser_err_token(parser, token, PM_ERR_PARAMETER_ORDER);
14537 return false;
14538 }
14539
14540 if (state < *current) *current = state;
14541 return true;
14542}
14543
14544static PRISM_INLINE void
14545parse_parameters_handle_trailing_comma(
14546 pm_parser_t *parser,
14547 pm_parameters_node_t *params,
14548 pm_parameters_order_t order,
14549 bool in_block,
14550 bool allows_trailing_comma
14551) {
14552 if (!allows_trailing_comma) {
14553 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14554 return;
14555 }
14556
14557 if (in_block) {
14558 if (order >= PM_PARAMETERS_ORDER_NAMED) {
14559 // foo do |bar,|; end
14560 pm_node_t *param = UP(pm_implicit_rest_node_create(parser, &parser->previous));
14561
14562 if (params->rest == NULL) {
14563 pm_parameters_node_rest_set(params, param);
14564 } else {
14565 pm_parser_err_node(parser, UP(param), PM_ERR_PARAMETER_SPLAT_MULTI);
14566 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14567 }
14568 } else {
14569 // foo do |*bar,|; end
14570 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14571 }
14572 } else {
14573 // https://bugs.ruby-lang.org/issues/19107
14574 // Allow `def foo(bar,); end`, `def foo(*bar,); end`, etc. but not `def foo(...,); end`
14575 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1 || order == PM_PARAMETERS_ORDER_NOTHING_AFTER) {
14576 pm_parser_err_previous(parser, PM_ERR_PARAMETER_WILD_LOOSE_COMMA);
14577 }
14578 }
14579}
14580
14584static pm_parameters_node_t *
14585parse_parameters(
14586 pm_parser_t *parser,
14587 pm_binding_power_t binding_power,
14588 bool uses_parentheses,
14589 bool allows_trailing_comma,
14590 bool allows_forwarding_parameters,
14591 bool accepts_blocks_in_defaults,
14592 bool in_block,
14593 pm_diagnostic_id_t diag_id_forwarding,
14594 uint16_t depth
14595) {
14596 pm_do_loop_stack_push(parser, false);
14597
14598 pm_parameters_node_t *params = pm_parameters_node_create(parser);
14599 pm_parameters_order_t order = PM_PARAMETERS_ORDER_NONE;
14600
14601 while (true) {
14602 bool parsing = true;
14603
14604 switch (parser->current.type) {
14605 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
14606 update_parameter_state(parser, &parser->current, &order);
14607 pm_node_t *param = UP(parse_required_destructured_parameter(parser));
14608
14609 if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14610 pm_parameters_node_requireds_append(parser->arena, params, param);
14611 } else {
14612 pm_parameters_node_posts_append(parser->arena, params, param);
14613 }
14614 break;
14615 }
14616 case PM_TOKEN_UAMPERSAND:
14617 case PM_TOKEN_AMPERSAND: {
14618 update_parameter_state(parser, &parser->current, &order);
14619 parser_lex(parser);
14620
14621 pm_token_t operator = parser->previous;
14622 pm_node_t *param;
14623
14624 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1 && accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
14625 param = (pm_node_t *) pm_no_block_parameter_node_create(parser, &operator, &parser->previous);
14626 } else {
14627 pm_token_t name = {0};
14628
14629 bool repeated = false;
14630 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14631 name = parser->previous;
14632 repeated = pm_parser_parameter_name_check(parser, &name);
14633 pm_parser_local_add_token(parser, &name, 1);
14634 } else {
14635 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
14636 }
14637
14638 param = (pm_node_t *) pm_block_parameter_node_create(parser, NTOK2PTR(name), &operator);
14639 if (repeated) {
14640 pm_node_flag_set_repeated_parameter(param);
14641 }
14642 }
14643
14644 if (params->block == NULL) {
14645 pm_parameters_node_block_set(params, param);
14646 } else {
14647 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_BLOCK_MULTI);
14648 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
14649 }
14650
14651 break;
14652 }
14653 case PM_TOKEN_UDOT_DOT_DOT: {
14654 if (!allows_forwarding_parameters) {
14655 pm_parser_err_current(parser, diag_id_forwarding);
14656 }
14657
14658 bool succeeded = update_parameter_state(parser, &parser->current, &order);
14659 parser_lex(parser);
14660
14661 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
14662 pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
14663
14664 if (params->keyword_rest != NULL) {
14665 // If we already have a keyword rest parameter, then we replace it with the
14666 // forwarding parameter and move the keyword rest parameter to the posts list.
14667 pm_node_t *keyword_rest = params->keyword_rest;
14668 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, keyword_rest)));
14669 if (succeeded) pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_FWD);
14670 params->keyword_rest = NULL;
14671 }
14672
14673 pm_parameters_node_keyword_rest_set(params, UP(param));
14674 break;
14675 }
14676 case PM_TOKEN_CLASS_VARIABLE:
14677 case PM_TOKEN_IDENTIFIER:
14678 case PM_TOKEN_CONSTANT:
14679 case PM_TOKEN_INSTANCE_VARIABLE:
14680 case PM_TOKEN_GLOBAL_VARIABLE:
14681 case PM_TOKEN_METHOD_NAME: {
14682 parser_lex(parser);
14683 switch (parser->previous.type) {
14684 case PM_TOKEN_CONSTANT:
14685 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14686 break;
14687 case PM_TOKEN_INSTANCE_VARIABLE:
14688 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
14689 break;
14690 case PM_TOKEN_GLOBAL_VARIABLE:
14691 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
14692 break;
14693 case PM_TOKEN_CLASS_VARIABLE:
14694 pm_parser_err_previous(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
14695 break;
14696 case PM_TOKEN_METHOD_NAME:
14697 pm_parser_err_previous(parser, PM_ERR_PARAMETER_METHOD_NAME);
14698 break;
14699 default: break;
14700 }
14701
14702 if (parser->current.type == PM_TOKEN_EQUAL) {
14703 update_parameter_state(parser, &parser->current, &order);
14704 } else {
14705 update_parameter_state(parser, &parser->previous, &order);
14706 }
14707
14708 pm_token_t name = parser->previous;
14709 bool repeated = pm_parser_parameter_name_check(parser, &name);
14710 pm_parser_local_add_token(parser, &name, 1);
14711
14712 if (match1(parser, PM_TOKEN_EQUAL)) {
14713 pm_token_t operator = parser->current;
14714 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14715 parser_lex(parser);
14716
14717 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
14718 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14719
14720 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14721 pm_node_t *value = parse_value_expression(parser, binding_power, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1));
14722 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14723
14724 pm_optional_parameter_node_t *param = pm_optional_parameter_node_create(parser, &name, &operator, value);
14725
14726 if (repeated) {
14727 pm_node_flag_set_repeated_parameter(UP(param));
14728 }
14729 pm_parameters_node_optionals_append(parser->arena, params, param);
14730
14731 // If the value of the parameter increased the number of
14732 // reads of that parameter, then we need to warn that we
14733 // have a circular definition.
14734 if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14735 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &name, PM_ERR_PARAMETER_CIRCULAR);
14736 }
14737
14738 context_pop(parser);
14739
14740 // If parsing the value of the parameter resulted in error recovery,
14741 // then we can put a missing node in its place and stop parsing the
14742 // parameters entirely now.
14743 if (parser->recovering) {
14744 parsing = false;
14745 break;
14746 }
14747 } else if (order > PM_PARAMETERS_ORDER_AFTER_OPTIONAL) {
14748 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14749 if (repeated) {
14750 pm_node_flag_set_repeated_parameter(UP(param));
14751 }
14752 pm_parameters_node_requireds_append(parser->arena, params, UP(param));
14753 } else {
14754 pm_required_parameter_node_t *param = pm_required_parameter_node_create(parser, &name);
14755 if (repeated) {
14756 pm_node_flag_set_repeated_parameter(UP(param));
14757 }
14758 pm_parameters_node_posts_append(parser->arena, params, UP(param));
14759 }
14760
14761 break;
14762 }
14763 case PM_TOKEN_LABEL: {
14764 if (!uses_parentheses && !in_block) parser->in_keyword_arg = true;
14765 update_parameter_state(parser, &parser->current, &order);
14766
14767 context_push(parser, PM_CONTEXT_DEFAULT_PARAMS);
14768 parser_lex(parser);
14769
14770 pm_token_t name = parser->previous;
14771 pm_token_t local = name;
14772 local.end -= 1;
14773
14774 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)) {
14775 pm_parser_err(parser, PM_TOKEN_START(parser, &local), PM_TOKEN_LENGTH(&local), PM_ERR_ARGUMENT_FORMAL_CONSTANT);
14776 } else if (local.end[-1] == '!' || local.end[-1] == '?') {
14777 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE);
14778 }
14779
14780 bool repeated = pm_parser_parameter_name_check(parser, &local);
14781 pm_parser_local_add_token(parser, &local, 1);
14782
14783 switch (parser->current.type) {
14784 case PM_TOKEN_COMMA:
14785 case PM_TOKEN_PARENTHESIS_RIGHT:
14786 case PM_TOKEN_PIPE: {
14787 context_pop(parser);
14788
14789 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14790 if (repeated) {
14791 pm_node_flag_set_repeated_parameter(param);
14792 }
14793
14794 pm_parameters_node_keywords_append(parser->arena, params, param);
14795 break;
14796 }
14797 case PM_TOKEN_SEMICOLON:
14798 case PM_TOKEN_NEWLINE: {
14799 context_pop(parser);
14800
14801 if (uses_parentheses) {
14802 parsing = false;
14803 break;
14804 }
14805
14806 pm_node_t *param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14807 if (repeated) {
14808 pm_node_flag_set_repeated_parameter(param);
14809 }
14810
14811 pm_parameters_node_keywords_append(parser->arena, params, param);
14812 break;
14813 }
14814 default: {
14815 pm_node_t *param;
14816
14817 if (token_begins_expression_p(parser->current.type)) {
14818 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
14819 uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14820
14821 if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14822 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));
14823 if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14824
14825 if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14826 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &local, PM_ERR_PARAMETER_CIRCULAR);
14827 }
14828
14829 param = UP(pm_optional_keyword_parameter_node_create(parser, &name, value));
14830 }
14831 else {
14832 param = UP(pm_required_keyword_parameter_node_create(parser, &name));
14833 }
14834
14835 if (repeated) {
14836 pm_node_flag_set_repeated_parameter(param);
14837 }
14838
14839 context_pop(parser);
14840 pm_parameters_node_keywords_append(parser->arena, params, param);
14841
14842 // If parsing the value of the parameter resulted in error recovery,
14843 // then we can put a missing node in its place and stop parsing the
14844 // parameters entirely now.
14845 if (parser->recovering) {
14846 parsing = false;
14847 break;
14848 }
14849 }
14850 }
14851
14852 parser->in_keyword_arg = false;
14853 break;
14854 }
14855 case PM_TOKEN_USTAR:
14856 case PM_TOKEN_STAR: {
14857 update_parameter_state(parser, &parser->current, &order);
14858 parser_lex(parser);
14859
14860 pm_token_t operator = parser->previous;
14861 pm_token_t name = { 0 };
14862 bool repeated = false;
14863
14864 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14865 name = parser->previous;
14866 repeated = pm_parser_parameter_name_check(parser, &name);
14867 pm_parser_local_add_token(parser, &name, 1);
14868 } else {
14869 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
14870 }
14871
14872 pm_node_t *param = UP(pm_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
14873 if (repeated) {
14874 pm_node_flag_set_repeated_parameter(param);
14875 }
14876
14877 if (params->rest == NULL) {
14878 pm_parameters_node_rest_set(params, param);
14879 } else {
14880 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_SPLAT_MULTI);
14881 pm_parameters_node_posts_append(parser->arena, params, param);
14882 }
14883
14884 break;
14885 }
14886 case PM_TOKEN_STAR_STAR:
14887 case PM_TOKEN_USTAR_STAR: {
14888 pm_parameters_order_t previous_order = order;
14889 update_parameter_state(parser, &parser->current, &order);
14890 parser_lex(parser);
14891
14892 pm_token_t operator = parser->previous;
14893 pm_node_t *param;
14894
14895 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
14896 if (previous_order <= PM_PARAMETERS_ORDER_KEYWORDS) {
14897 pm_parser_err_previous(parser, PM_ERR_PARAMETER_UNEXPECTED_NO_KW);
14898 }
14899
14900 param = UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
14901 } else {
14902 pm_token_t name = { 0 };
14903
14904 bool repeated = false;
14905 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
14906 name = parser->previous;
14907 repeated = pm_parser_parameter_name_check(parser, &name);
14908 pm_parser_local_add_token(parser, &name, 1);
14909 } else {
14910 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_KEYWORDS;
14911 }
14912
14913 param = UP(pm_keyword_rest_parameter_node_create(parser, &operator, NTOK2PTR(name)));
14914 if (repeated) {
14915 pm_node_flag_set_repeated_parameter(param);
14916 }
14917 }
14918
14919 if (params->keyword_rest == NULL) {
14920 pm_parameters_node_keyword_rest_set(params, param);
14921 } else {
14922 pm_parser_err_node(parser, param, PM_ERR_PARAMETER_ASSOC_SPLAT_MULTI);
14923 pm_parameters_node_posts_append(parser->arena, params, UP(pm_error_recovery_node_create_unexpected(parser, param)));
14924 }
14925
14926 break;
14927 }
14928 default:
14929 if (parser->previous.type == PM_TOKEN_COMMA) {
14930 parse_parameters_handle_trailing_comma(parser, params, order, in_block, allows_trailing_comma);
14931 }
14932
14933 parsing = false;
14934 break;
14935 }
14936
14937 // If we hit some kind of issue while parsing the parameter, this would
14938 // have been set to false. In that case, we need to break out of the
14939 // loop.
14940 if (!parsing) break;
14941
14942 bool accepted_newline = false;
14943 if (uses_parentheses) {
14944 accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
14945 }
14946
14947 if (accept1(parser, PM_TOKEN_COMMA)) {
14948 // If there was a comma, but we also accepted a newline, then this
14949 // is a syntax error.
14950 if (accepted_newline) {
14951 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
14952 }
14953 } else {
14954 // If there was no comma, then we're done parsing parameters.
14955 break;
14956 }
14957 }
14958
14959 pm_do_loop_stack_pop(parser);
14960
14961 // If we don't have any parameters, return `NULL` instead of an empty `ParametersNode`.
14962 if (PM_NODE_START(params) == PM_NODE_END(params)) {
14963 return NULL;
14964 }
14965
14966 return params;
14967}
14968
14973static size_t
14974token_newline_index(const pm_parser_t *parser) {
14975 if (parser->heredoc_end == NULL) {
14976 // This is the common case. In this case we can look at the previously
14977 // recorded newline in the newline list and subtract from the current
14978 // offset.
14979 return parser->line_offsets.size - 1;
14980 } else {
14981 // This is unlikely. This is the case that we have already parsed the
14982 // start of a heredoc, so we cannot rely on looking at the previous
14983 // offset of the newline list, and instead must go through the whole
14984 // process of a binary search for the line number.
14985 return (size_t) pm_line_offset_list_line(&parser->line_offsets, PM_TOKEN_START(parser, &parser->current), 0);
14986 }
14987}
14988
14993static int64_t
14994token_column(const pm_parser_t *parser, size_t newline_index, const pm_token_t *token, bool break_on_non_space) {
14995 const uint8_t *cursor = parser->start + parser->line_offsets.offsets[newline_index];
14996 const uint8_t *end = token->start;
14997
14998 // Skip over the BOM if it is present.
14999 if (
15000 newline_index == 0 &&
15001 parser->start[0] == 0xef &&
15002 parser->start[1] == 0xbb &&
15003 parser->start[2] == 0xbf
15004 ) cursor += 3;
15005
15006 int64_t column = 0;
15007 for (; cursor < end; cursor++) {
15008 switch (*cursor) {
15009 case '\t':
15010 column = ((column / PM_TAB_WHITESPACE_SIZE) + 1) * PM_TAB_WHITESPACE_SIZE;
15011 break;
15012 case ' ':
15013 column++;
15014 break;
15015 default:
15016 column++;
15017 if (break_on_non_space) return -1;
15018 break;
15019 }
15020 }
15021
15022 return column;
15023}
15024
15029static void
15030parser_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) {
15031 // If these warnings are disabled (unlikely), then we can just return.
15032 if (!parser->warn_mismatched_indentation) return;
15033
15034 // If the tokens are on the same line, we do not warn.
15035 size_t closing_newline_index = token_newline_index(parser);
15036 if (opening_newline_index == closing_newline_index) return;
15037
15038 // If the opening token has anything other than spaces or tabs before it,
15039 // then we do not warn. This is unless we are matching up an `if`/`end` pair
15040 // and the `if` immediately follows an `else` keyword.
15041 int64_t opening_column = token_column(parser, opening_newline_index, opening_token, !if_after_else);
15042 if (!if_after_else && (opening_column == -1)) return;
15043
15044 // Get a reference to the closing token off the current parser. This assumes
15045 // that the caller has placed this in the correct position.
15046 pm_token_t *closing_token = &parser->current;
15047
15048 // If the tokens are at the same indentation, we do not warn.
15049 int64_t closing_column = token_column(parser, closing_newline_index, closing_token, true);
15050 if ((closing_column == -1) || (opening_column == closing_column)) return;
15051
15052 // If the closing column is greater than the opening column and we are
15053 // allowing indentation, then we do not warn.
15054 if (allow_indent && (closing_column > opening_column)) return;
15055
15056 // Otherwise, add a warning.
15057 PM_PARSER_WARN_FORMAT(
15058 parser,
15059 PM_TOKEN_START(parser, closing_token),
15060 PM_TOKEN_LENGTH(closing_token),
15061 PM_WARN_INDENTATION_MISMATCH,
15062 (int) (closing_token->end - closing_token->start),
15063 (const char *) closing_token->start,
15064 (int) (opening_token->end - opening_token->start),
15065 (const char *) opening_token->start,
15066 ((int32_t) opening_newline_index) + parser->start_line
15067 );
15068}
15069
15070typedef enum {
15071 PM_RESCUES_BEGIN = 1,
15072 PM_RESCUES_BLOCK,
15073 PM_RESCUES_CLASS,
15074 PM_RESCUES_DEF,
15075 PM_RESCUES_LAMBDA,
15076 PM_RESCUES_MODULE,
15077 PM_RESCUES_SCLASS
15078} pm_rescues_type_t;
15079
15084static PRISM_INLINE void
15085parse_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) {
15086 pm_rescue_node_t *current = NULL;
15087
15088 while (match1(parser, PM_TOKEN_KEYWORD_RESCUE)) {
15089 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15090 parser_lex(parser);
15091
15092 pm_rescue_node_t *rescue = pm_rescue_node_create(parser, &parser->previous);
15093
15094 switch (parser->current.type) {
15095 case PM_TOKEN_EQUAL_GREATER: {
15096 // Here we have an immediate => after the rescue keyword, in which case
15097 // we're going to have an empty list of exceptions to rescue (which
15098 // implies StandardError).
15099 parser_lex(parser);
15100 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15101
15102 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15103 reference = parse_target(parser, reference, false, false);
15104
15105 pm_rescue_node_reference_set(rescue, reference);
15106 break;
15107 }
15108 case PM_TOKEN_NEWLINE:
15109 case PM_TOKEN_SEMICOLON:
15110 case PM_TOKEN_KEYWORD_THEN:
15111 // Here we have a terminator for the rescue keyword, in which
15112 // case we're going to just continue on.
15113 break;
15114 default: {
15115 if (token_begins_expression_p(parser->current.type) || match1(parser, PM_TOKEN_USTAR)) {
15116 // Here we have something that could be an exception expression, so
15117 // we'll attempt to parse it here and any others delimited by commas.
15118
15119 do {
15120 pm_node_t *expression = parse_starred_expression(parser, PM_BINDING_POWER_DEFINED, false, PM_ERR_RESCUE_EXPRESSION, (uint16_t) (depth + 1));
15121 pm_rescue_node_exceptions_append(parser->arena, rescue, expression);
15122
15123 // If we hit a newline, then this is the end of the rescue expression. We
15124 // can continue on to parse the statements.
15125 if (match3(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_THEN)) break;
15126
15127 // If we hit a `=>` then we're going to parse the exception variable. Once
15128 // we've done that, we'll break out of the loop and parse the statements.
15129 if (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
15130 pm_rescue_node_operator_set(parser, rescue, &parser->previous);
15131
15132 pm_node_t *reference = parse_expression(parser, PM_BINDING_POWER_INDEX, PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_RESCUE_VARIABLE, (uint16_t) (depth + 1));
15133 reference = parse_target(parser, reference, false, false);
15134
15135 pm_rescue_node_reference_set(rescue, reference);
15136 break;
15137 }
15138 } while (accept1(parser, PM_TOKEN_COMMA));
15139 }
15140 }
15141 }
15142
15143 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
15144 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
15145 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15146 }
15147 } else {
15148 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_RESCUE_TERM);
15149 rescue->then_keyword_loc = TOK2LOC(parser, &parser->previous);
15150 }
15151
15152 if (!match3(parser, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_END)) {
15153 pm_accepts_block_stack_push(parser, true);
15154 pm_context_t context;
15155
15156 switch (type) {
15157 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_RESCUE; break;
15158 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_RESCUE; break;
15159 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_RESCUE; break;
15160 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_RESCUE; break;
15161 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_RESCUE; break;
15162 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_RESCUE; break;
15163 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_RESCUE; break;
15164 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15165 }
15166
15167 pm_statements_node_t *statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15168 if (statements != NULL) pm_rescue_node_statements_set(rescue, statements);
15169
15170 pm_accepts_block_stack_pop(parser);
15171 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15172 }
15173
15174 if (current == NULL) {
15175 pm_begin_node_rescue_clause_set(parent_node, rescue);
15176 } else {
15177 pm_rescue_node_subsequent_set(current, rescue);
15178 }
15179
15180 current = rescue;
15181 }
15182
15183 // The end node locations on rescue nodes will not be set correctly
15184 // since we won't know the end until we've found all subsequent
15185 // clauses. This sets the end location on all rescues once we know it.
15186 if (current != NULL) {
15187 pm_rescue_node_t *clause = parent_node->rescue_clause;
15188
15189 while (clause != NULL) {
15190 PM_NODE_LENGTH_SET_NODE(clause, current);
15191 clause = clause->subsequent;
15192 }
15193 }
15194
15195 pm_token_t else_keyword;
15196 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
15197 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15198 opening_newline_index = token_newline_index(parser);
15199
15200 else_keyword = parser->current;
15201 opening = &else_keyword;
15202
15203 parser_lex(parser);
15204 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15205
15206 pm_statements_node_t *else_statements = NULL;
15207 if (!match2(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_ENSURE)) {
15208 pm_accepts_block_stack_push(parser, true);
15209 pm_context_t context;
15210
15211 switch (type) {
15212 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ELSE; break;
15213 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ELSE; break;
15214 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ELSE; break;
15215 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ELSE; break;
15216 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ELSE; break;
15217 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ELSE; break;
15218 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ELSE; break;
15219 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_ELSE; break;
15220 }
15221
15222 else_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15223 pm_accepts_block_stack_pop(parser);
15224
15225 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15226 }
15227
15228 pm_else_node_t *else_clause = pm_else_node_create(parser, &else_keyword, else_statements, &parser->current);
15229 pm_begin_node_else_clause_set(parent_node, else_clause);
15230
15231 // If we don't have a `current` rescue node, then this is a dangling
15232 // else, and it's an error.
15233 if (current == NULL) pm_parser_err_node(parser, UP(else_clause), PM_ERR_BEGIN_LONELY_ELSE);
15234 }
15235
15236 if (match1(parser, PM_TOKEN_KEYWORD_ENSURE)) {
15237 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15238 pm_token_t ensure_keyword = parser->current;
15239
15240 parser_lex(parser);
15241 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15242
15243 pm_statements_node_t *ensure_statements = NULL;
15244 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15245 pm_accepts_block_stack_push(parser, true);
15246 pm_context_t context;
15247
15248 switch (type) {
15249 case PM_RESCUES_BEGIN: context = PM_CONTEXT_BEGIN_ENSURE; break;
15250 case PM_RESCUES_BLOCK: context = PM_CONTEXT_BLOCK_ENSURE; break;
15251 case PM_RESCUES_CLASS: context = PM_CONTEXT_CLASS_ENSURE; break;
15252 case PM_RESCUES_DEF: context = PM_CONTEXT_DEF_ENSURE; break;
15253 case PM_RESCUES_LAMBDA: context = PM_CONTEXT_LAMBDA_ENSURE; break;
15254 case PM_RESCUES_MODULE: context = PM_CONTEXT_MODULE_ENSURE; break;
15255 case PM_RESCUES_SCLASS: context = PM_CONTEXT_SCLASS_ENSURE; break;
15256 default: assert(false && "unreachable"); context = PM_CONTEXT_BEGIN_RESCUE; break;
15257 }
15258
15259 ensure_statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15260 pm_accepts_block_stack_pop(parser);
15261
15262 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15263 }
15264
15265 pm_ensure_node_t *ensure_clause = pm_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current);
15266 pm_begin_node_ensure_clause_set(parent_node, ensure_clause);
15267 }
15268
15269 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
15270 if (opening != NULL) parser_warn_indentation_mismatch(parser, opening_newline_index, opening, false, false);
15271 pm_begin_node_end_keyword_set(parser, parent_node, &parser->current);
15272 } else {
15273 pm_token_t end_keyword = (pm_token_t) { .type = PM_TOKEN_KEYWORD_END, .start = parser->previous.end, .end = parser->previous.end };
15274 pm_begin_node_end_keyword_set(parser, parent_node, &end_keyword);
15275 }
15276}
15277
15282static pm_begin_node_t *
15283parse_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) {
15284 pm_begin_node_t *node = pm_begin_node_create(parser, NULL, statements);
15285 parse_rescues(parser, opening_newline_index, opening, node, type, (uint16_t) (depth + 1));
15286
15287 node->base.location.start = U32(start - parser->start);
15288 PM_NODE_LENGTH_SET_TOKEN(parser, node, &parser->current);
15289
15290 return node;
15291}
15292
15297parse_block_parameters(
15298 pm_parser_t *parser,
15299 bool allows_trailing_comma,
15300 const pm_token_t *opening,
15301 bool is_lambda_literal,
15302 bool accepts_blocks_in_defaults,
15303 uint16_t depth
15304) {
15305 pm_parameters_node_t *parameters = NULL;
15306 if (!match1(parser, PM_TOKEN_SEMICOLON)) {
15307 if (!is_lambda_literal) {
15308 context_push(parser, PM_CONTEXT_BLOCK_PARAMETERS);
15309 }
15310 parameters = parse_parameters(
15311 parser,
15312 is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX,
15313 false,
15314 allows_trailing_comma,
15315 false,
15316 accepts_blocks_in_defaults,
15317 true,
15318 is_lambda_literal ? PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_LAMBDA : PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES_BLOCK,
15319 (uint16_t) (depth + 1)
15320 );
15321 if (!is_lambda_literal) {
15322 context_pop(parser);
15323 }
15324 }
15325
15326 pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening);
15327 if (opening != NULL) {
15328 accept1(parser, PM_TOKEN_NEWLINE);
15329
15330 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
15331 do {
15332 switch (parser->current.type) {
15333 case PM_TOKEN_CONSTANT:
15334 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CONSTANT);
15335 parser_lex(parser);
15336 break;
15337 case PM_TOKEN_INSTANCE_VARIABLE:
15338 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_IVAR);
15339 parser_lex(parser);
15340 break;
15341 case PM_TOKEN_GLOBAL_VARIABLE:
15342 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_GLOBAL);
15343 parser_lex(parser);
15344 break;
15345 case PM_TOKEN_CLASS_VARIABLE:
15346 pm_parser_err_current(parser, PM_ERR_ARGUMENT_FORMAL_CLASS);
15347 parser_lex(parser);
15348 break;
15349 default:
15350 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_BLOCK_PARAM_LOCAL_VARIABLE);
15351 break;
15352 }
15353
15354 bool repeated = pm_parser_parameter_name_check(parser, &parser->previous);
15355 pm_parser_local_add_token(parser, &parser->previous, 1);
15356
15357 pm_block_local_variable_node_t *local = pm_block_local_variable_node_create(parser, &parser->previous);
15358 if (repeated) pm_node_flag_set_repeated_parameter(UP(local));
15359
15360 pm_block_parameters_node_append_local(parser->arena, block_parameters, local);
15361 } while (accept1(parser, PM_TOKEN_COMMA));
15362 }
15363 }
15364
15365 return block_parameters;
15366}
15367
15372static bool
15373outer_scope_using_numbered_parameters_p(pm_parser_t *parser) {
15374 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15375 if (scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_FOUND) return true;
15376 }
15377
15378 return false;
15379}
15380
15386static const char * const pm_numbered_parameter_names[] = {
15387 "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9"
15388};
15389
15395static pm_node_t *
15396parse_blocklike_parameters(pm_parser_t *parser, pm_node_t *parameters, const pm_token_t *opening, const pm_token_t *closing) {
15397 pm_node_list_t *implicit_parameters = &parser->current_scope->implicit_parameters;
15398
15399 // If we have ordinary parameters, then we will return them as the set of
15400 // parameters.
15401 if (parameters != NULL) {
15402 // If we also have implicit parameters, then this is an error.
15403 if (implicit_parameters->size > 0) {
15404 pm_node_t *node = implicit_parameters->nodes[0];
15405
15406 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15407 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_ORDINARY);
15408 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15409 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_ORDINARY);
15410 } else {
15411 assert(false && "unreachable");
15412 }
15413 }
15414
15415 return parameters;
15416 }
15417
15418 // If we don't have any implicit parameters, then the set of parameters is
15419 // NULL.
15420 if (implicit_parameters->size == 0) {
15421 return NULL;
15422 }
15423
15424 // If we don't have ordinary parameters, then we now must validate our set
15425 // of implicit parameters. We can only have numbered parameters or it, but
15426 // they cannot be mixed.
15427 uint8_t numbered_parameter = 0;
15428 bool it_parameter = false;
15429
15430 for (size_t index = 0; index < implicit_parameters->size; index++) {
15431 pm_node_t *node = implicit_parameters->nodes[index];
15432
15433 if (PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE)) {
15434 if (it_parameter) {
15435 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_IT);
15436 } else if (outer_scope_using_numbered_parameters_p(parser)) {
15437 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_OUTER_BLOCK);
15438 } else if (parser->current_scope->parameters & PM_SCOPE_PARAMETERS_NUMBERED_INNER) {
15439 pm_parser_err_node(parser, node, PM_ERR_NUMBERED_PARAMETER_INNER_BLOCK);
15440 } else if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
15441 numbered_parameter = MAX(numbered_parameter, (uint8_t) (parser->start[node->location.start + 1] - '0'));
15442 } else {
15443 assert(false && "unreachable");
15444 }
15445 } else if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
15446 if (numbered_parameter > 0) {
15447 pm_parser_err_node(parser, node, PM_ERR_IT_NOT_ALLOWED_NUMBERED);
15448 } else {
15449 it_parameter = true;
15450 }
15451 }
15452 }
15453
15454 if (numbered_parameter > 0) {
15455 // Go through the parent scopes and mark them as being disallowed from
15456 // using numbered parameters because this inner scope is using them.
15457 for (pm_scope_t *scope = parser->current_scope->previous; scope != NULL && !scope->closed; scope = scope->previous) {
15458 scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_INNER;
15459 }
15460 return UP(pm_numbered_parameters_node_create(parser, opening, closing, numbered_parameter));
15461 }
15462
15463 if (it_parameter) {
15464 return UP(pm_it_parameters_node_create(parser, opening, closing));
15465 }
15466
15467 return NULL;
15468}
15469
15473static pm_block_node_t *
15474parse_block(pm_parser_t *parser, uint16_t depth) {
15475 pm_token_t opening = parser->previous;
15476 accept1(parser, PM_TOKEN_NEWLINE);
15477
15478 /* A brace block is delimited by `{`/`}`, whose block-accepting frame is
15479 * managed by the lexer. A `do`/`end` block is delimited by keywords, so we
15480 * push the frame here (covering the block parameters and body) and pop it
15481 * before consuming `end`, mirroring parse.y's `do_body` rule. */
15482 bool do_block = opening.type != PM_TOKEN_BRACE_LEFT && opening.type != PM_TOKEN_BRACE_LEFT_ARGUMENT;
15483 if (do_block) pm_accepts_block_stack_push(parser, true);
15484 pm_parser_scope_push(parser, false);
15485
15486 pm_block_parameters_node_t *block_parameters = NULL;
15487
15488 if (accept1(parser, PM_TOKEN_PIPE)) {
15489 pm_token_t block_parameters_opening = parser->previous;
15490 if (match1(parser, PM_TOKEN_PIPE)) {
15491 block_parameters = pm_block_parameters_node_create(parser, NULL, &block_parameters_opening);
15492 parser->command_start = true;
15493 parser_lex(parser);
15494 } else {
15495 block_parameters = parse_block_parameters(parser, true, &block_parameters_opening, false, true, (uint16_t) (depth + 1));
15496 accept1(parser, PM_TOKEN_NEWLINE);
15497 parser->command_start = true;
15498 expect1(parser, PM_TOKEN_PIPE, PM_ERR_BLOCK_PARAM_PIPE_TERM);
15499 }
15500
15501 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
15502 }
15503
15504 accept1(parser, PM_TOKEN_NEWLINE);
15505 pm_node_t *statements = NULL;
15506
15507 if (!do_block) {
15508 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
15509 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_BRACES, (uint16_t) (depth + 1)));
15510 }
15511
15512 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BLOCK_TERM_BRACE, &opening);
15513 } else {
15514 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
15515 if (!match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_ENSURE)) {
15516 statements = UP(parse_statements(parser, PM_CONTEXT_BLOCK_KEYWORDS, (uint16_t) (depth + 1)));
15517 }
15518
15519 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
15520 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
15521 statements = UP(parse_rescues_implicit_begin(parser, 0, NULL, opening.start, (pm_statements_node_t *) statements, PM_RESCUES_BLOCK, (uint16_t) (depth + 1)));
15522 }
15523 }
15524
15525 /* Pop the `do`/`end` frame before consuming `end` so the token
15526 * following the block is lexed in the enclosing context. */
15527 pm_accepts_block_stack_pop(parser);
15528 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BLOCK_TERM_END, &opening);
15529 }
15530
15531 pm_constant_id_list_t locals;
15532 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
15533 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &opening, &parser->previous);
15534
15535 pm_parser_scope_pop(parser);
15536 return pm_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous);
15537}
15538
15550static bool
15551parse_arguments_list(pm_parser_t *parser, pm_arguments_t *arguments, bool full_arguments, uint8_t flags, uint16_t depth) {
15552 /* Fast path: if the current token can't begin an expression and isn't
15553 * a parenthesis, block opener, or splat/block-pass operator, there are
15554 * no arguments to parse. */
15555 if (
15556 !token_begins_expression_p(parser->current.type) &&
15557 !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)
15558 ) {
15559 return false;
15560 }
15561
15562 bool found = false;
15563 bool parsed_command_args = false;
15564
15565 if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
15566 found |= true;
15567 arguments->opening_loc = TOK2LOC(parser, &parser->previous);
15568
15569 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15570 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15571 } else {
15572 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_PARENTHESIS_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
15573
15574 // `yield` parses its arguments through the restricted `call_args`
15575 // grammar, which (unlike the `opt_call_args` that method calls and
15576 // `super` use) permits neither a block argument nor a trailing
15577 // comma. `full_arguments` is false only for `yield`, so we use it
15578 // to reject the trailing comma in `yield(a,)` that the arguments
15579 // parser otherwise accepts before the closing parenthesis.
15580 if (!full_arguments && parser->previous.type == PM_TOKEN_COMMA) {
15581 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15582 }
15583
15584 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
15585 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARGUMENT_TERM_PAREN, pm_token_str(parser->current.type));
15586 parser->previous.start = parser->previous.end;
15587 parser->previous.type = 0;
15588 }
15589
15590 arguments->closing_loc = TOK2LOC(parser, &parser->previous);
15591 }
15592 } 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)) {
15593 found |= true;
15594 parsed_command_args = true;
15595
15596 /* The command-args frame does not accept blocks, so that a trailing
15597 * `do` binds to this command rather than to an argument. Mirroring
15598 * parse.y's `command_args` rule: when the first argument begins with an
15599 * opening delimiter, the lexer has already pushed that delimiter's
15600 * (block-accepting) frame. We must push the command-args frame beneath
15601 * it, so pop the delimiter frame, push the command-args frame, and then
15602 * restore the delimiter frame on top (the delimiter's closing token
15603 * will pop it back off during argument parsing). */
15604 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);
15605 if (lookahead_delimiter) pm_accepts_block_stack_pop(parser);
15606 pm_accepts_block_stack_push(parser, false);
15607 if (lookahead_delimiter) pm_accepts_block_stack_push(parser, true);
15608
15609 // If we get here, then the subsequent token cannot be used as an infix
15610 // operator. In this case we assume the subsequent token is part of an
15611 // argument to this method call.
15612 parse_arguments(parser, arguments, full_arguments, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
15613
15614 // If we have done with the arguments and still not consumed the comma,
15615 // then we have a trailing comma where we need to check whether it is
15616 // allowed or not.
15617 if (parser->previous.type == PM_TOKEN_COMMA && !match1(parser, PM_TOKEN_SEMICOLON)) {
15618 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
15619 }
15620
15621 /* Symmetrically, if the command arguments are followed by a brace block
15622 * (`m args { }`), the lexer has already pushed that block's frame. Pop
15623 * it, pop the command-args frame beneath it, and restore the block
15624 * frame so the block's `}` still pops it. This mirrors the `tLBRACE_ARG`
15625 * lookahead handling in parse.y's `command_args` rule. */
15626 bool lookahead_brace = match2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT);
15627 if (lookahead_brace) pm_accepts_block_stack_pop(parser);
15628 pm_accepts_block_stack_pop(parser);
15629 if (lookahead_brace) pm_accepts_block_stack_push(parser, true);
15630 }
15631
15632 // If we're at the end of the arguments, we can now check if there is a block
15633 // node that starts with a {. If there is, then we can parse it and add it to
15634 // the arguments.
15635 if (full_arguments) {
15636 pm_block_node_t *block = NULL;
15637
15638 if (accept2(parser, PM_TOKEN_BRACE_LEFT, PM_TOKEN_BRACE_LEFT_ARGUMENT)) {
15639 found |= true;
15640 block = parse_block(parser, (uint16_t) (depth + 1));
15641 pm_arguments_validate_block(parser, arguments, block);
15642 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
15643 found |= true;
15644 block = parse_block(parser, (uint16_t) (depth + 1));
15645 } else if (parsed_command_args && pm_accepts_block_stack_p(parser) && (flags & PM_PARSE_ACCEPTS_DO_BLOCK) && accept1(parser, PM_TOKEN_KEYWORD_DO_BLOCK)) {
15646 found |= true;
15647 block = parse_block(parser, (uint16_t) (depth + 1));
15648 }
15649
15650 if (block != NULL) {
15651 if (arguments->block == NULL && !arguments->has_forwarding) {
15652 arguments->block = UP(block);
15653 } else {
15654 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_BLOCK_MULTI);
15655
15656 if (arguments->block != NULL) {
15657 if (arguments->arguments == NULL) {
15658 arguments->arguments = pm_arguments_node_create(parser);
15659 }
15660 pm_arguments_node_arguments_append(parser->arena, arguments->arguments, arguments->block);
15661 }
15662 arguments->block = UP(block);
15663 }
15664 }
15665 }
15666
15667 return found;
15668}
15669
15674static void
15675parse_return(pm_parser_t *parser, pm_node_t *node) {
15676 bool in_sclass = false;
15677 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15678 switch (context_node->context) {
15679 case PM_CONTEXT_BEGIN_ELSE:
15680 case PM_CONTEXT_BEGIN_ENSURE:
15681 case PM_CONTEXT_BEGIN_RESCUE:
15682 case PM_CONTEXT_BEGIN:
15683 case PM_CONTEXT_CASE_IN:
15684 case PM_CONTEXT_CASE_WHEN:
15685 case PM_CONTEXT_DEFAULT_PARAMS:
15686 case PM_CONTEXT_DEFINED:
15687 case PM_CONTEXT_ELSE:
15688 case PM_CONTEXT_ELSIF:
15689 case PM_CONTEXT_EMBEXPR:
15690 case PM_CONTEXT_FOR_INDEX:
15691 case PM_CONTEXT_FOR:
15692 case PM_CONTEXT_IF:
15693 case PM_CONTEXT_LOOP_PREDICATE:
15694 case PM_CONTEXT_MAIN:
15695 case PM_CONTEXT_MULTI_TARGET:
15696 case PM_CONTEXT_PARENS:
15697 case PM_CONTEXT_POSTEXE:
15698 case PM_CONTEXT_PREDICATE:
15699 case PM_CONTEXT_PREEXE:
15700 case PM_CONTEXT_RESCUE_MODIFIER:
15701 case PM_CONTEXT_TERNARY:
15702 case PM_CONTEXT_UNLESS:
15703 case PM_CONTEXT_UNTIL:
15704 case PM_CONTEXT_WHILE:
15705 // Keep iterating up the lists of contexts, because returns can
15706 // see through these.
15707 continue;
15708 case PM_CONTEXT_SCLASS_ELSE:
15709 case PM_CONTEXT_SCLASS_ENSURE:
15710 case PM_CONTEXT_SCLASS_RESCUE:
15711 case PM_CONTEXT_SCLASS:
15712 in_sclass = true;
15713 continue;
15714 case PM_CONTEXT_CLASS_ELSE:
15715 case PM_CONTEXT_CLASS_ENSURE:
15716 case PM_CONTEXT_CLASS_RESCUE:
15717 case PM_CONTEXT_CLASS:
15718 case PM_CONTEXT_MODULE_ELSE:
15719 case PM_CONTEXT_MODULE_ENSURE:
15720 case PM_CONTEXT_MODULE_RESCUE:
15721 case PM_CONTEXT_MODULE:
15722 // These contexts are invalid for a return.
15723 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15724 return;
15725 case PM_CONTEXT_BLOCK_BRACES:
15726 case PM_CONTEXT_BLOCK_ELSE:
15727 case PM_CONTEXT_BLOCK_ENSURE:
15728 case PM_CONTEXT_BLOCK_KEYWORDS:
15729 case PM_CONTEXT_BLOCK_RESCUE:
15730 case PM_CONTEXT_BLOCK_PARAMETERS:
15731 case PM_CONTEXT_DEF_ELSE:
15732 case PM_CONTEXT_DEF_ENSURE:
15733 case PM_CONTEXT_DEF_PARAMS:
15734 case PM_CONTEXT_DEF_RESCUE:
15735 case PM_CONTEXT_DEF:
15736 case PM_CONTEXT_LAMBDA_BRACES:
15737 case PM_CONTEXT_LAMBDA_DO_END:
15738 case PM_CONTEXT_LAMBDA_ELSE:
15739 case PM_CONTEXT_LAMBDA_ENSURE:
15740 case PM_CONTEXT_LAMBDA_RESCUE:
15741 // These contexts are valid for a return, and we should not
15742 // continue to loop.
15743 return;
15744 case PM_CONTEXT_NONE:
15745 // This case should never happen.
15746 assert(false && "unreachable");
15747 break;
15748 }
15749 }
15750 if (in_sclass && parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
15751 pm_parser_err_node(parser, node, PM_ERR_RETURN_INVALID);
15752 }
15753}
15754
15759static void
15760parse_block_exit(pm_parser_t *parser, pm_node_t *node) {
15761 for (pm_context_node_t *context_node = parser->current_context; context_node != NULL; context_node = context_node->prev) {
15762 switch (context_node->context) {
15763 case PM_CONTEXT_BLOCK_BRACES:
15764 case PM_CONTEXT_BLOCK_KEYWORDS:
15765 case PM_CONTEXT_BLOCK_ELSE:
15766 case PM_CONTEXT_BLOCK_ENSURE:
15767 case PM_CONTEXT_BLOCK_PARAMETERS:
15768 case PM_CONTEXT_BLOCK_RESCUE:
15769 case PM_CONTEXT_DEFINED:
15770 case PM_CONTEXT_FOR:
15771 case PM_CONTEXT_LAMBDA_BRACES:
15772 case PM_CONTEXT_LAMBDA_DO_END:
15773 case PM_CONTEXT_LAMBDA_ELSE:
15774 case PM_CONTEXT_LAMBDA_ENSURE:
15775 case PM_CONTEXT_LAMBDA_RESCUE:
15776 case PM_CONTEXT_LOOP_PREDICATE:
15777 case PM_CONTEXT_UNTIL:
15778 case PM_CONTEXT_WHILE:
15779 // These are the good cases. We're allowed to have a block exit
15780 // in these contexts.
15781 return;
15782 case PM_CONTEXT_POSTEXE:
15783 // https://bugs.ruby-lang.org/issues/20409
15784 if (context_node->context == PM_CONTEXT_POSTEXE) {
15785 if (parser->version < PM_OPTIONS_VERSION_CRUBY_4_1) {
15786 return;
15787 }
15788 }
15790 case PM_CONTEXT_DEF:
15791 case PM_CONTEXT_DEF_PARAMS:
15792 case PM_CONTEXT_DEF_ELSE:
15793 case PM_CONTEXT_DEF_ENSURE:
15794 case PM_CONTEXT_DEF_RESCUE:
15795 case PM_CONTEXT_MAIN:
15796 case PM_CONTEXT_PREEXE:
15797 case PM_CONTEXT_SCLASS:
15798 case PM_CONTEXT_SCLASS_ELSE:
15799 case PM_CONTEXT_SCLASS_ENSURE:
15800 case PM_CONTEXT_SCLASS_RESCUE:
15801 // These are the bad cases. We're not allowed to have a block
15802 // exit in these contexts.
15803 //
15804 // If we get here, then we're about to mark this block exit
15805 // as invalid. However, it could later _become_ valid if we
15806 // find a trailing while/until on the expression. In this
15807 // case instead of adding the error here, we'll add the
15808 // block exit to the list of exits for the expression, and
15809 // the node parsing will handle validating it instead.
15810 assert(parser->current_block_exits != NULL);
15811 pm_node_list_append(parser->arena, parser->current_block_exits, node);
15812 return;
15813 case PM_CONTEXT_BEGIN_ELSE:
15814 case PM_CONTEXT_BEGIN_ENSURE:
15815 case PM_CONTEXT_BEGIN_RESCUE:
15816 case PM_CONTEXT_BEGIN:
15817 case PM_CONTEXT_CASE_IN:
15818 case PM_CONTEXT_CASE_WHEN:
15819 case PM_CONTEXT_CLASS_ELSE:
15820 case PM_CONTEXT_CLASS_ENSURE:
15821 case PM_CONTEXT_CLASS_RESCUE:
15822 case PM_CONTEXT_CLASS:
15823 case PM_CONTEXT_DEFAULT_PARAMS:
15824 case PM_CONTEXT_ELSE:
15825 case PM_CONTEXT_ELSIF:
15826 case PM_CONTEXT_EMBEXPR:
15827 case PM_CONTEXT_FOR_INDEX:
15828 case PM_CONTEXT_IF:
15829 case PM_CONTEXT_MODULE_ELSE:
15830 case PM_CONTEXT_MODULE_ENSURE:
15831 case PM_CONTEXT_MODULE_RESCUE:
15832 case PM_CONTEXT_MODULE:
15833 case PM_CONTEXT_MULTI_TARGET:
15834 case PM_CONTEXT_PARENS:
15835 case PM_CONTEXT_PREDICATE:
15836 case PM_CONTEXT_RESCUE_MODIFIER:
15837 case PM_CONTEXT_TERNARY:
15838 case PM_CONTEXT_UNLESS:
15839 // In these contexts we should continue walking up the list of
15840 // contexts.
15841 break;
15842 case PM_CONTEXT_NONE:
15843 // This case should never happen.
15844 assert(false && "unreachable");
15845 break;
15846 }
15847 }
15848}
15849
15854static pm_node_list_t *
15855push_block_exits(pm_parser_t *parser, pm_node_list_t *current_block_exits) {
15856 pm_node_list_t *previous_block_exits = parser->current_block_exits;
15857 parser->current_block_exits = current_block_exits;
15858 return previous_block_exits;
15859}
15860
15866static void
15867flush_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
15868 pm_node_t *block_exit;
15869 PM_NODE_LIST_FOREACH(parser->current_block_exits, index, block_exit) {
15870 const char *type;
15871
15872 switch (PM_NODE_TYPE(block_exit)) {
15873 case PM_BREAK_NODE: type = "break"; break;
15874 case PM_NEXT_NODE: type = "next"; break;
15875 case PM_REDO_NODE: type = "redo"; break;
15876 default: assert(false && "unreachable"); type = ""; break;
15877 }
15878
15879 PM_PARSER_ERR_NODE_FORMAT(parser, block_exit, PM_ERR_INVALID_BLOCK_EXIT, type);
15880 }
15881
15882 parser->current_block_exits = previous_block_exits;
15883}
15884
15889static void
15890pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
15891 if (match2(parser, PM_TOKEN_KEYWORD_WHILE_MODIFIER, PM_TOKEN_KEYWORD_UNTIL_MODIFIER)) {
15892 // If we matched a trailing while/until, then all of the block exits in
15893 // the contained list are valid. In this case we do not need to do
15894 // anything.
15895 parser->current_block_exits = previous_block_exits;
15896 } else if (previous_block_exits != NULL) {
15897 // If we did not matching a trailing while/until, then all of the block
15898 // exits contained in the list are invalid for this specific context.
15899 // However, they could still become valid in a higher level context if
15900 // there is another list above this one. In this case we'll push all of
15901 // the block exits up to the previous list.
15902 pm_node_list_concat(parser->arena, previous_block_exits, parser->current_block_exits);
15903 parser->current_block_exits = previous_block_exits;
15904 } else {
15905 // If we did not match a trailing while/until and this was the last
15906 // chance to do so, then all of the block exits in the list are invalid
15907 // and we need to add an error for each of them.
15908 flush_block_exits(parser, previous_block_exits);
15909 }
15910}
15911
15912static PRISM_INLINE pm_node_t *
15913parse_predicate(pm_parser_t *parser, pm_binding_power_t binding_power, pm_context_t context, pm_token_t *then_keyword, uint16_t depth) {
15914 context_push(parser, PM_CONTEXT_PREDICATE);
15915 pm_diagnostic_id_t error_id = context == PM_CONTEXT_IF ? PM_ERR_CONDITIONAL_IF_PREDICATE : PM_ERR_CONDITIONAL_UNLESS_PREDICATE;
15916 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));
15917
15918 // Predicates are closed by a term, a "then", or a term and then a "then".
15919 bool predicate_closed = accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15920
15921 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
15922 predicate_closed = true;
15923 *then_keyword = parser->previous;
15924 }
15925
15926 if (!predicate_closed) {
15927 pm_parser_err_current(parser, PM_ERR_CONDITIONAL_PREDICATE_TERM);
15928 }
15929
15930 context_pop(parser);
15931 return predicate;
15932}
15933
15934static PRISM_INLINE pm_node_t *
15935parse_conditional(pm_parser_t *parser, pm_context_t context, size_t opening_newline_index, bool if_after_else, uint16_t depth) {
15936 pm_node_list_t current_block_exits = { 0 };
15937 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
15938
15939 pm_token_t keyword = parser->previous;
15940 pm_token_t then_keyword = { 0 };
15941
15942 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, context, &then_keyword, (uint16_t) (depth + 1));
15943 pm_statements_node_t *statements = NULL;
15944
15945 if (!match3(parser, PM_TOKEN_KEYWORD_ELSIF, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
15946 pm_accepts_block_stack_push(parser, true);
15947 statements = parse_statements(parser, context, (uint16_t) (depth + 1));
15948 pm_accepts_block_stack_pop(parser);
15949 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15950 }
15951
15952 pm_node_t *parent = NULL;
15953
15954 switch (context) {
15955 case PM_CONTEXT_IF:
15956 parent = UP(pm_if_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
15957 break;
15958 case PM_CONTEXT_UNLESS:
15959 parent = UP(pm_unless_node_create(parser, &keyword, predicate, NTOK2PTR(then_keyword), statements));
15960 break;
15961 default:
15962 assert(false && "unreachable");
15963 break;
15964 }
15965
15966 pm_node_t *current = parent;
15967
15968 // Parse any number of elsif clauses. This will form a linked list of if
15969 // nodes pointing to each other from the top.
15970 if (context == PM_CONTEXT_IF) {
15971 while (match1(parser, PM_TOKEN_KEYWORD_ELSIF)) {
15972 if (parser_end_of_line_p(parser)) {
15973 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
15974 }
15975
15976 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
15977 pm_token_t elsif_keyword = parser->current;
15978 parser_lex(parser);
15979
15980 pm_node_t *predicate = parse_predicate(parser, PM_BINDING_POWER_COMPOSITION, PM_CONTEXT_ELSIF, &then_keyword, (uint16_t) (depth + 1));
15981 pm_accepts_block_stack_push(parser, true);
15982
15983 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_ELSIF, (uint16_t) (depth + 1));
15984 pm_accepts_block_stack_pop(parser);
15985 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
15986
15987 pm_node_t *elsif = UP(pm_if_node_create(parser, &elsif_keyword, predicate, NTOK2PTR(then_keyword), statements, NULL, NULL));
15988 ((pm_if_node_t *) current)->subsequent = elsif;
15989 current = elsif;
15990 }
15991 }
15992
15993 if (match1(parser, PM_TOKEN_KEYWORD_ELSE)) {
15994 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
15995 opening_newline_index = token_newline_index(parser);
15996
15997 parser_lex(parser);
15998 pm_token_t else_keyword = parser->previous;
15999
16000 pm_accepts_block_stack_push(parser, true);
16001 pm_statements_node_t *else_statements = parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1));
16002 pm_accepts_block_stack_pop(parser);
16003
16004 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
16005 parser_warn_indentation_mismatch(parser, opening_newline_index, &else_keyword, false, false);
16006 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM_ELSE, &keyword);
16007
16008 pm_else_node_t *else_node = pm_else_node_create(parser, &else_keyword, else_statements, &parser->previous);
16009
16010 switch (context) {
16011 case PM_CONTEXT_IF:
16012 ((pm_if_node_t *) current)->subsequent = UP(else_node);
16013 break;
16014 case PM_CONTEXT_UNLESS:
16015 ((pm_unless_node_t *) parent)->else_clause = else_node;
16016 break;
16017 default:
16018 assert(false && "unreachable");
16019 break;
16020 }
16021 } else {
16022 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, if_after_else, false);
16023 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CONDITIONAL_TERM, &keyword);
16024 }
16025
16026 // Set the appropriate end location for all of the nodes in the subtree.
16027 switch (context) {
16028 case PM_CONTEXT_IF: {
16029 pm_node_t *current = parent;
16030 bool recursing = true;
16031
16032 while (recursing) {
16033 switch (PM_NODE_TYPE(current)) {
16034 case PM_IF_NODE:
16035 pm_if_node_end_keyword_loc_set(parser, (pm_if_node_t *) current, &parser->previous);
16036 current = ((pm_if_node_t *) current)->subsequent;
16037 recursing = current != NULL;
16038 break;
16039 case PM_ELSE_NODE:
16040 pm_else_node_end_keyword_loc_set(parser, (pm_else_node_t *) current, &parser->previous);
16041 recursing = false;
16042 break;
16043 default: {
16044 recursing = false;
16045 break;
16046 }
16047 }
16048 }
16049 break;
16050 }
16051 case PM_CONTEXT_UNLESS:
16052 pm_unless_node_end_keyword_loc_set(parser, (pm_unless_node_t *) parent, &parser->previous);
16053 break;
16054 default:
16055 assert(false && "unreachable");
16056 break;
16057 }
16058
16059 pop_block_exits(parser, previous_block_exits);
16060 return parent;
16061}
16062
16067#define PM_CASE_KEYWORD PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16068 case PM_TOKEN_KEYWORD_ALIAS: case PM_TOKEN_KEYWORD_AND: case PM_TOKEN_KEYWORD_BEGIN: case PM_TOKEN_KEYWORD_BEGIN_UPCASE: \
16069 case PM_TOKEN_KEYWORD_BREAK: case PM_TOKEN_KEYWORD_CASE: case PM_TOKEN_KEYWORD_CLASS: case PM_TOKEN_KEYWORD_DEF: \
16070 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: \
16071 case PM_TOKEN_KEYWORD_ELSIF: case PM_TOKEN_KEYWORD_END: case PM_TOKEN_KEYWORD_END_UPCASE: case PM_TOKEN_KEYWORD_ENSURE: \
16072 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD_FOR: case PM_TOKEN_KEYWORD_IF: case PM_TOKEN_KEYWORD_IN: \
16073 case PM_TOKEN_KEYWORD_MODULE: case PM_TOKEN_KEYWORD_NEXT: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_NOT: \
16074 case PM_TOKEN_KEYWORD_OR: case PM_TOKEN_KEYWORD_REDO: case PM_TOKEN_KEYWORD_RESCUE: case PM_TOKEN_KEYWORD_RETRY: \
16075 case PM_TOKEN_KEYWORD_RETURN: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_SUPER: case PM_TOKEN_KEYWORD_THEN: \
16076 case PM_TOKEN_KEYWORD_TRUE: case PM_TOKEN_KEYWORD_UNDEF: case PM_TOKEN_KEYWORD_UNLESS: case PM_TOKEN_KEYWORD_UNTIL: \
16077 case PM_TOKEN_KEYWORD_WHEN: case PM_TOKEN_KEYWORD_WHILE: case PM_TOKEN_KEYWORD_YIELD
16078
16083#define PM_CASE_OPERATOR PM_TOKEN_AMPERSAND: case PM_TOKEN_BACKTICK: case PM_TOKEN_BANG_EQUAL: \
16084 case PM_TOKEN_BANG_TILDE: case PM_TOKEN_BANG: case PM_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \
16085 case PM_TOKEN_BRACKET_LEFT_RIGHT: case PM_TOKEN_CARET: case PM_TOKEN_EQUAL_EQUAL_EQUAL: case PM_TOKEN_EQUAL_EQUAL: \
16086 case PM_TOKEN_EQUAL_TILDE: case PM_TOKEN_GREATER_EQUAL: case PM_TOKEN_GREATER_GREATER: case PM_TOKEN_GREATER: \
16087 case PM_TOKEN_LESS_EQUAL_GREATER: case PM_TOKEN_LESS_EQUAL: case PM_TOKEN_LESS_LESS: case PM_TOKEN_LESS: \
16088 case PM_TOKEN_MINUS: case PM_TOKEN_PERCENT: case PM_TOKEN_PIPE: case PM_TOKEN_PLUS: case PM_TOKEN_SLASH: \
16089 case PM_TOKEN_STAR_STAR: case PM_TOKEN_STAR: case PM_TOKEN_TILDE: case PM_TOKEN_UAMPERSAND: case PM_TOKEN_UMINUS: \
16090 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_UPLUS: case PM_TOKEN_USTAR: case PM_TOKEN_USTAR_STAR
16091
16097#define PM_CASE_PRIMITIVE PM_TOKEN_INTEGER: case PM_TOKEN_INTEGER_IMAGINARY: case PM_TOKEN_INTEGER_RATIONAL: \
16098 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: case PM_TOKEN_FLOAT: case PM_TOKEN_FLOAT_IMAGINARY: \
16099 case PM_TOKEN_FLOAT_RATIONAL: case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY: case PM_TOKEN_SYMBOL_BEGIN: \
16100 case PM_TOKEN_REGEXP_BEGIN: case PM_TOKEN_XSTRING_BEGIN: case PM_TOKEN_PERCENT_LOWER_X: case PM_TOKEN_PERCENT_LOWER_I: \
16101 case PM_TOKEN_PERCENT_LOWER_W: case PM_TOKEN_PERCENT_UPPER_I: case PM_TOKEN_PERCENT_UPPER_W: \
16102 case PM_TOKEN_STRING_BEGIN: case PM_TOKEN_KEYWORD_NIL: case PM_TOKEN_KEYWORD_SELF: case PM_TOKEN_KEYWORD_TRUE: \
16103 case PM_TOKEN_KEYWORD_FALSE: case PM_TOKEN_KEYWORD___FILE__: case PM_TOKEN_KEYWORD___LINE__: \
16104 case PM_TOKEN_KEYWORD___ENCODING__: case PM_TOKEN_MINUS_GREATER: case PM_TOKEN_HEREDOC_START: \
16105 case PM_TOKEN_UMINUS_NUM: case PM_TOKEN_CHARACTER_LITERAL
16106
16111#define PM_CASE_PARAMETER PM_TOKEN_UAMPERSAND: case PM_TOKEN_AMPERSAND: case PM_TOKEN_UDOT_DOT_DOT: \
16112 case PM_TOKEN_IDENTIFIER: case PM_TOKEN_LABEL: case PM_TOKEN_USTAR: case PM_TOKEN_STAR: case PM_TOKEN_STAR_STAR: \
16113 case PM_TOKEN_USTAR_STAR: case PM_TOKEN_CONSTANT: case PM_TOKEN_INSTANCE_VARIABLE: case PM_TOKEN_GLOBAL_VARIABLE: \
16114 case PM_TOKEN_CLASS_VARIABLE
16115
16120#define PM_CASE_WRITABLE PM_CLASS_VARIABLE_READ_NODE: case PM_CONSTANT_PATH_NODE: \
16121 case PM_CONSTANT_READ_NODE: case PM_GLOBAL_VARIABLE_READ_NODE: case PM_LOCAL_VARIABLE_READ_NODE: \
16122 case PM_INSTANCE_VARIABLE_READ_NODE: case PM_MULTI_TARGET_NODE: case PM_BACK_REFERENCE_READ_NODE: \
16123 case PM_NUMBERED_REFERENCE_READ_NODE: case PM_IT_LOCAL_VARIABLE_READ_NODE
16124
16125// Assert here that the flags are the same so that we can safely switch the type
16126// of the node without having to move the flags.
16127PM_STATIC_ASSERT(__LINE__, ((int) PM_STRING_FLAGS_FORCED_UTF8_ENCODING) == ((int) PM_ENCODING_FLAGS_FORCED_UTF8_ENCODING), "Expected the flags to match.");
16128
16133static PRISM_INLINE pm_node_flags_t
16134parse_unescaped_encoding(const pm_parser_t *parser, const pm_encoding_t *explicit_encoding) {
16135 if (explicit_encoding != NULL) {
16136 if (explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
16137 // If the there's an explicit encoding and it's using a UTF-8 escape
16138 // sequence, then mark the string as UTF-8.
16139 return PM_STRING_FLAGS_FORCED_UTF8_ENCODING;
16140 } else if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
16141 // If there's a non-UTF-8 escape sequence being used, then the
16142 // string uses the source encoding, unless the source is marked as
16143 // US-ASCII. In that case the string is forced as ASCII-8BIT in
16144 // order to keep the string valid.
16145 return PM_STRING_FLAGS_FORCED_BINARY_ENCODING;
16146 }
16147 }
16148 return 0;
16149}
16150
16155static pm_node_t *
16156parse_string_part(pm_parser_t *parser, uint16_t depth) {
16157 switch (parser->current.type) {
16158 // Here the lexer has returned to us plain string content. In this case
16159 // we'll create a string node that has no opening or closing and return that
16160 // as the part. These kinds of parts look like:
16161 //
16162 // "aaa #{bbb} #@ccc ddd"
16163 // ^^^^ ^ ^^^^
16164 case PM_TOKEN_STRING_CONTENT: {
16165 pm_node_t *node = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16166 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
16167
16168 parser_lex(parser);
16169 return node;
16170 }
16171 // Here the lexer has returned the beginning of an embedded expression. In
16172 // that case we'll parse the inner statements and return that as the part.
16173 // These kinds of parts look like:
16174 //
16175 // "aaa #{bbb} #@ccc ddd"
16176 // ^^^^^^
16177 case PM_TOKEN_EMBEXPR_BEGIN: {
16178 // Ruby disallows seeing encoding around interpolation in strings,
16179 // even though it is known at parse time.
16180 parser->explicit_encoding = NULL;
16181
16182 pm_lex_state_t state = parser->lex_state;
16183 int brace_nesting = parser->brace_nesting;
16184
16185 parser->brace_nesting = 0;
16186 lex_state_set(parser, PM_LEX_STATE_BEG);
16187 parser_lex(parser);
16188
16189 pm_token_t opening = parser->previous;
16190 pm_statements_node_t *statements = NULL;
16191
16192 if (!match3(parser, PM_TOKEN_EMBEXPR_END, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
16193 statements = parse_statements(parser, PM_CONTEXT_EMBEXPR, (uint16_t) (depth + 1));
16194 }
16195
16196 parser->brace_nesting = brace_nesting;
16197 lex_state_set(parser, state);
16198 expect1(parser, PM_TOKEN_EMBEXPR_END, PM_ERR_EMBEXPR_END);
16199
16200 // If this set of embedded statements only contains a single
16201 // statement, then Ruby does not consider it as a possible statement
16202 // that could emit a line event.
16203 if (statements != NULL && statements->body.size == 1) {
16204 pm_node_flag_unset(statements->body.nodes[0], PM_NODE_FLAG_NEWLINE);
16205 }
16206
16207 return UP(pm_embedded_statements_node_create(parser, &opening, statements, &parser->previous));
16208 }
16209
16210 // Here the lexer has returned the beginning of an embedded variable.
16211 // In that case we'll parse the variable and create an appropriate node
16212 // for it and then return that node. These kinds of parts look like:
16213 //
16214 // "aaa #{bbb} #@ccc ddd"
16215 // ^^^^^
16216 case PM_TOKEN_EMBVAR: {
16217 // Ruby disallows seeing encoding around interpolation in strings,
16218 // even though it is known at parse time.
16219 parser->explicit_encoding = NULL;
16220
16221 lex_state_set(parser, PM_LEX_STATE_BEG);
16222 parser_lex(parser);
16223
16224 pm_token_t operator = parser->previous;
16225 pm_node_t *variable;
16226
16227 switch (parser->current.type) {
16228 // In this case a back reference is being interpolated. We'll
16229 // create a global variable read node.
16230 case PM_TOKEN_BACK_REFERENCE:
16231 parser_lex(parser);
16232 variable = UP(pm_back_reference_read_node_create(parser, &parser->previous));
16233 break;
16234 // In this case an nth reference is being interpolated. We'll
16235 // create a global variable read node.
16236 case PM_TOKEN_NUMBERED_REFERENCE:
16237 parser_lex(parser);
16238 variable = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16239 break;
16240 // In this case a global variable is being interpolated. We'll
16241 // create a global variable read node.
16242 case PM_TOKEN_GLOBAL_VARIABLE:
16243 parser_lex(parser);
16244 variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
16245 break;
16246 // In this case an instance variable is being interpolated.
16247 // We'll create an instance variable read node.
16248 case PM_TOKEN_INSTANCE_VARIABLE:
16249 parser_lex(parser);
16250 variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
16251 break;
16252 // In this case a class variable is being interpolated. We'll
16253 // create a class variable read node.
16254 case PM_TOKEN_CLASS_VARIABLE:
16255 parser_lex(parser);
16256 variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
16257 break;
16258 // We can hit here if we got an invalid token. In that case
16259 // we'll not attempt to lex this token and instead just return a
16260 // missing node.
16261 default:
16262 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_EMBVAR_INVALID);
16263 variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16264 break;
16265 }
16266
16267 return UP(pm_embedded_variable_node_create(parser, &operator, variable));
16268 }
16269 default:
16270 parser_lex(parser);
16271 pm_parser_err_previous(parser, PM_ERR_CANNOT_PARSE_STRING_PART);
16272 return NULL;
16273 }
16274}
16275
16281static const uint8_t *
16282parse_operator_symbol_name(const pm_token_t *name) {
16283 switch (name->type) {
16284 case PM_TOKEN_TILDE:
16285 case PM_TOKEN_BANG:
16286 if (name->end[-1] == '@') return name->end - 1;
16288 default:
16289 return name->end;
16290 }
16291}
16292
16293static pm_node_t *
16294parse_operator_symbol(pm_parser_t *parser, const pm_token_t *opening, pm_lex_state_t next_state) {
16295 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, opening, &parser->current, NULL);
16296 const uint8_t *end = parse_operator_symbol_name(&parser->current);
16297
16298 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16299 parser_lex(parser);
16300
16301 pm_string_shared_init(&symbol->unescaped, parser->previous.start, end);
16302 pm_node_flag_set(UP(symbol), PM_SYMBOL_FLAGS_FORCED_US_ASCII_ENCODING);
16303
16304 return UP(symbol);
16305}
16306
16312static pm_node_t *
16313parse_symbol(pm_parser_t *parser, pm_lex_mode_t *lex_mode, pm_lex_state_t next_state, uint16_t depth) {
16314 const pm_token_t opening = parser->previous;
16315
16316 if (lex_mode->mode != PM_LEX_STRING) {
16317 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16318
16319 switch (parser->current.type) {
16320 case PM_CASE_OPERATOR:
16321 return parse_operator_symbol(parser, &opening, next_state == PM_LEX_STATE_NONE ? PM_LEX_STATE_ENDFN : next_state);
16322 case PM_TOKEN_IDENTIFIER:
16323 case PM_TOKEN_CONSTANT:
16324 case PM_TOKEN_INSTANCE_VARIABLE:
16325 case PM_TOKEN_METHOD_NAME:
16326 case PM_TOKEN_CLASS_VARIABLE:
16327 case PM_TOKEN_GLOBAL_VARIABLE:
16328 case PM_TOKEN_NUMBERED_REFERENCE:
16329 case PM_TOKEN_BACK_REFERENCE:
16330 case PM_CASE_KEYWORD:
16331 parser_lex(parser);
16332 break;
16333 default:
16334 expect2(parser, PM_TOKEN_IDENTIFIER, PM_TOKEN_METHOD_NAME, PM_ERR_SYMBOL_INVALID);
16335 break;
16336 }
16337
16338 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, &parser->previous, NULL);
16339 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16340 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16341
16342 return UP(symbol);
16343 }
16344
16345 if (lex_mode->as.string.interpolation) {
16346 // If we have the end of the symbol, then we can return an empty symbol.
16347 if (match1(parser, PM_TOKEN_STRING_END)) {
16348 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16349 parser_lex(parser);
16350 pm_token_t content = {
16351 .type = PM_TOKEN_STRING_CONTENT,
16352 .start = parser->previous.start,
16353 .end = parser->previous.start
16354 };
16355
16356 return UP(pm_symbol_node_create(parser, &opening, &content, &parser->previous));
16357 }
16358
16359 // Now we can parse the first part of the symbol.
16360 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
16361
16362 // If we got a string part, then it's possible that we could transform
16363 // what looks like an interpolated symbol into a regular symbol.
16364 if (part && PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16365 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16366 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16367
16368 return UP(pm_string_node_to_symbol_node(parser, (pm_string_node_t *) part, &opening, &parser->previous));
16369 }
16370
16371 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16372 if (part) pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16373
16374 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16375 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16376 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16377 }
16378 }
16379
16380 if (next_state != PM_LEX_STATE_NONE) lex_state_set(parser, next_state);
16381 if (match1(parser, PM_TOKEN_EOF)) {
16382 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16383 } else {
16384 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_INTERPOLATED);
16385 }
16386
16387 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16388 return UP(symbol);
16389 }
16390
16391 pm_token_t content;
16392 pm_string_t unescaped;
16393
16394 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16395 content = parser->current;
16396 unescaped = parser->current_string;
16397 parser_lex(parser);
16398
16399 // If we have two string contents in a row, then the content of this
16400 // symbol is split because of heredoc contents. This looks like:
16401 //
16402 // <<A; :'a
16403 // A
16404 // b'
16405 //
16406 // In this case, the best way we have to represent this is as an
16407 // interpolated string node, so that's what we'll do here.
16408 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16409 pm_interpolated_symbol_node_t *symbol = pm_interpolated_symbol_node_create(parser, &opening, NULL, &opening);
16410 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16411 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16412
16413 part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->current, NULL, &parser->current_string));
16414 pm_interpolated_symbol_node_append(parser->arena, symbol, part);
16415
16416 if (next_state != PM_LEX_STATE_NONE) {
16417 lex_state_set(parser, next_state);
16418 }
16419
16420 parser_lex(parser);
16421 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16422
16423 pm_interpolated_symbol_node_closing_loc_set(parser, symbol, &parser->previous);
16424 return UP(symbol);
16425 }
16426 } else {
16427 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end };
16428 pm_string_shared_init(&unescaped, content.start, content.end);
16429 }
16430
16431 if (next_state != PM_LEX_STATE_NONE) {
16432 lex_state_set(parser, next_state);
16433 }
16434
16435 if (match1(parser, PM_TOKEN_EOF)) {
16436 pm_parser_err_token(parser, &opening, PM_ERR_SYMBOL_TERM_DYNAMIC);
16437 } else {
16438 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_SYMBOL_TERM_DYNAMIC);
16439 }
16440
16441 return UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, false)));
16442}
16443
16448static PRISM_INLINE pm_node_t *
16449parse_undef_argument(pm_parser_t *parser, uint16_t depth) {
16450 switch (parser->current.type) {
16451 case PM_CASE_OPERATOR:
16452 return parse_operator_symbol(parser, NULL, PM_LEX_STATE_NONE);
16453 case PM_CASE_KEYWORD:
16454 case PM_TOKEN_CONSTANT:
16455 case PM_TOKEN_IDENTIFIER:
16456 case PM_TOKEN_METHOD_NAME: {
16457 parser_lex(parser);
16458
16459 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16460 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16461 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16462
16463 return UP(symbol);
16464 }
16465 case PM_TOKEN_SYMBOL_BEGIN: {
16466 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
16467 parser_lex(parser);
16468
16469 return parse_symbol(parser, &lex_mode, PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16470 }
16471 default:
16472 pm_parser_err_current(parser, PM_ERR_UNDEF_ARGUMENT);
16473 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16474 }
16475}
16476
16483static PRISM_INLINE pm_node_t *
16484parse_alias_argument(pm_parser_t *parser, bool first, uint16_t depth) {
16485 switch (parser->current.type) {
16486 case PM_CASE_OPERATOR:
16487 return parse_operator_symbol(parser, NULL, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE);
16488 case PM_CASE_KEYWORD:
16489 case PM_TOKEN_CONSTANT:
16490 case PM_TOKEN_IDENTIFIER:
16491 case PM_TOKEN_METHOD_NAME: {
16492 if (first) lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
16493 parser_lex(parser);
16494
16495 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, NULL, &parser->previous, NULL);
16496 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.end);
16497 pm_node_flag_set(UP(symbol), parse_symbol_encoding(parser, parser->explicit_encoding, &parser->previous, &symbol->unescaped, false));
16498
16499 return UP(symbol);
16500 }
16501 case PM_TOKEN_SYMBOL_BEGIN: {
16502 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
16503 parser_lex(parser);
16504
16505 return parse_symbol(parser, &lex_mode, first ? PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM : PM_LEX_STATE_NONE, (uint16_t) (depth + 1));
16506 }
16507 case PM_TOKEN_BACK_REFERENCE:
16508 parser_lex(parser);
16509 return UP(pm_back_reference_read_node_create(parser, &parser->previous));
16510 case PM_TOKEN_NUMBERED_REFERENCE:
16511 parser_lex(parser);
16512 return UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
16513 case PM_TOKEN_GLOBAL_VARIABLE:
16514 parser_lex(parser);
16515 return UP(pm_global_variable_read_node_create(parser, &parser->previous));
16516 default:
16517 pm_parser_err_current(parser, PM_ERR_ALIAS_ARGUMENT);
16518 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
16519 }
16520}
16521
16526static pm_node_t *
16527parse_variable(pm_parser_t *parser) {
16528 pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &parser->previous);
16529 int depth;
16530 bool is_numbered_param = pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
16531
16532 if (!is_numbered_param && ((depth = pm_parser_local_depth_constant_id(parser, name_id)) != -1)) {
16533 return UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, (uint32_t) depth, false));
16534 }
16535
16536 pm_scope_t *current_scope = parser->current_scope;
16537 if (!current_scope->closed && !(current_scope->parameters & PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED)) {
16538 if (is_numbered_param) {
16539 // When you use a numbered parameter, it implies the existence of
16540 // all of the locals that exist before it. For example, referencing
16541 // _2 means that _1 must exist. Therefore here we loop through all
16542 // of the possibilities and add them into the constant pool.
16543 uint8_t maximum = (uint8_t) (parser->previous.start[1] - '0');
16544 for (uint8_t number = 1; number <= maximum; number++) {
16545 pm_parser_local_add_constant(parser, pm_numbered_parameter_names[number - 1], 2);
16546 }
16547
16548 if (!match1(parser, PM_TOKEN_EQUAL)) {
16549 parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_NUMBERED_FOUND;
16550 }
16551
16552 pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false));
16553 pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16554
16555 return node;
16556 } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16557 pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous));
16558 pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16559
16560 return node;
16561 }
16562 }
16563
16564 return NULL;
16565}
16566
16570static pm_node_t *
16571parse_variable_call(pm_parser_t *parser) {
16572 pm_node_flags_t flags = 0;
16573
16574 if (!match1(parser, PM_TOKEN_PARENTHESIS_LEFT) && (parser->previous.end[-1] != '!') && (parser->previous.end[-1] != '?')) {
16575 pm_node_t *node = parse_variable(parser);
16576 if (node != NULL) return node;
16577 flags |= PM_CALL_NODE_FLAGS_VARIABLE_CALL;
16578 }
16579
16580 pm_call_node_t *node = pm_call_node_variable_call_create(parser, &parser->previous);
16581 pm_node_flag_set(UP(node), flags);
16582
16583 return UP(node);
16584}
16585
16592parse_method_definition_name(pm_parser_t *parser) {
16593 switch (parser->current.type) {
16594 case PM_CASE_KEYWORD:
16595 case PM_TOKEN_CONSTANT:
16596 case PM_TOKEN_METHOD_NAME:
16597 parser_lex(parser);
16598 return parser->previous;
16599 case PM_TOKEN_IDENTIFIER:
16600 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current));
16601 parser_lex(parser);
16602 return parser->previous;
16603 case PM_CASE_OPERATOR:
16604 lex_state_set(parser, PM_LEX_STATE_ENDFN);
16605 parser_lex(parser);
16606 return parser->previous;
16607 default:
16608 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_NAME, pm_token_str(parser->current.type));
16609 return (pm_token_t) { .type = 0, .start = parser->current.start, .end = parser->current.end };
16610 }
16611}
16612
16613static void
16614parse_heredoc_dedent_string(pm_arena_t *arena, pm_string_t *string, size_t common_whitespace) {
16615 // Make a writable copy in the arena if the string isn't already writable.
16616 // We keep a mutable pointer to the arena memory so we can memmove into it
16617 // below without casting away const from the string's source field.
16618 uint8_t *writable;
16619
16620 if (string->type != PM_STRING_OWNED) {
16621 size_t length = pm_string_length(string);
16622 writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
16623 pm_string_constant_init(string, (const char *) writable, length);
16624 } else {
16625 writable = (uint8_t *) string->source;
16626 }
16627
16628 // Now get the bounds of the existing string. We'll use this as a
16629 // destination to move bytes into. We'll also use it for bounds checking
16630 // since we don't require that these strings be null terminated.
16631 size_t dest_length = pm_string_length(string);
16632 const uint8_t *source_cursor = writable;
16633 const uint8_t *source_end = source_cursor + dest_length;
16634
16635 // We're going to move bytes backward in the string when we get leading
16636 // whitespace, so we'll maintain a pointer to the current position in the
16637 // string that we're writing to.
16638 size_t trimmed_whitespace = 0;
16639
16640 // While we haven't reached the amount of common whitespace that we need to
16641 // trim and we haven't reached the end of the string, we'll keep trimming
16642 // whitespace. Trimming in this context means skipping over these bytes such
16643 // that they aren't copied into the new string.
16644 while ((source_cursor < source_end) && pm_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) {
16645 if (*source_cursor == '\t') {
16646 trimmed_whitespace = (trimmed_whitespace / PM_TAB_WHITESPACE_SIZE + 1) * PM_TAB_WHITESPACE_SIZE;
16647 if (trimmed_whitespace > common_whitespace) break;
16648 } else {
16649 trimmed_whitespace++;
16650 }
16651
16652 source_cursor++;
16653 dest_length--;
16654 }
16655
16656 memmove(writable, source_cursor, (size_t) (source_end - source_cursor));
16657 string->length = dest_length;
16658}
16659
16664static PRISM_INLINE bool
16665heredoc_dedent_discard_string_node(pm_parser_t *parser, pm_string_node_t *string_node) {
16666 if (string_node->unescaped.length == 0) {
16667 const uint8_t *cursor = parser->start + PM_LOCATION_START(&string_node->content_loc);
16668 return pm_memchr(cursor, '\\', string_node->content_loc.length, parser->encoding_changed, parser->encoding) == NULL;
16669 }
16670 return false;
16671}
16672
16676static void
16677parse_heredoc_dedent(pm_parser_t *parser, pm_node_list_t *nodes, size_t common_whitespace) {
16678 // The next node should be dedented if it's the first node in the list or if
16679 // it follows a string node.
16680 bool dedent_next = true;
16681
16682 // Iterate over all nodes, and trim whitespace accordingly. We're going to
16683 // keep around two indices: a read and a write.
16684 size_t write_index = 0;
16685
16686 pm_node_t *node;
16687 PM_NODE_LIST_FOREACH(nodes, read_index, node) {
16688 // We're not manipulating child nodes that aren't strings. In this case
16689 // we'll skip past it and indicate that the subsequent node should not
16690 // be dedented.
16691 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE)) {
16692 nodes->nodes[write_index++] = node;
16693 dedent_next = false;
16694 continue;
16695 }
16696
16697 pm_string_node_t *string_node = ((pm_string_node_t *) node);
16698 if (dedent_next) {
16699 parse_heredoc_dedent_string(parser->arena, &string_node->unescaped, common_whitespace);
16700 }
16701
16702 if (heredoc_dedent_discard_string_node(parser, string_node)) {
16703 } else {
16704 nodes->nodes[write_index++] = node;
16705 }
16706
16707 // We always dedent the next node if it follows a string node.
16708 dedent_next = true;
16709 }
16710
16711 nodes->size = write_index;
16712}
16713
16717static pm_token_t
16718parse_strings_empty_content(const uint8_t *location) {
16719 return (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = location, .end = location };
16720}
16721
16725static PRISM_INLINE pm_node_t *
16726parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint16_t depth) {
16727 assert(parser->current.type == PM_TOKEN_STRING_BEGIN);
16728 bool concating = false;
16729
16730 while (match1(parser, PM_TOKEN_STRING_BEGIN)) {
16731 pm_node_t *node = NULL;
16732
16733 // Here we have found a string literal. We'll parse it and add it to
16734 // the list of strings.
16735 const pm_lex_mode_t *lex_mode = parser->lex_modes.current;
16736 assert(lex_mode->mode == PM_LEX_STRING);
16737 bool lex_interpolation = lex_mode->as.string.interpolation;
16738 bool label_allowed = lex_mode->as.string.label_allowed && accepts_label;
16739
16740 pm_token_t opening = parser->current;
16741 parser_lex(parser);
16742
16743 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16744 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16745 // If we get here, then we have an end immediately after a
16746 // start. In that case we'll create an empty content token and
16747 // return an uninterpolated string.
16748 pm_token_t content = parse_strings_empty_content(parser->previous.start);
16749 pm_string_node_t *string = pm_string_node_create(parser, &opening, &content, &parser->previous);
16750
16751 pm_string_shared_init(&string->unescaped, content.start, content.end);
16752 node = UP(string);
16753 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16754 // If we get here, then we have an end of a label immediately
16755 // after a start. In that case we'll create an empty symbol
16756 // node.
16757 pm_symbol_node_t *symbol = pm_symbol_node_create(parser, &opening, NULL, &parser->previous);
16758 pm_string_shared_init(&symbol->unescaped, parser->previous.start, parser->previous.start);
16759 node = UP(symbol);
16760
16761 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16762 } else if (!lex_interpolation) {
16763 // If we don't accept interpolation then we expect the string to
16764 // start with a single string content node.
16765 pm_string_t unescaped;
16766 pm_token_t content;
16767
16768 if (match1(parser, PM_TOKEN_EOF)) {
16769 unescaped = PM_STRING_EMPTY;
16770 content = (pm_token_t) { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start, .end = parser->start };
16771 } else {
16772 unescaped = parser->current_string;
16773 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_EXPECT_STRING_CONTENT);
16774 content = parser->previous;
16775 }
16776
16777 // It is unfortunately possible to have multiple string content
16778 // nodes in a row in the case that there's heredoc content in
16779 // the middle of the string, like this cursed example:
16780 //
16781 // <<-END+'b
16782 // a
16783 // END
16784 // c'+'d'
16785 //
16786 // In that case we need to switch to an interpolated string to
16787 // be able to contain all of the parts.
16788 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16789 pm_node_list_t parts = { 0 };
16790 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16791 pm_node_list_append(parser->arena, &parts, part);
16792
16793 do {
16794 part = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16795 pm_node_list_append(parser->arena, &parts, part);
16796 parser_lex(parser);
16797 } while (match1(parser, PM_TOKEN_STRING_CONTENT));
16798
16799 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_LITERAL_EOF);
16800 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16801 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16802 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, parser->explicit_encoding, &content, &unescaped, true)));
16803 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16804 } else if (match1(parser, PM_TOKEN_EOF)) {
16805 pm_parser_err_token(parser, &opening, PM_ERR_STRING_LITERAL_EOF);
16806 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16807 } else if (accept1(parser, PM_TOKEN_STRING_END)) {
16808 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16809 } else {
16810 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_STRING_LITERAL_TERM, pm_token_str(parser->previous.type));
16811 parser->previous.start = parser->previous.end;
16812 parser->previous.type = 0;
16813 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped));
16814 }
16815 } else if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16816 // In this case we've hit string content so we know the string
16817 // at least has something in it. We'll need to check if the
16818 // following token is the end (in which case we can return a
16819 // plain string) or if it's not then it has interpolation.
16820 pm_token_t content = parser->current;
16821 pm_string_t unescaped = parser->current_string;
16822 const pm_encoding_t *explicit_encoding = parser->explicit_encoding;
16823 parser_lex(parser);
16824
16825 if (match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
16826 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
16827 pm_node_flag_set(node, parse_unescaped_encoding(parser, explicit_encoding));
16828
16829 // Kind of odd behavior, but basically if we have an
16830 // unterminated string and it ends in a newline, we back up one
16831 // character so that the error message is on the last line of
16832 // content in the string.
16833 if (!accept1(parser, PM_TOKEN_STRING_END)) {
16834 const uint8_t *location = parser->previous.end;
16835 if (location > parser->start && location[-1] == '\n') location--;
16836 pm_parser_err(parser, U32(location - parser->start), 0, PM_ERR_STRING_LITERAL_EOF);
16837
16838 parser->previous.start = parser->previous.end;
16839 parser->previous.type = 0;
16840 }
16841 } else if (accept1(parser, PM_TOKEN_LABEL_END)) {
16842 node = UP(pm_symbol_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped, parse_symbol_encoding(parser, explicit_encoding, &content, &unescaped, true)));
16843 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16844 } else {
16845 // If we get here, then we have interpolation so we'll need
16846 // to create a string or symbol node with interpolation.
16847 pm_node_list_t parts = { 0 };
16848 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
16849 pm_node_flag_set(part, parse_unescaped_encoding(parser, explicit_encoding));
16850 pm_node_list_append(parser->arena, &parts, part);
16851
16852 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
16853 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16854 pm_node_list_append(parser->arena, &parts, part);
16855 }
16856 }
16857
16858 if (accept1(parser, PM_TOKEN_LABEL_END)) {
16859 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
16860 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16861 } else if (match1(parser, PM_TOKEN_EOF)) {
16862 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
16863 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
16864 } else {
16865 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
16866 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16867 }
16868 }
16869 } else {
16870 // If we get here, then the first part of the string is not plain
16871 // string content, in which case we need to parse the string as an
16872 // interpolated string.
16873 pm_node_list_t parts = { 0 };
16874 pm_node_t *part;
16875
16876 while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
16877 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
16878 pm_node_list_append(parser->arena, &parts, part);
16879 }
16880 }
16881
16882 if (accept1(parser, PM_TOKEN_LABEL_END)) {
16883 node = UP(pm_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous));
16884 if (!label_allowed) pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_LABEL);
16885 } else if (match1(parser, PM_TOKEN_EOF)) {
16886 pm_parser_err_token(parser, &opening, PM_ERR_STRING_INTERPOLATED_TERM);
16887 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->current));
16888 } else {
16889 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_STRING_INTERPOLATED_TERM);
16890 node = UP(pm_interpolated_string_node_create(parser, &opening, &parts, &parser->previous));
16891 }
16892 }
16893
16894 if (current == NULL) {
16895 // If the node we just parsed is a symbol node, then we can't
16896 // concatenate it with anything else, so we can now return that
16897 // node.
16898 if (PM_NODE_TYPE_P(node, PM_SYMBOL_NODE) || PM_NODE_TYPE_P(node, PM_INTERPOLATED_SYMBOL_NODE)) {
16899 return node;
16900 }
16901
16902 // If we don't already have a node, then it's fine and we can just
16903 // set the result to be the node we just parsed.
16904 current = node;
16905 } else {
16906 // Otherwise we need to check the type of the node we just parsed.
16907 // If it cannot be concatenated with the previous node, then we'll
16908 // need to add a syntax error.
16909 if (!PM_NODE_TYPE_P(node, PM_STRING_NODE) && !PM_NODE_TYPE_P(node, PM_INTERPOLATED_STRING_NODE)) {
16910 pm_parser_err_node(parser, node, PM_ERR_STRING_CONCATENATION);
16911 }
16912
16913 // If we haven't already created our container for concatenation,
16914 // we'll do that now.
16915 if (!concating) {
16916 if (!PM_NODE_TYPE_P(current, PM_STRING_NODE) && !PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
16917 pm_parser_err_node(parser, current, PM_ERR_STRING_CONCATENATION);
16918 }
16919
16920 concating = true;
16921 pm_interpolated_string_node_t *container = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
16922 pm_interpolated_string_node_append(parser, container, current);
16923 current = UP(container);
16924 }
16925
16926 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, node);
16927 }
16928 }
16929
16930 return current;
16931}
16932
16933#define PM_PARSE_PATTERN_SINGLE 0
16934#define PM_PARSE_PATTERN_TOP 1
16935#define PM_PARSE_PATTERN_MULTI 2
16936
16937static pm_node_t *
16938parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth);
16939
16945static void
16946parse_pattern_capture(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_constant_id_t capture, const pm_location_t *location) {
16947 // Skip this capture if it starts with an underscore.
16948 if (peek_at(parser, parser->start + location->start) == '_') return;
16949
16950 if (pm_constant_id_list_includes(captures, capture)) {
16951 pm_parser_err(parser, location->start, location->length, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
16952 } else {
16953 pm_constant_id_list_append(parser->arena, captures, capture);
16954 }
16955}
16956
16960static pm_node_t *
16961parse_pattern_constant_path(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *node, uint16_t depth) {
16962 // Now, if there are any :: operators that follow, parse them as constant
16963 // path nodes.
16964 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
16965 pm_token_t delimiter = parser->previous;
16966 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
16967 node = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
16968 }
16969
16970 // If there is a [ or ( that follows, then this is part of a larger pattern
16971 // expression. We'll parse the inner pattern here, then modify the returned
16972 // inner pattern with our constant path attached.
16973 if (!match2(parser, PM_TOKEN_BRACKET_LEFT, PM_TOKEN_PARENTHESIS_LEFT)) {
16974 return node;
16975 }
16976
16977 pm_token_t opening;
16978 pm_token_t closing;
16979 pm_node_t *inner = NULL;
16980
16981 if (accept1(parser, PM_TOKEN_BRACKET_LEFT)) {
16982 opening = parser->previous;
16983 accept1(parser, PM_TOKEN_NEWLINE);
16984
16985 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
16986 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
16987 accept1(parser, PM_TOKEN_NEWLINE);
16988 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
16989 }
16990
16991 closing = parser->previous;
16992 } else {
16993 parser_lex(parser);
16994 opening = parser->previous;
16995 accept1(parser, PM_TOKEN_NEWLINE);
16996
16997 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
16998 inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
16999 accept1(parser, PM_TOKEN_NEWLINE);
17000 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17001 }
17002
17003 closing = parser->previous;
17004 }
17005
17006 if (!inner) {
17007 // If there was no inner pattern, then we have something like Foo() or
17008 // Foo[]. In that case we'll create an array pattern with no requireds.
17009 return UP(pm_array_pattern_node_constant_create(parser, node, &opening, &closing));
17010 }
17011
17012 // Now that we have the inner pattern, check to see if it's an array, find,
17013 // or hash pattern. If it is, then we'll attach our constant path to it if
17014 // it doesn't already have a constant. If it's not one of those node types
17015 // or it does have a constant, then we'll create an array pattern.
17016 switch (PM_NODE_TYPE(inner)) {
17017 case PM_ARRAY_PATTERN_NODE: {
17018 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17019
17020 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17021 PM_NODE_START_SET_NODE(pattern_node, node);
17022 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17023
17024 pattern_node->constant = node;
17025 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17026 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17027
17028 return UP(pattern_node);
17029 }
17030
17031 break;
17032 }
17033 case PM_FIND_PATTERN_NODE: {
17034 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17035
17036 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17037 PM_NODE_START_SET_NODE(pattern_node, node);
17038 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17039
17040 pattern_node->constant = node;
17041 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17042 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17043
17044 return UP(pattern_node);
17045 }
17046
17047 break;
17048 }
17049 case PM_HASH_PATTERN_NODE: {
17050 pm_hash_pattern_node_t *pattern_node = (pm_hash_pattern_node_t *) inner;
17051
17052 if (pattern_node->constant == NULL && pattern_node->opening_loc.length == 0) {
17053 PM_NODE_START_SET_NODE(pattern_node, node);
17054 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17055
17056 pattern_node->constant = node;
17057 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17058 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17059
17060 return UP(pattern_node);
17061 }
17062
17063 break;
17064 }
17065 default:
17066 break;
17067 }
17068
17069 // If we got here, then we didn't return one of the inner patterns by
17070 // attaching its constant. In this case we'll create an array pattern and
17071 // attach our constant to it.
17072 pm_array_pattern_node_t *pattern_node = pm_array_pattern_node_constant_create(parser, node, &opening, &closing);
17073 pm_array_pattern_node_requireds_append(parser->arena, pattern_node, inner);
17074 return UP(pattern_node);
17075}
17076
17080static pm_splat_node_t *
17081parse_pattern_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
17082 assert(parser->previous.type == PM_TOKEN_USTAR);
17083 pm_token_t operator = parser->previous;
17084 pm_node_t *name = NULL;
17085
17086 // Rest patterns don't necessarily have a name associated with them. So we
17087 // will check for that here. If they do, then we'll add it to the local
17088 // table since this pattern will cause it to become a local variable.
17089 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17090 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17091
17092 int depth;
17093 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17094 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17095 }
17096
17097 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17098 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17099 name = UP(pm_local_variable_target_node_create(
17100 parser,
17101 &previous_loc,
17102 constant_id,
17103 (uint32_t) (depth == -1 ? 0 : depth)
17104 ));
17105 }
17106
17107 // Finally we can return the created node.
17108 return pm_splat_node_create(parser, &operator, name);
17109}
17110
17114static pm_node_t *
17115parse_pattern_keyword_rest(pm_parser_t *parser, pm_constant_id_list_t *captures) {
17116 assert(parser->current.type == PM_TOKEN_USTAR_STAR);
17117 parser_lex(parser);
17118
17119 pm_token_t operator = parser->previous;
17120 pm_node_t *value = NULL;
17121
17122 if (accept1(parser, PM_TOKEN_KEYWORD_NIL)) {
17123 return UP(pm_no_keywords_parameter_node_create(parser, &operator, &parser->previous));
17124 }
17125
17126 if (accept1(parser, PM_TOKEN_IDENTIFIER)) {
17127 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17128
17129 int depth;
17130 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17131 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17132 }
17133
17134 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17135 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17136 value = UP(pm_local_variable_target_node_create(
17137 parser,
17138 &previous_loc,
17139 constant_id,
17140 (uint32_t) (depth == -1 ? 0 : depth)
17141 ));
17142 }
17143
17144 return UP(pm_assoc_splat_node_create(parser, value, &operator));
17145}
17146
17151static bool
17152pm_slice_is_valid_local(const pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
17153 ptrdiff_t length = end - start;
17154 if (length == 0) return false;
17155
17156 // First ensure that it starts with a valid identifier starting character.
17157 size_t width = char_is_identifier_start(parser, start, end - start);
17158 if (width == 0) return false;
17159
17160 // Next, ensure that it's not an uppercase character.
17161 if (parser->encoding_changed) {
17162 if (parser->encoding->isupper_char(start, length)) return false;
17163 } else {
17164 if (pm_encoding_utf_8_isupper_char(start, length)) return false;
17165 }
17166
17167 // Next, iterate through all of the bytes of the string to ensure that they
17168 // are all valid identifier characters.
17169 const uint8_t *cursor = start + width;
17170 while ((width = char_is_identifier(parser, cursor, end - cursor))) cursor += width;
17171 return cursor == end;
17172}
17173
17178static pm_node_t *
17179parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_symbol_node_t *key) {
17180 const pm_location_t *value_loc = &((pm_symbol_node_t *) key)->value_loc;
17181 const uint8_t *start = parser->start + PM_LOCATION_START(value_loc);
17182 const uint8_t *end = parser->start + PM_LOCATION_END(value_loc);
17183
17184 pm_constant_id_t constant_id = pm_parser_constant_id_raw(parser, start, end);
17185 int depth = -1;
17186
17187 if (pm_slice_is_valid_local(parser, start, end)) {
17188 depth = pm_parser_local_depth_constant_id(parser, constant_id);
17189 } else {
17190 pm_parser_err(parser, PM_NODE_START(key), PM_NODE_LENGTH(key), PM_ERR_PATTERN_HASH_KEY_LOCALS);
17191
17192 if ((end > start) && ((end[-1] == '!') || (end[-1] == '?'))) {
17193 PM_PARSER_ERR_FORMAT(parser, value_loc->start, value_loc->length, PM_ERR_INVALID_LOCAL_VARIABLE_WRITE, (int) (end - start), (const char *) start);
17194 }
17195 }
17196
17197 if (depth == -1) {
17198 pm_parser_local_add(parser, constant_id, start, end, 0);
17199 }
17200
17201 parse_pattern_capture(parser, captures, constant_id, value_loc);
17202 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17203 parser,
17204 value_loc,
17205 constant_id,
17206 (uint32_t) (depth == -1 ? 0 : depth)
17207 );
17208
17209 return UP(pm_implicit_node_create(parser, UP(target)));
17210}
17211
17216static void
17217parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) {
17218 if (pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, keys, node, true) != NULL) {
17219 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE);
17220 }
17221}
17222
17227parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node_t *first_node, uint16_t depth) {
17228 pm_node_list_t assocs = { 0 };
17229 pm_static_literals_t keys = { 0 };
17230 pm_node_t *rest = NULL;
17231
17232 switch (PM_NODE_TYPE(first_node)) {
17233 case PM_ASSOC_SPLAT_NODE:
17234 case PM_NO_KEYWORDS_PARAMETER_NODE:
17235 rest = first_node;
17236 break;
17237 case PM_INTERPOLATED_SYMBOL_NODE:
17238 case PM_SYMBOL_NODE: {
17239 if (pm_symbol_node_label_p(parser, first_node)) {
17240 if (PM_NODE_TYPE_P(first_node, PM_INTERPOLATED_SYMBOL_NODE)) {
17241 pm_parser_err_node(parser, first_node, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17242 } else {
17243 parse_pattern_hash_key(parser, &keys, first_node);
17244 }
17245
17246 pm_node_t *value;
17247
17248 /*
17249 * The label has an implicit value when the next token cannot
17250 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17251 * reduction.
17252 */
17253 if (!token_begins_pattern_p(parser->current.type)) {
17254 if (PM_NODE_TYPE_P(first_node, PM_SYMBOL_NODE)) {
17255 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) first_node);
17256 } else {
17257 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(first_node), 0));
17258 }
17259 } else {
17260 // Here we have a value for the first assoc in the list, so
17261 // we will parse it now.
17262 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17263 }
17264
17265 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17266 pm_node_list_append(parser->arena, &assocs, assoc);
17267 break;
17268 }
17269 }
17271 default: {
17272 // If we get anything else, then this is an error. For this we'll
17273 // create a missing node for the value and create an assoc node for
17274 // the first node in the list.
17275 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;
17276 pm_parser_err_node(parser, first_node, diag_id);
17277
17278 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_START(first_node), PM_NODE_LENGTH(first_node)));
17279 pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17280
17281 pm_node_list_append(parser->arena, &assocs, assoc);
17282 break;
17283 }
17284 }
17285
17286 // If there are any other assocs, then we'll parse them now.
17287 while (accept1(parser, PM_TOKEN_COMMA)) {
17288 /*
17289 * A trailing comma ends the pattern when the next token cannot begin
17290 * another element, mirroring the grammar's `p_kwargs: p_kwarg ','`
17291 * reduction.
17292 */
17293 if (!token_begins_pattern_p(parser->current.type)) {
17294 // Trailing commas are not allowed to follow a rest pattern.
17295 if (rest != NULL) {
17296 pm_parser_err_token(parser, &parser->current, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17297 }
17298
17299 break;
17300 }
17301
17302 if (match1(parser, PM_TOKEN_USTAR_STAR)) {
17303 pm_node_t *assoc = parse_pattern_keyword_rest(parser, captures);
17304
17305 if (rest == NULL) {
17306 rest = assoc;
17307 } else {
17308 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17309 pm_node_list_append(parser->arena, &assocs, assoc);
17310 }
17311 } else {
17312 pm_node_t *key;
17313
17314 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
17315 key = parse_strings(parser, NULL, true, (uint16_t) (depth + 1));
17316
17317 if (PM_NODE_TYPE_P(key, PM_INTERPOLATED_SYMBOL_NODE)) {
17318 pm_parser_err_node(parser, key, PM_ERR_PATTERN_HASH_KEY_INTERPOLATED);
17319 } else if (!pm_symbol_node_label_p(parser, key)) {
17320 pm_parser_err_node(parser, key, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17321 }
17322 } else if (accept1(parser, PM_TOKEN_LABEL)) {
17323 key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17324 } else {
17325 expect1(parser, PM_TOKEN_LABEL, PM_ERR_PATTERN_LABEL_AFTER_COMMA);
17326
17327 pm_token_t label = { .type = PM_TOKEN_LABEL, .start = parser->previous.end, .end = parser->previous.end };
17328 key = UP(pm_symbol_node_create(parser, NULL, &label, NULL));
17329 }
17330
17331 parse_pattern_hash_key(parser, &keys, key);
17332 pm_node_t *value = NULL;
17333
17334 /*
17335 * The label has an implicit value when the next token cannot
17336 * begin a pattern, mirroring the grammar's `p_kw: p_kw_label`
17337 * reduction.
17338 */
17339 if (!token_begins_pattern_p(parser->current.type)) {
17340 if (PM_NODE_TYPE_P(key, PM_SYMBOL_NODE)) {
17341 value = parse_pattern_hash_implicit_value(parser, captures, (pm_symbol_node_t *) key);
17342 } else {
17343 value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(key), 0));
17344 }
17345 } else {
17346 value = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_KEY, (uint16_t) (depth + 1));
17347 }
17348
17349 pm_node_t *assoc = UP(pm_assoc_node_create(parser, key, NULL, value));
17350
17351 if (rest != NULL) {
17352 pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17353 }
17354
17355 pm_node_list_append(parser->arena, &assocs, assoc);
17356 }
17357 }
17358
17359 pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest);
17360 // assocs.nodes is arena-allocated; no explicit free needed.
17361
17362 pm_static_literals_free(&keys);
17363 return node;
17364}
17365
17369static pm_node_t *
17370parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_diagnostic_id_t diag_id, uint16_t depth) {
17371 switch (parser->current.type) {
17372 case PM_TOKEN_IDENTIFIER:
17373 case PM_TOKEN_METHOD_NAME: {
17374 parser_lex(parser);
17375 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17376
17377 int depth;
17378 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17379 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17380 }
17381
17382 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17383 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17384 return UP(pm_local_variable_target_node_create(
17385 parser,
17386 &previous_loc,
17387 constant_id,
17388 (uint32_t) (depth == -1 ? 0 : depth)
17389 ));
17390 }
17391 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
17392 pm_token_t opening = parser->current;
17393 parser_lex(parser);
17394
17395 if (accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
17396 // If we have an empty array pattern, then we'll just return a new
17397 // array pattern node.
17398 return UP(pm_array_pattern_node_empty_create(parser, &opening, &parser->previous));
17399 }
17400
17401 // Otherwise, we'll parse the inner pattern, then deal with it depending
17402 // on the type it returns.
17403 pm_node_t *inner = parse_pattern(parser, captures, PM_PARSE_PATTERN_MULTI, PM_ERR_PATTERN_EXPRESSION_AFTER_BRACKET, (uint16_t) (depth + 1));
17404
17405 accept1(parser, PM_TOKEN_NEWLINE);
17406 expect1_opening(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_PATTERN_TERM_BRACKET, &opening);
17407 pm_token_t closing = parser->previous;
17408
17409 switch (PM_NODE_TYPE(inner)) {
17410 case PM_ARRAY_PATTERN_NODE: {
17411 pm_array_pattern_node_t *pattern_node = (pm_array_pattern_node_t *) inner;
17412 if (pattern_node->opening_loc.length == 0) {
17413 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17414 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17415
17416 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17417 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17418
17419 return UP(pattern_node);
17420 }
17421
17422 break;
17423 }
17424 case PM_FIND_PATTERN_NODE: {
17425 pm_find_pattern_node_t *pattern_node = (pm_find_pattern_node_t *) inner;
17426 if (pattern_node->opening_loc.length == 0) {
17427 PM_NODE_START_SET_TOKEN(parser, pattern_node, &opening);
17428 PM_NODE_LENGTH_SET_TOKEN(parser, pattern_node, &closing);
17429
17430 pattern_node->opening_loc = TOK2LOC(parser, &opening);
17431 pattern_node->closing_loc = TOK2LOC(parser, &closing);
17432
17433 return UP(pattern_node);
17434 }
17435
17436 break;
17437 }
17438 default:
17439 break;
17440 }
17441
17442 pm_array_pattern_node_t *node = pm_array_pattern_node_empty_create(parser, &opening, &closing);
17443 pm_array_pattern_node_requireds_append(parser->arena, node, inner);
17444 return UP(node);
17445 }
17446 case PM_TOKEN_BRACE_LEFT_HASH: {
17447 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17448 parser->pattern_matching_newlines = false;
17449
17451 pm_token_t opening = parser->current;
17452 parser_lex(parser);
17453
17454 if (accept1(parser, PM_TOKEN_BRACE_RIGHT)) {
17455 // If we have an empty hash pattern, then we'll just return a new hash
17456 // pattern node.
17457 node = pm_hash_pattern_node_empty_create(parser, &opening, &parser->previous);
17458 } else {
17459 pm_node_t *first_node;
17460
17461 switch (parser->current.type) {
17462 case PM_TOKEN_LABEL:
17463 parser_lex(parser);
17464 first_node = UP(pm_symbol_node_label_create(parser, &parser->previous));
17465 break;
17466 case PM_TOKEN_USTAR_STAR:
17467 first_node = parse_pattern_keyword_rest(parser, captures);
17468 break;
17469 case PM_TOKEN_STRING_BEGIN:
17470 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));
17471 break;
17472 default: {
17473 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_PATTERN_HASH_KEY, pm_token_str(parser->current.type));
17474 parser_lex(parser);
17475
17476 first_node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
17477 break;
17478 }
17479 }
17480
17481 node = parse_pattern_hash(parser, captures, first_node, (uint16_t) (depth + 1));
17482
17483 accept1(parser, PM_TOKEN_NEWLINE);
17484 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_PATTERN_TERM_BRACE, &opening);
17485 pm_token_t closing = parser->previous;
17486
17487 PM_NODE_START_SET_TOKEN(parser, node, &opening);
17488 PM_NODE_LENGTH_SET_TOKEN(parser, node, &closing);
17489
17490 node->opening_loc = TOK2LOC(parser, &opening);
17491 node->closing_loc = TOK2LOC(parser, &closing);
17492 }
17493
17494 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17495 return UP(node);
17496 }
17497 case PM_TOKEN_UDOT_DOT:
17498 case PM_TOKEN_UDOT_DOT_DOT: {
17499 pm_token_t operator = parser->current;
17500 parser_lex(parser);
17501
17502 // Since we have a unary range operator, we need to parse the subsequent
17503 // expression as the right side of the range.
17504 switch (parser->current.type) {
17505 case PM_CASE_PRIMITIVE: {
17506 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));
17507 return UP(pm_range_node_create(parser, NULL, &operator, right));
17508 }
17509 default: {
17510 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_RANGE);
17511 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17512 return UP(pm_range_node_create(parser, NULL, &operator, right));
17513 }
17514 }
17515 }
17516 case PM_CASE_PRIMITIVE: {
17517 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));
17518
17519 // If we found a label, we need to immediately return to the caller.
17520 if (pm_symbol_node_label_p(parser, node)) return node;
17521
17522 // Call nodes (arithmetic operations) are not allowed in patterns
17523 if (PM_NODE_TYPE(node) == PM_CALL_NODE) {
17524 pm_parser_err_node(parser, node, diag_id);
17525 return UP(pm_error_recovery_node_create_unexpected(parser, node));
17526 }
17527
17528 // Now that we have a primitive, we need to check if it's part of a range.
17529 if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
17530 pm_token_t operator = parser->previous;
17531
17532 // Now that we have the operator, we need to check if this is followed
17533 // by another expression. If it is, then we will create a full range
17534 // node. Otherwise, we'll create an endless range.
17535 switch (parser->current.type) {
17536 case PM_CASE_PRIMITIVE: {
17537 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));
17538 return UP(pm_range_node_create(parser, node, &operator, right));
17539 }
17540 default:
17541 return UP(pm_range_node_create(parser, node, &operator, NULL));
17542 }
17543 }
17544
17545 return node;
17546 }
17547 case PM_TOKEN_CARET: {
17548 parser_lex(parser);
17549 pm_token_t operator = parser->previous;
17550
17551 // At this point we have a pin operator. We need to check the subsequent
17552 // expression to determine if it's a variable or an expression.
17553 switch (parser->current.type) {
17554 case PM_TOKEN_IDENTIFIER: {
17555 parser_lex(parser);
17556 pm_node_t *variable = UP(parse_variable(parser));
17557
17558 if (variable == NULL) {
17559 PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->previous, PM_ERR_NO_LOCAL_VARIABLE);
17560 variable = UP(pm_local_variable_read_node_missing_create(parser, &parser->previous, 0));
17561 }
17562
17563 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17564 }
17565 case PM_TOKEN_INSTANCE_VARIABLE: {
17566 parser_lex(parser);
17567 pm_node_t *variable = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
17568
17569 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17570 }
17571 case PM_TOKEN_CLASS_VARIABLE: {
17572 parser_lex(parser);
17573 pm_node_t *variable = UP(pm_class_variable_read_node_create(parser, &parser->previous));
17574
17575 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17576 }
17577 case PM_TOKEN_GLOBAL_VARIABLE: {
17578 parser_lex(parser);
17579 pm_node_t *variable = UP(pm_global_variable_read_node_create(parser, &parser->previous));
17580
17581 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17582 }
17583 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING: {
17584 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
17585 parser->pattern_matching_newlines = false;
17586
17587 pm_token_t lparen = parser->current;
17588 parser_lex(parser);
17589
17590 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));
17591 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
17592
17593 accept1(parser, PM_TOKEN_NEWLINE);
17594 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &lparen);
17595 return UP(pm_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous));
17596 }
17597 default: {
17598 // If we get here, then we have a pin operator followed by something
17599 // not understood. We'll create a missing node and return that.
17600 pm_parser_err_token(parser, &operator, PM_ERR_PATTERN_EXPRESSION_AFTER_PIN);
17601 pm_node_t *variable = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &operator), PM_TOKEN_LENGTH(&operator)));
17602 return UP(pm_pinned_variable_node_create(parser, &operator, variable));
17603 }
17604 }
17605 }
17606 case PM_TOKEN_UCOLON_COLON: {
17607 pm_token_t delimiter = parser->current;
17608 parser_lex(parser);
17609
17610 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
17611 pm_constant_path_node_t *node = pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous);
17612
17613 return parse_pattern_constant_path(parser, captures, UP(node), (uint16_t) (depth + 1));
17614 }
17615 case PM_TOKEN_CONSTANT: {
17616 pm_token_t constant = parser->current;
17617 parser_lex(parser);
17618
17619 pm_node_t *node = UP(pm_constant_read_node_create(parser, &constant));
17620 return parse_pattern_constant_path(parser, captures, node, (uint16_t) (depth + 1));
17621 }
17622 default:
17623 pm_parser_err_current(parser, diag_id);
17624 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17625 }
17626}
17627
17628static bool
17629parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
17630 switch (PM_NODE_TYPE(node)) {
17631 case PM_LOCAL_VARIABLE_TARGET_NODE: {
17632 pm_parser_t *parser = (pm_parser_t *) data;
17633 pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
17634 return false;
17635 }
17636 default:
17637 return true;
17638 }
17639}
17640
17645static void
17646parse_pattern_alternation_error(pm_parser_t *parser, const pm_node_t *node) {
17647 pm_visit_node(node, parse_pattern_alternation_error_each, parser);
17648}
17649
17654static pm_node_t *
17655parse_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) {
17656 pm_node_t *node = first_node;
17657 bool alternation = false;
17658
17659 while ((node == NULL) || (alternation = accept1(parser, PM_TOKEN_PIPE))) {
17660 if (alternation && !PM_NODE_TYPE_P(node, PM_ALTERNATION_PATTERN_NODE) && captures->size) {
17661 parse_pattern_alternation_error(parser, node);
17662 }
17663
17664 switch (parser->current.type) {
17665 case PM_TOKEN_IDENTIFIER:
17666 case PM_TOKEN_BRACKET_LEFT_ARRAY:
17667 case PM_TOKEN_BRACE_LEFT_HASH:
17668 case PM_TOKEN_CARET:
17669 case PM_TOKEN_CONSTANT:
17670 case PM_TOKEN_UCOLON_COLON:
17671 case PM_TOKEN_UDOT_DOT:
17672 case PM_TOKEN_UDOT_DOT_DOT:
17673 case PM_CASE_PRIMITIVE: {
17674 if (!alternation) {
17675 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17676 } else {
17677 pm_token_t operator = parser->previous;
17678 pm_node_t *right = parse_pattern_primitive(parser, captures, PM_ERR_PATTERN_EXPRESSION_AFTER_PIPE, (uint16_t) (depth + 1));
17679
17680 if (captures->size) parse_pattern_alternation_error(parser, right);
17681 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17682 }
17683
17684 break;
17685 }
17686 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
17687 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
17688 pm_token_t operator = parser->previous;
17689 pm_token_t opening = parser->current;
17690 parser_lex(parser);
17691
17692 pm_node_t *body = parse_pattern(parser, captures, PM_PARSE_PATTERN_SINGLE, PM_ERR_PATTERN_EXPRESSION_AFTER_PAREN, (uint16_t) (depth + 1));
17693 accept1(parser, PM_TOKEN_NEWLINE);
17694 expect1_opening(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_PATTERN_TERM_PAREN, &opening);
17695 pm_node_t *right = UP(pm_parentheses_node_create(parser, &opening, body, &parser->previous, 0));
17696
17697 if (!alternation) {
17698 node = right;
17699 } else {
17700 if (captures->size) parse_pattern_alternation_error(parser, right);
17701 node = UP(pm_alternation_pattern_node_create(parser, node, right, &operator));
17702 }
17703
17704 break;
17705 }
17706 default: {
17707 pm_parser_err_current(parser, diag_id);
17708 pm_node_t *right = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
17709
17710 if (!alternation) {
17711 node = right;
17712 } else {
17713 if (captures->size) parse_pattern_alternation_error(parser, right);
17714 node = UP(pm_alternation_pattern_node_create(parser, node, right, &parser->previous));
17715 }
17716
17717 break;
17718 }
17719 }
17720 }
17721
17722 // If we have an =>, then we are assigning this pattern to a variable.
17723 // In this case we should create an assignment node.
17724 while (accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
17725 pm_token_t operator = parser->previous;
17726 expect1(parser, PM_TOKEN_IDENTIFIER, PM_ERR_PATTERN_IDENT_AFTER_HROCKET);
17727
17728 pm_constant_id_t constant_id = pm_parser_constant_id_token(parser, &parser->previous);
17729 int depth;
17730
17731 if ((depth = pm_parser_local_depth_constant_id(parser, constant_id)) == -1) {
17732 pm_parser_local_add(parser, constant_id, parser->previous.start, parser->previous.end, 0);
17733 }
17734
17735 pm_location_t previous_loc = TOK2LOC(parser, &parser->previous);
17736 parse_pattern_capture(parser, captures, constant_id, &previous_loc);
17737 pm_local_variable_target_node_t *target = pm_local_variable_target_node_create(
17738 parser,
17739 &previous_loc,
17740 constant_id,
17741 (uint32_t) (depth == -1 ? 0 : depth)
17742 );
17743
17744 node = UP(pm_capture_pattern_node_create(parser, node, target, &operator));
17745 }
17746
17747 return node;
17748}
17749
17753static pm_node_t *
17754parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
17755 pm_node_t *node = NULL;
17756
17757 bool leading_rest = false;
17758 bool trailing_rest = false;
17759
17760 switch (parser->current.type) {
17761 case PM_TOKEN_LABEL: {
17762 parser_lex(parser);
17763 pm_node_t *key = UP(pm_symbol_node_label_create(parser, &parser->previous));
17764 node = UP(parse_pattern_hash(parser, captures, key, (uint16_t) (depth + 1)));
17765
17766 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17767 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17768 }
17769
17770 return node;
17771 }
17772 case PM_TOKEN_USTAR_STAR: {
17773 node = parse_pattern_keyword_rest(parser, captures);
17774 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17775
17776 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17777 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17778 }
17779
17780 return node;
17781 }
17782 case PM_TOKEN_STRING_BEGIN: {
17783 // We need special handling for string beginnings because they could
17784 // be dynamic symbols leading to hash patterns.
17785 node = parse_pattern_primitive(parser, captures, diag_id, (uint16_t) (depth + 1));
17786
17787 if (pm_symbol_node_label_p(parser, node)) {
17788 node = UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17789
17790 if (!(flags & PM_PARSE_PATTERN_TOP)) {
17791 pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_IMPLICIT);
17792 }
17793
17794 return node;
17795 }
17796
17797 node = parse_pattern_primitives(parser, captures, node, diag_id, (uint16_t) (depth + 1));
17798 break;
17799 }
17800 case PM_TOKEN_USTAR: {
17801 if (flags & (PM_PARSE_PATTERN_TOP | PM_PARSE_PATTERN_MULTI)) {
17802 parser_lex(parser);
17803 node = UP(parse_pattern_rest(parser, captures));
17804 leading_rest = true;
17805 break;
17806 }
17807 }
17809 default:
17810 node = parse_pattern_primitives(parser, captures, NULL, diag_id, (uint16_t) (depth + 1));
17811 break;
17812 }
17813
17814 // If we got a dynamic label symbol, then we need to treat it like the
17815 // beginning of a hash pattern.
17816 if (pm_symbol_node_label_p(parser, node)) {
17817 return UP(parse_pattern_hash(parser, captures, node, (uint16_t) (depth + 1)));
17818 }
17819
17820 if ((flags & PM_PARSE_PATTERN_MULTI) && match1(parser, PM_TOKEN_COMMA)) {
17821 // If we have a comma, then we are now parsing either an array pattern
17822 // or a find pattern. We need to parse all of the patterns, put them
17823 // into a big list, and then determine which type of node we have.
17824 pm_node_list_t nodes = { 0 };
17825 pm_node_list_append(parser->arena, &nodes, node);
17826
17827 // Gather up all of the patterns into the list.
17828 while (accept1(parser, PM_TOKEN_COMMA)) {
17829 /*
17830 * A trailing comma ends the pattern when the next token cannot
17831 * begin another pattern element, leaving the token for the
17832 * enclosing context to accept or reject.
17833 */
17834 if (!token_begins_pattern_p(parser->current.type)) {
17835 // A trailing comma forms an implicit rest pattern (`[a,]` is
17836 // `[a, *]`). If a rest pattern has already been parsed, then
17837 // this is a second rest, which is not allowed (e.g. `[a, *b,]`
17838 // or `x => a, *b,`).
17839 if (trailing_rest) {
17840 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
17841 }
17842
17843 node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
17844 pm_node_list_append(parser->arena, &nodes, node);
17845 trailing_rest = true;
17846 break;
17847 }
17848
17849 if (accept1(parser, PM_TOKEN_USTAR)) {
17850 node = UP(parse_pattern_rest(parser, captures));
17851
17852 // If we have already parsed a splat pattern, then this is an
17853 // error. We will continue to parse the rest of the patterns,
17854 // but we will indicate it as an error.
17855 if (trailing_rest) {
17856 pm_parser_err_previous(parser, PM_ERR_PATTERN_REST);
17857 }
17858
17859 trailing_rest = true;
17860 } else {
17861 node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
17862 }
17863
17864 pm_node_list_append(parser->arena, &nodes, node);
17865 }
17866
17867 // If the first pattern and the last pattern are rest patterns, then we
17868 // will call this a find pattern, regardless of how many rest patterns
17869 // are in between because we know we already added the appropriate
17870 // errors. Otherwise we will create an array pattern.
17871 if (leading_rest && PM_NODE_TYPE_P(nodes.nodes[nodes.size - 1], PM_SPLAT_NODE)) {
17872 node = UP(pm_find_pattern_node_create(parser, &nodes));
17873
17874 if (nodes.size == 2) {
17875 pm_parser_err_node(parser, node, PM_ERR_PATTERN_FIND_MISSING_INNER);
17876 }
17877 } else {
17878 node = UP(pm_array_pattern_node_node_list_create(parser, &nodes));
17879
17880 if (leading_rest && trailing_rest) {
17881 pm_parser_err_node(parser, node, PM_ERR_PATTERN_ARRAY_MULTIPLE_RESTS);
17882 }
17883 }
17884
17885 // nodes.nodes is arena-allocated; no explicit free needed.
17886 } else if (leading_rest) {
17887 // Otherwise, if we parsed a single splat pattern, then we know we have
17888 // an array pattern, so we can go ahead and create that node.
17889 node = UP(pm_array_pattern_node_rest_create(parser, node));
17890 }
17891
17892 return node;
17893}
17894
17900static PRISM_INLINE void
17901parse_negative_numeric(pm_node_t *node) {
17902 switch (PM_NODE_TYPE(node)) {
17903 case PM_INTEGER_NODE: {
17904 pm_integer_node_t *cast = (pm_integer_node_t *) node;
17905 cast->base.location.start--;
17906 cast->base.location.length++;
17907 cast->value.negative = true;
17908 break;
17909 }
17910 case PM_FLOAT_NODE: {
17911 pm_float_node_t *cast = (pm_float_node_t *) node;
17912 cast->base.location.start--;
17913 cast->base.location.length++;
17914 cast->value = -cast->value;
17915 break;
17916 }
17917 case PM_RATIONAL_NODE: {
17918 pm_rational_node_t *cast = (pm_rational_node_t *) node;
17919 cast->base.location.start--;
17920 cast->base.location.length++;
17921 cast->numerator.negative = true;
17922 break;
17923 }
17924 case PM_IMAGINARY_NODE:
17925 node->location.start--;
17926 node->location.length++;
17927 parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric);
17928 break;
17929 default:
17930 assert(false && "unreachable");
17931 break;
17932 }
17933}
17934
17940static void
17941pm_parser_err_prefix(pm_parser_t *parser, pm_diagnostic_id_t diag_id) {
17942 switch (diag_id) {
17943 case PM_ERR_HASH_KEY: {
17944 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, pm_token_str(parser->previous.type));
17945 break;
17946 }
17947 case PM_ERR_HASH_VALUE:
17948 case PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR: {
17949 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
17950 break;
17951 }
17952 case PM_ERR_UNARY_RECEIVER: {
17953 const char *human = (parser->current.type == PM_TOKEN_EOF ? "end-of-input" : pm_token_str(parser->current.type));
17954 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, diag_id, human, parser->previous.start[0]);
17955 break;
17956 }
17957 case PM_ERR_UNARY_DISALLOWED:
17958 case PM_ERR_EXPECT_ARGUMENT: {
17959 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, diag_id, pm_token_str(parser->current.type));
17960 break;
17961 }
17962 default:
17963 pm_parser_err_previous(parser, diag_id);
17964 break;
17965 }
17966}
17967
17971static void
17972parse_retry(pm_parser_t *parser, const pm_node_t *node) {
17973#define CONTEXT_NONE 0
17974#define CONTEXT_THROUGH_ENSURE 1
17975#define CONTEXT_THROUGH_ELSE 2
17976
17977 pm_context_node_t *context_node = parser->current_context;
17978 int context = CONTEXT_NONE;
17979
17980 while (context_node != NULL) {
17981 switch (context_node->context) {
17982 case PM_CONTEXT_BEGIN_RESCUE:
17983 case PM_CONTEXT_BLOCK_RESCUE:
17984 case PM_CONTEXT_CLASS_RESCUE:
17985 case PM_CONTEXT_DEF_RESCUE:
17986 case PM_CONTEXT_LAMBDA_RESCUE:
17987 case PM_CONTEXT_MODULE_RESCUE:
17988 case PM_CONTEXT_SCLASS_RESCUE:
17989 case PM_CONTEXT_DEFINED:
17990 case PM_CONTEXT_RESCUE_MODIFIER:
17991 // These are the good cases. We're allowed to have a retry here.
17992 return;
17993 case PM_CONTEXT_CLASS:
17994 case PM_CONTEXT_DEF:
17995 case PM_CONTEXT_DEF_PARAMS:
17996 case PM_CONTEXT_MAIN:
17997 case PM_CONTEXT_MODULE:
17998 case PM_CONTEXT_PREEXE:
17999 case PM_CONTEXT_SCLASS:
18000 // These are the bad cases. We're not allowed to have a retry in
18001 // these contexts.
18002 if (context == CONTEXT_NONE) {
18003 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_WITHOUT_RESCUE);
18004 } else if (context == CONTEXT_THROUGH_ENSURE) {
18005 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ENSURE);
18006 } else if (context == CONTEXT_THROUGH_ELSE) {
18007 pm_parser_err_node(parser, node, PM_ERR_INVALID_RETRY_AFTER_ELSE);
18008 }
18009 return;
18010 case PM_CONTEXT_BEGIN_ELSE:
18011 case PM_CONTEXT_BLOCK_ELSE:
18012 case PM_CONTEXT_CLASS_ELSE:
18013 case PM_CONTEXT_DEF_ELSE:
18014 case PM_CONTEXT_LAMBDA_ELSE:
18015 case PM_CONTEXT_MODULE_ELSE:
18016 case PM_CONTEXT_SCLASS_ELSE:
18017 // These are also bad cases, but with a more specific error
18018 // message indicating the else.
18019 context = CONTEXT_THROUGH_ELSE;
18020 break;
18021 case PM_CONTEXT_BEGIN_ENSURE:
18022 case PM_CONTEXT_BLOCK_ENSURE:
18023 case PM_CONTEXT_CLASS_ENSURE:
18024 case PM_CONTEXT_DEF_ENSURE:
18025 case PM_CONTEXT_LAMBDA_ENSURE:
18026 case PM_CONTEXT_MODULE_ENSURE:
18027 case PM_CONTEXT_SCLASS_ENSURE:
18028 // These are also bad cases, but with a more specific error
18029 // message indicating the ensure.
18030 context = CONTEXT_THROUGH_ENSURE;
18031 break;
18032 case PM_CONTEXT_NONE:
18033 // This case should never happen.
18034 assert(false && "unreachable");
18035 break;
18036 case PM_CONTEXT_BEGIN:
18037 case PM_CONTEXT_BLOCK_BRACES:
18038 case PM_CONTEXT_BLOCK_KEYWORDS:
18039 case PM_CONTEXT_BLOCK_PARAMETERS:
18040 case PM_CONTEXT_CASE_IN:
18041 case PM_CONTEXT_CASE_WHEN:
18042 case PM_CONTEXT_DEFAULT_PARAMS:
18043 case PM_CONTEXT_ELSE:
18044 case PM_CONTEXT_ELSIF:
18045 case PM_CONTEXT_EMBEXPR:
18046 case PM_CONTEXT_FOR_INDEX:
18047 case PM_CONTEXT_FOR:
18048 case PM_CONTEXT_IF:
18049 case PM_CONTEXT_LAMBDA_BRACES:
18050 case PM_CONTEXT_LAMBDA_DO_END:
18051 case PM_CONTEXT_LOOP_PREDICATE:
18052 case PM_CONTEXT_MULTI_TARGET:
18053 case PM_CONTEXT_PARENS:
18054 case PM_CONTEXT_POSTEXE:
18055 case PM_CONTEXT_PREDICATE:
18056 case PM_CONTEXT_TERNARY:
18057 case PM_CONTEXT_UNLESS:
18058 case PM_CONTEXT_UNTIL:
18059 case PM_CONTEXT_WHILE:
18060 // In these contexts we should continue walking up the list of
18061 // contexts.
18062 break;
18063 }
18064
18065 context_node = context_node->prev;
18066 }
18067
18068#undef CONTEXT_NONE
18069#undef CONTEXT_ENSURE
18070#undef CONTEXT_ELSE
18071}
18072
18076static void
18077parse_yield(pm_parser_t *parser, const pm_node_t *node) {
18078 pm_context_node_t *context_node = parser->current_context;
18079
18080 while (context_node != NULL) {
18081 switch (context_node->context) {
18082 case PM_CONTEXT_DEF:
18083 case PM_CONTEXT_DEF_PARAMS:
18084 case PM_CONTEXT_DEFINED:
18085 case PM_CONTEXT_DEF_ENSURE:
18086 case PM_CONTEXT_DEF_RESCUE:
18087 case PM_CONTEXT_DEF_ELSE:
18088 // These are the good cases. We're allowed to have a block exit
18089 // in these contexts.
18090 return;
18091 case PM_CONTEXT_CLASS:
18092 case PM_CONTEXT_CLASS_ENSURE:
18093 case PM_CONTEXT_CLASS_RESCUE:
18094 case PM_CONTEXT_CLASS_ELSE:
18095 case PM_CONTEXT_MAIN:
18096 case PM_CONTEXT_MODULE:
18097 case PM_CONTEXT_MODULE_ENSURE:
18098 case PM_CONTEXT_MODULE_RESCUE:
18099 case PM_CONTEXT_MODULE_ELSE:
18100 case PM_CONTEXT_SCLASS:
18101 case PM_CONTEXT_SCLASS_RESCUE:
18102 case PM_CONTEXT_SCLASS_ENSURE:
18103 case PM_CONTEXT_SCLASS_ELSE:
18104 // These are the bad cases. We're not allowed to have a retry in
18105 // these contexts.
18106 pm_parser_err_node(parser, node, PM_ERR_INVALID_YIELD);
18107 return;
18108 case PM_CONTEXT_NONE:
18109 // This case should never happen.
18110 assert(false && "unreachable");
18111 break;
18112 case PM_CONTEXT_BEGIN:
18113 case PM_CONTEXT_BEGIN_ELSE:
18114 case PM_CONTEXT_BEGIN_ENSURE:
18115 case PM_CONTEXT_BEGIN_RESCUE:
18116 case PM_CONTEXT_BLOCK_BRACES:
18117 case PM_CONTEXT_BLOCK_KEYWORDS:
18118 case PM_CONTEXT_BLOCK_ELSE:
18119 case PM_CONTEXT_BLOCK_ENSURE:
18120 case PM_CONTEXT_BLOCK_PARAMETERS:
18121 case PM_CONTEXT_BLOCK_RESCUE:
18122 case PM_CONTEXT_CASE_IN:
18123 case PM_CONTEXT_CASE_WHEN:
18124 case PM_CONTEXT_DEFAULT_PARAMS:
18125 case PM_CONTEXT_ELSE:
18126 case PM_CONTEXT_ELSIF:
18127 case PM_CONTEXT_EMBEXPR:
18128 case PM_CONTEXT_FOR_INDEX:
18129 case PM_CONTEXT_FOR:
18130 case PM_CONTEXT_IF:
18131 case PM_CONTEXT_LAMBDA_BRACES:
18132 case PM_CONTEXT_LAMBDA_DO_END:
18133 case PM_CONTEXT_LAMBDA_ELSE:
18134 case PM_CONTEXT_LAMBDA_ENSURE:
18135 case PM_CONTEXT_LAMBDA_RESCUE:
18136 case PM_CONTEXT_LOOP_PREDICATE:
18137 case PM_CONTEXT_MULTI_TARGET:
18138 case PM_CONTEXT_PARENS:
18139 case PM_CONTEXT_POSTEXE:
18140 case PM_CONTEXT_PREDICATE:
18141 case PM_CONTEXT_PREEXE:
18142 case PM_CONTEXT_RESCUE_MODIFIER:
18143 case PM_CONTEXT_TERNARY:
18144 case PM_CONTEXT_UNLESS:
18145 case PM_CONTEXT_UNTIL:
18146 case PM_CONTEXT_WHILE:
18147 // In these contexts we should continue walking up the list of
18148 // contexts.
18149 break;
18150 }
18151
18152 context_node = context_node->prev;
18153 }
18154}
18155
18160static pm_node_t *
18161parse_case(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18162 size_t opening_newline_index = token_newline_index(parser);
18163 parser_lex(parser);
18164
18165 pm_token_t case_keyword = parser->previous;
18166 pm_node_t *predicate = NULL;
18167
18168 pm_node_list_t current_block_exits = { 0 };
18169 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18170
18171 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18172 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18173 predicate = NULL;
18174 } else if (match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_END)) {
18175 predicate = NULL;
18176 } else if (!token_begins_expression_p(parser->current.type)) {
18177 predicate = NULL;
18178 } else {
18179 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));
18180 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
18181 }
18182
18183 if (match1(parser, PM_TOKEN_KEYWORD_END)) {
18184 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18185 parser_lex(parser);
18186 pop_block_exits(parser, previous_block_exits);
18187 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18188 return UP(pm_case_node_create(parser, &case_keyword, predicate, &parser->previous));
18189 }
18190
18191 /* At this point we can create a case node, though we don't yet know if it
18192 * is a case-in or case-when node. */
18193 pm_node_t *node;
18194
18195 if (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18196 pm_case_node_t *case_node = pm_case_node_create(parser, &case_keyword, predicate, NULL);
18197 pm_static_literals_t literals = { 0 };
18198
18199 /* At this point we've seen a when keyword, so we know this is a
18200 * case-when node. We will continue to parse the when nodes until we hit
18201 * the end of the list. */
18202 while (match1(parser, PM_TOKEN_KEYWORD_WHEN)) {
18203 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18204 parser_lex(parser);
18205
18206 pm_token_t when_keyword = parser->previous;
18207 pm_when_node_t *when_node = pm_when_node_create(parser, &when_keyword);
18208
18209 do {
18210 if (accept1(parser, PM_TOKEN_USTAR)) {
18211 pm_token_t operator = parser->previous;
18212 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));
18213
18214 pm_splat_node_t *splat_node = pm_splat_node_create(parser, &operator, expression);
18215 pm_when_node_conditions_append(parser->arena, when_node, UP(splat_node));
18216
18217 if (PM_NODE_TYPE_P(expression, PM_ERROR_RECOVERY_NODE)) break;
18218 } else {
18219 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));
18220 pm_when_node_conditions_append(parser->arena, when_node, condition);
18221
18222 /* If we found a missing node, then this is a syntax error
18223 * and we should stop looping. */
18224 if (PM_NODE_TYPE_P(condition, PM_ERROR_RECOVERY_NODE)) break;
18225
18226 /* If this is a string node, then we need to mark it as
18227 * frozen because when clause strings are frozen. */
18228 if (PM_NODE_TYPE_P(condition, PM_STRING_NODE)) {
18229 pm_node_flag_set(condition, PM_STRING_FLAGS_FROZEN | PM_NODE_FLAG_STATIC_LITERAL);
18230 } else if (PM_NODE_TYPE_P(condition, PM_SOURCE_FILE_NODE)) {
18231 pm_node_flag_set(condition, PM_NODE_FLAG_STATIC_LITERAL);
18232 }
18233
18234 pm_when_clause_static_literals_add(parser, &literals, condition);
18235 }
18236 } while (accept1(parser, PM_TOKEN_COMMA));
18237
18238 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18239 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18240 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18241 }
18242 } else {
18243 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_WHEN_DELIMITER);
18244 pm_when_node_then_keyword_loc_set(parser, when_node, &parser->previous);
18245 }
18246
18247 if (!match3(parser, PM_TOKEN_KEYWORD_WHEN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18248 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_CASE_WHEN, (uint16_t) (depth + 1));
18249 if (statements != NULL) {
18250 pm_when_node_statements_set(when_node, statements);
18251 }
18252 }
18253
18254 pm_case_node_condition_append(parser->arena, case_node, UP(when_node));
18255 }
18256
18257 /* If we didn't parse any conditions (in or when) then we need to
18258 * indicate that we have an error. */
18259 if (case_node->conditions.size == 0) {
18260 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18261 }
18262
18263 pm_static_literals_free(&literals);
18264 node = UP(case_node);
18265 } else {
18266 pm_case_match_node_t *case_node = pm_case_match_node_create(parser, &case_keyword, predicate);
18267
18268 /* If this is a case-match node (i.e., it is a pattern matching case
18269 * statement) then we must have a predicate. */
18270 if (predicate == NULL) {
18271 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MATCH_MISSING_PREDICATE);
18272 }
18273
18274 /* At this point we expect that we're parsing a case-in node. We will
18275 * continue to parse the in nodes until we hit the end of the list. */
18276 while (match1(parser, PM_TOKEN_KEYWORD_IN)) {
18277 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, true);
18278
18279 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
18280 parser->pattern_matching_newlines = true;
18281
18282 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
18283 parser->command_start = false;
18284 parser_lex(parser);
18285
18286 pm_token_t in_keyword = parser->previous;
18287
18288 pm_constant_id_list_t captures = { 0 };
18289 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));
18290
18291 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
18292
18293 /* Since we're in the top-level of the case-in node we need to
18294 * check for guard clauses in the form of `if` or `unless`
18295 * statements. */
18296 if (accept1(parser, PM_TOKEN_KEYWORD_IF_MODIFIER)) {
18297 pm_token_t keyword = parser->previous;
18298 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));
18299 pattern = UP(pm_if_node_modifier_create(parser, pattern, &keyword, predicate));
18300 } else if (accept1(parser, PM_TOKEN_KEYWORD_UNLESS_MODIFIER)) {
18301 pm_token_t keyword = parser->previous;
18302 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));
18303 pattern = UP(pm_unless_node_modifier_create(parser, pattern, &keyword, predicate));
18304 }
18305
18306 /* Now we need to check for the terminator of the in node's pattern.
18307 * It can be a newline or semicolon optionally followed by a `then`
18308 * keyword. */
18309 pm_token_t then_keyword = { 0 };
18310 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18311 if (accept1(parser, PM_TOKEN_KEYWORD_THEN)) {
18312 then_keyword = parser->previous;
18313 }
18314 } else {
18315 expect1(parser, PM_TOKEN_KEYWORD_THEN, PM_ERR_EXPECT_IN_DELIMITER);
18316 then_keyword = parser->previous;
18317 }
18318
18319 /* Now we can actually parse the statements associated with the in
18320 * node. */
18321 pm_statements_node_t *statements;
18322 if (match3(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18323 statements = NULL;
18324 } else {
18325 statements = parse_statements(parser, PM_CONTEXT_CASE_IN, (uint16_t) (depth + 1));
18326 }
18327
18328 /* Now that we have the full pattern and statements, we can create
18329 * the node and attach it to the case node. */
18330 pm_node_t *condition = UP(pm_in_node_create(parser, pattern, statements, &in_keyword, NTOK2PTR(then_keyword)));
18331 pm_case_match_node_condition_append(parser->arena, case_node, condition);
18332 }
18333
18334 /* If we didn't parse any conditions (in or when) then we need to
18335 * indicate that we have an error. */
18336 if (case_node->conditions.size == 0) {
18337 pm_parser_err_token(parser, &case_keyword, PM_ERR_CASE_MISSING_CONDITIONS);
18338 }
18339
18340 node = UP(case_node);
18341 }
18342
18343 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18344 if (accept1(parser, PM_TOKEN_KEYWORD_ELSE)) {
18345 pm_token_t else_keyword = parser->previous;
18346 pm_else_node_t *else_node;
18347
18348 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
18349 else_node = pm_else_node_create(parser, &else_keyword, parse_statements(parser, PM_CONTEXT_ELSE, (uint16_t) (depth + 1)), &parser->current);
18350 } else {
18351 else_node = pm_else_node_create(parser, &else_keyword, NULL, &parser->current);
18352 }
18353
18354 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18355 pm_case_node_else_clause_set((pm_case_node_t *) node, else_node);
18356 } else {
18357 pm_case_match_node_else_clause_set((pm_case_match_node_t *) node, else_node);
18358 }
18359 }
18360
18361 parser_warn_indentation_mismatch(parser, opening_newline_index, &case_keyword, false, false);
18362 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CASE_TERM, &case_keyword);
18363
18364 if (PM_NODE_TYPE_P(node, PM_CASE_NODE)) {
18365 pm_case_node_end_keyword_loc_set(parser, (pm_case_node_t *) node, &parser->previous);
18366 } else {
18367 pm_case_match_node_end_keyword_loc_set(parser, (pm_case_match_node_t *) node, &parser->previous);
18368 }
18369
18370 pop_block_exits(parser, previous_block_exits);
18371 return node;
18372}
18373
18378static pm_node_t *
18379parse_class(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18380 size_t opening_newline_index = token_newline_index(parser);
18381 parser_lex(parser);
18382
18383 pm_token_t class_keyword = parser->previous;
18384 pm_do_loop_stack_push(parser, false);
18385
18386 pm_node_list_t current_block_exits = { 0 };
18387 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18388
18389 if (accept1(parser, PM_TOKEN_LESS_LESS)) {
18390 pm_token_t operator = parser->previous;
18391 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));
18392
18393 pm_parser_scope_push(parser, true);
18394 if (!match2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
18395 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_SINGLETON_CLASS_DELIMITER, pm_token_str(parser->current.type));
18396 }
18397
18398 pm_node_t *statements = NULL;
18399 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18400 pm_accepts_block_stack_push(parser, true);
18401 statements = UP(parse_statements(parser, PM_CONTEXT_SCLASS, (uint16_t) (depth + 1)));
18402 pm_accepts_block_stack_pop(parser);
18403 }
18404
18405 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18406 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18407 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)));
18408 } else {
18409 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18410 }
18411
18412 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18413
18414 pm_constant_id_list_t locals;
18415 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18416
18417 pm_parser_scope_pop(parser);
18418 pm_do_loop_stack_pop(parser);
18419
18420 flush_block_exits(parser, previous_block_exits);
18421 return UP(pm_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous));
18422 }
18423
18424 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));
18425 pm_token_t name = parser->previous;
18426 if (name.type != PM_TOKEN_CONSTANT) {
18427 pm_parser_err_token(parser, &name, PM_ERR_CLASS_NAME);
18428 }
18429
18430 pm_token_t inheritance_operator = { 0 };
18431 pm_node_t *superclass;
18432
18433 if (match1(parser, PM_TOKEN_LESS)) {
18434 inheritance_operator = parser->current;
18435 lex_state_set(parser, PM_LEX_STATE_BEG);
18436
18437 parser->command_start = true;
18438 parser_lex(parser);
18439
18440 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));
18441 } else {
18442 superclass = NULL;
18443 }
18444
18445 pm_parser_scope_push(parser, true);
18446
18447 if (inheritance_operator.start != NULL) {
18448 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CLASS_UNEXPECTED_END);
18449 } else {
18450 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18451 }
18452 pm_node_t *statements = NULL;
18453
18454 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18455 pm_accepts_block_stack_push(parser, true);
18456 statements = UP(parse_statements(parser, PM_CONTEXT_CLASS, (uint16_t) (depth + 1)));
18457 pm_accepts_block_stack_pop(parser);
18458 }
18459
18460 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
18461 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18462 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)));
18463 } else {
18464 parser_warn_indentation_mismatch(parser, opening_newline_index, &class_keyword, false, false);
18465 }
18466
18467 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_CLASS_TERM, &class_keyword);
18468
18469 if (context_def_p(parser)) {
18470 pm_parser_err_token(parser, &class_keyword, PM_ERR_CLASS_IN_METHOD);
18471 }
18472
18473 pm_constant_id_list_t locals;
18474 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18475
18476 pm_parser_scope_pop(parser);
18477 pm_do_loop_stack_pop(parser);
18478
18479 if (!PM_NODE_TYPE_P(constant_path, PM_CONSTANT_PATH_NODE) && !(PM_NODE_TYPE_P(constant_path, PM_CONSTANT_READ_NODE))) {
18480 pm_parser_err_node(parser, constant_path, PM_ERR_CLASS_NAME);
18481 if (!PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18482 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18483 }
18484 }
18485
18486 pop_block_exits(parser, previous_block_exits);
18487 return UP(pm_class_node_create(parser, &locals, &class_keyword, constant_path, &name, NTOK2PTR(inheritance_operator), superclass, statements, &parser->previous));
18488}
18489
18493static pm_node_t *
18494parse_def(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
18495 pm_node_list_t current_block_exits = { 0 };
18496 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18497
18498 pm_token_t def_keyword = parser->current;
18499 size_t opening_newline_index = token_newline_index(parser);
18500
18501 pm_node_t *receiver = NULL;
18502 pm_token_t operator = { 0 };
18503 pm_token_t name;
18504
18505 /* This context is necessary for lexing `...` in a bare params correctly. It
18506 * must be pushed before lexing the first param, so it is here. */
18507 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18508 parser_lex(parser);
18509
18510 /* This will be false if the method name is not a valid identifier but could
18511 * be followed by an operator. */
18512 bool valid_name = true;
18513
18514 switch (parser->current.type) {
18515 case PM_CASE_OPERATOR:
18516 pm_parser_scope_push(parser, true);
18517 lex_state_set(parser, PM_LEX_STATE_ENDFN);
18518 parser_lex(parser);
18519
18520 name = parser->previous;
18521 break;
18522 case PM_TOKEN_IDENTIFIER: {
18523 parser_lex(parser);
18524
18525 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18526 receiver = parse_variable_call(parser);
18527
18528 pm_parser_scope_push(parser, true);
18529 lex_state_set(parser, PM_LEX_STATE_FNAME);
18530 parser_lex(parser);
18531
18532 operator = parser->previous;
18533 name = parse_method_definition_name(parser);
18534 } else {
18535 pm_refute_numbered_parameter(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous));
18536 pm_parser_scope_push(parser, true);
18537
18538 name = parser->previous;
18539 }
18540
18541 break;
18542 }
18543 case PM_TOKEN_INSTANCE_VARIABLE:
18544 case PM_TOKEN_CLASS_VARIABLE:
18545 case PM_TOKEN_GLOBAL_VARIABLE:
18546 valid_name = false;
18548 case PM_TOKEN_CONSTANT:
18549 case PM_TOKEN_KEYWORD_NIL:
18550 case PM_TOKEN_KEYWORD_SELF:
18551 case PM_TOKEN_KEYWORD_TRUE:
18552 case PM_TOKEN_KEYWORD_FALSE:
18553 case PM_TOKEN_KEYWORD___FILE__:
18554 case PM_TOKEN_KEYWORD___LINE__:
18555 case PM_TOKEN_KEYWORD___ENCODING__: {
18556 pm_parser_scope_push(parser, true);
18557 parser_lex(parser);
18558
18559 pm_token_t identifier = parser->previous;
18560
18561 if (match2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON)) {
18562 lex_state_set(parser, PM_LEX_STATE_FNAME);
18563 parser_lex(parser);
18564 operator = parser->previous;
18565
18566 switch (identifier.type) {
18567 case PM_TOKEN_CONSTANT:
18568 receiver = UP(pm_constant_read_node_create(parser, &identifier));
18569 break;
18570 case PM_TOKEN_INSTANCE_VARIABLE:
18571 receiver = UP(pm_instance_variable_read_node_create(parser, &identifier));
18572 break;
18573 case PM_TOKEN_CLASS_VARIABLE:
18574 receiver = UP(pm_class_variable_read_node_create(parser, &identifier));
18575 break;
18576 case PM_TOKEN_GLOBAL_VARIABLE:
18577 receiver = UP(pm_global_variable_read_node_create(parser, &identifier));
18578 break;
18579 case PM_TOKEN_KEYWORD_NIL:
18580 receiver = UP(pm_nil_node_create(parser, &identifier));
18581 break;
18582 case PM_TOKEN_KEYWORD_SELF:
18583 receiver = UP(pm_self_node_create(parser, &identifier));
18584 break;
18585 case PM_TOKEN_KEYWORD_TRUE:
18586 receiver = UP(pm_true_node_create(parser, &identifier));
18587 break;
18588 case PM_TOKEN_KEYWORD_FALSE:
18589 receiver = UP(pm_false_node_create(parser, &identifier));
18590 break;
18591 case PM_TOKEN_KEYWORD___FILE__:
18592 receiver = UP(pm_source_file_node_create(parser, &identifier));
18593 break;
18594 case PM_TOKEN_KEYWORD___LINE__:
18595 receiver = UP(pm_source_line_node_create(parser, &identifier));
18596 break;
18597 case PM_TOKEN_KEYWORD___ENCODING__:
18598 receiver = UP(pm_source_encoding_node_create(parser, &identifier));
18599 break;
18600 default:
18601 break;
18602 }
18603
18604 name = parse_method_definition_name(parser);
18605 } else {
18606 if (!valid_name) {
18607 PM_PARSER_ERR_TOKEN_FORMAT(parser, &identifier, PM_ERR_DEF_NAME, pm_token_str(identifier.type));
18608 }
18609
18610 name = identifier;
18611 }
18612 break;
18613 }
18614 case PM_TOKEN_PARENTHESIS_LEFT: {
18615 /* The current context is `PM_CONTEXT_DEF_PARAMS`, however the inner
18616 * expression of this parenthesis should not be processed under this
18617 * context. Thus, the context is popped here. */
18618 context_pop(parser);
18619 parser_lex(parser);
18620
18621 pm_token_t lparen = parser->previous;
18622 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));
18623
18624 accept1(parser, PM_TOKEN_NEWLINE);
18625 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
18626 pm_token_t rparen = parser->previous;
18627
18628 lex_state_set(parser, PM_LEX_STATE_FNAME);
18629 expect2(parser, PM_TOKEN_DOT, PM_TOKEN_COLON_COLON, PM_ERR_DEF_RECEIVER_TERM);
18630
18631 operator = parser->previous;
18632 receiver = UP(pm_parentheses_node_create(parser, &lparen, expression, &rparen, 0));
18633
18634 /* To push `PM_CONTEXT_DEF_PARAMS` again is for the same reason as
18635 * described the above. */
18636 pm_parser_scope_push(parser, true);
18637 context_push(parser, PM_CONTEXT_DEF_PARAMS);
18638 name = parse_method_definition_name(parser);
18639 break;
18640 }
18641 default:
18642 pm_parser_scope_push(parser, true);
18643 name = parse_method_definition_name(parser);
18644 break;
18645 }
18646
18647 pm_token_t lparen = { 0 };
18648 pm_token_t rparen = { 0 };
18649 pm_parameters_node_t *params;
18650
18651 bool accept_endless_def = true;
18652 switch (parser->current.type) {
18653 case PM_TOKEN_PARENTHESIS_LEFT: {
18654 parser_lex(parser);
18655 lparen = parser->previous;
18656
18657 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18658 params = NULL;
18659 } else {
18660 /* https://bugs.ruby-lang.org/issues/19107 */
18661 bool allow_trailing_comma = parser->version >= PM_OPTIONS_VERSION_CRUBY_4_1;
18662 params = parse_parameters(
18663 parser,
18664 PM_BINDING_POWER_DEFINED,
18665 true,
18666 allow_trailing_comma,
18667 true,
18668 true,
18669 false,
18670 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18671 (uint16_t) (depth + 1)
18672 );
18673 }
18674
18675 lex_state_set(parser, PM_LEX_STATE_BEG);
18676 parser->command_start = true;
18677
18678 context_pop(parser);
18679 if (!accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
18680 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_DEF_PARAMS_TERM_PAREN, pm_token_str(parser->current.type));
18681 parser->previous.start = parser->previous.end;
18682 parser->previous.type = 0;
18683 }
18684
18685 rparen = parser->previous;
18686 break;
18687 }
18688 case PM_CASE_PARAMETER: {
18689 /* If we're about to lex a label, we need to add the label state to
18690 * make sure the next newline is ignored. */
18691 if (parser->current.type == PM_TOKEN_LABEL) {
18692 lex_state_set(parser, parser->lex_state | PM_LEX_STATE_LABEL);
18693 }
18694
18695 params = parse_parameters(
18696 parser,
18697 PM_BINDING_POWER_DEFINED,
18698 false,
18699 false,
18700 true,
18701 true,
18702 false,
18703 PM_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES,
18704 (uint16_t) (depth + 1)
18705 );
18706
18707 /* Reject `def * = 1` and similar. We have to specifically check for
18708 * them because they create ambiguity with optional arguments. */
18709 accept_endless_def = false;
18710
18711 context_pop(parser);
18712 break;
18713 }
18714 default: {
18715 params = NULL;
18716 context_pop(parser);
18717 break;
18718 }
18719 }
18720
18721 pm_node_t *statements = NULL;
18722 pm_token_t equal = { 0 };
18723 pm_token_t end_keyword = { 0 };
18724
18725 if (accept1(parser, PM_TOKEN_EQUAL)) {
18726 if (token_is_setter_name(&name)) {
18727 pm_parser_err_token(parser, &name, PM_ERR_DEF_ENDLESS_SETTER);
18728 }
18729 if (!accept_endless_def) {
18730 pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
18731 }
18732 if (
18733 parser->current_context->context == PM_CONTEXT_DEFAULT_PARAMS &&
18734 parser->current_context->prev->context == PM_CONTEXT_BLOCK_PARAMETERS
18735 ) {
18736 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");
18737 }
18738 equal = parser->previous;
18739
18740 context_push(parser, PM_CONTEXT_DEF);
18741 pm_do_loop_stack_push(parser, false);
18742 statements = UP(pm_statements_node_create(parser));
18743
18744 uint8_t allow_flags;
18745 if (parser->version >= PM_OPTIONS_VERSION_CRUBY_4_0) {
18746 allow_flags = flags & PM_PARSE_ACCEPTS_COMMAND_CALL;
18747 } else {
18748 /* Allow `def foo = puts "Hello"` but not
18749 * `private def foo = puts "Hello"` */
18750 allow_flags = (binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0;
18751 }
18752
18753 /* Inside a def body, we push true onto the accepts_block_stack so that
18754 * `do` is lexed as PM_TOKEN_KEYWORD_DO (which can only start a block
18755 * for primary-level constructs, not commands). During command argument
18756 * parsing, the stack is pushed to false, causing `do` to be lexed as
18757 * PM_TOKEN_KEYWORD_DO_BLOCK, which is not consumed inside the endless
18758 * def body and instead left for the outer context. A method definition
18759 * opens a fresh context all the way through its rescue modifier, so
18760 * this frame spans the rescue modifier value as well: the `do` in
18761 * `baz def f = a rescue z do end` lexes as a plain keyword that
18762 * attaches to `z` rather than to `baz`. */
18763 pm_accepts_block_stack_push(parser, true);
18764 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));
18765
18766 /* If an unconsumed PM_TOKEN_KEYWORD_DO follows the body, it is an error
18767 * (e.g., `def f = 1 do end`). PM_TOKEN_KEYWORD_DO_BLOCK is
18768 * intentionally not caught here — it should bubble up to the outer
18769 * context (e.g., `private def f = puts "Hello" do end` where the block
18770 * attaches to `private`). */
18771 if (accept1(parser, PM_TOKEN_KEYWORD_DO)) {
18772 pm_block_node_t *block = parse_block(parser, (uint16_t) (depth + 1));
18773 pm_parser_err_node(parser, UP(block), PM_ERR_DEF_ENDLESS_DO_BLOCK);
18774 }
18775
18776 /* Any number of rescue modifiers chain onto the body within the method
18777 * definition itself, associating to the left: `def f = a rescue b
18778 * rescue c` defines a method whose body is `(a rescue b) rescue c`,
18779 * rather than a rescue modifier guarding the definition. */
18780 while (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
18781 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
18782
18783 pm_token_t rescue_keyword = parser->previous;
18784
18785 /* In the Ruby grammar, the rescue value of an endless method
18786 * command excludes and/or and in/=>. */
18787 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));
18788 context_pop(parser);
18789
18790 statement = UP(pm_rescue_modifier_node_create(parser, statement, &rescue_keyword, value));
18791 }
18792
18793 pm_accepts_block_stack_pop(parser);
18794
18795 /* A nested endless def whose body is a command call (e.g.,
18796 * `def f = def g = foo bar`) is a command assignment and cannot appear
18797 * as a def body. */
18798 if (PM_NODE_TYPE_P(statement, PM_DEF_NODE) && pm_command_call_value_p(parser, statement)) {
18799 PM_PARSER_ERR_NODE_FORMAT(parser, statement, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
18800 }
18801
18802 pm_statements_node_body_append(parser, (pm_statements_node_t *) statements, statement, false);
18803 pm_do_loop_stack_pop(parser);
18804 context_pop(parser);
18805 } else {
18806 if (lparen.start == NULL) {
18807 lex_state_set(parser, PM_LEX_STATE_BEG);
18808 parser->command_start = true;
18809 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_DEF_PARAMS_TERM);
18810 } else {
18811 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
18812 }
18813
18814 pm_accepts_block_stack_push(parser, true);
18815 pm_do_loop_stack_push(parser, false);
18816
18817 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18818 pm_accepts_block_stack_push(parser, true);
18819 statements = UP(parse_statements(parser, PM_CONTEXT_DEF, (uint16_t) (depth + 1)));
18820 pm_accepts_block_stack_pop(parser);
18821 }
18822
18823 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18824 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18825 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)));
18826 } else {
18827 parser_warn_indentation_mismatch(parser, opening_newline_index, &def_keyword, false, false);
18828 }
18829
18830 pm_accepts_block_stack_pop(parser);
18831 pm_do_loop_stack_pop(parser);
18832
18833 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_DEF_TERM, &def_keyword);
18834 end_keyword = parser->previous;
18835 }
18836
18837 pm_constant_id_list_t locals;
18838 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18839 pm_parser_scope_pop(parser);
18840
18841 /* If the final character is `@` as is the case when defining methods to
18842 * override the unary operators, we should ignore the @ in the same way we
18843 * do for symbols. */
18844 pm_constant_id_t name_id = pm_parser_constant_id_raw(parser, name.start, parse_operator_symbol_name(&name));
18845
18846 flush_block_exits(parser, previous_block_exits);
18847
18848 return UP(pm_def_node_create(
18849 parser,
18850 name_id,
18851 &name,
18852 receiver,
18853 params,
18854 statements,
18855 &locals,
18856 &def_keyword,
18857 NTOK2PTR(operator),
18858 NTOK2PTR(lparen),
18859 NTOK2PTR(rparen),
18860 NTOK2PTR(equal),
18861 NTOK2PTR(end_keyword)
18862 ));
18863}
18864
18868static pm_node_t *
18869parse_module(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
18870 pm_node_list_t current_block_exits = { 0 };
18871 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
18872
18873 size_t opening_newline_index = token_newline_index(parser);
18874 parser_lex(parser);
18875 pm_token_t module_keyword = parser->previous;
18876
18877 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));
18878 pm_token_t name;
18879
18880 /* If we can recover from a syntax error that occurred while parsing the
18881 * name of the module, then we'll handle that here. */
18882 if (PM_NODE_TYPE_P(constant_path, PM_ERROR_RECOVERY_NODE)) {
18883 pop_block_exits(parser, previous_block_exits);
18884
18885 pm_token_t missing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
18886 return UP(pm_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing));
18887 }
18888
18889 while (accept1(parser, PM_TOKEN_COLON_COLON)) {
18890 pm_token_t double_colon = parser->previous;
18891
18892 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
18893 constant_path = UP(pm_constant_path_node_create(parser, constant_path, &double_colon, &parser->previous));
18894 }
18895
18896 /* Here we retrieve the name of the module. If it wasn't a constant, then
18897 * it's possible that `module foo` was passed, which is a syntax error. We
18898 * handle that here as well. */
18899 name = parser->previous;
18900 if (name.type != PM_TOKEN_CONSTANT) {
18901 pm_parser_err_token(parser, &name, PM_ERR_MODULE_NAME);
18902 }
18903
18904 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)) {
18905 constant_path = UP(pm_error_recovery_node_create_unexpected(parser, constant_path));
18906 }
18907
18908 pm_parser_scope_push(parser, true);
18909 accept2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE);
18910 pm_node_t *statements = NULL;
18911
18912 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
18913 pm_accepts_block_stack_push(parser, true);
18914 statements = UP(parse_statements(parser, PM_CONTEXT_MODULE, (uint16_t) (depth + 1)));
18915 pm_accepts_block_stack_pop(parser);
18916 }
18917
18918 if (match3(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE)) {
18919 assert(statements == NULL || PM_NODE_TYPE_P(statements, PM_STATEMENTS_NODE));
18920 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)));
18921 } else {
18922 parser_warn_indentation_mismatch(parser, opening_newline_index, &module_keyword, false, false);
18923 }
18924
18925 pm_constant_id_list_t locals;
18926 pm_locals_order(parser, &parser->current_scope->locals, &locals, false);
18927
18928 pm_parser_scope_pop(parser);
18929 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_MODULE_TERM, &module_keyword);
18930
18931 if (context_def_p(parser)) {
18932 pm_parser_err_token(parser, &module_keyword, PM_ERR_MODULE_IN_METHOD);
18933 }
18934
18935 pop_block_exits(parser, previous_block_exits);
18936
18937 return UP(pm_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous));
18938}
18939
18943static pm_node_t *
18944parse_string_array(pm_parser_t *parser, uint16_t depth) {
18945 parser_lex(parser);
18946 pm_token_t opening = parser->previous;
18947 pm_array_node_t *array = pm_array_node_create(parser, &opening);
18948
18949 /* This is the current node that we are parsing that will be added to the
18950 * list of elements. */
18951 pm_node_t *current = NULL;
18952
18953 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
18954 switch (parser->current.type) {
18955 case PM_TOKEN_WORDS_SEP: {
18956 /* Reset the explicit encoding if we hit a separator since each
18957 * element can have its own encoding. */
18958 parser->explicit_encoding = NULL;
18959
18960 if (current == NULL) {
18961 /* If we hit a separator before we have any content, then we
18962 * don't need to do anything. */
18963 } else {
18964 /* If we hit a separator after we've hit content, then we
18965 * need to append that content to the list and reset the
18966 * current node. */
18967 pm_array_node_elements_append(parser->arena, array, current);
18968 current = NULL;
18969 }
18970
18971 parser_lex(parser);
18972 break;
18973 }
18974 case PM_TOKEN_STRING_CONTENT: {
18975 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
18976 pm_node_flag_set(string, parse_unescaped_encoding(parser, parser->explicit_encoding));
18977 parser_lex(parser);
18978
18979 if (current == NULL) {
18980 /* If we hit content and the current node is NULL, then this
18981 * is the first string content we've seen. In that case
18982 * we're going to create a new string node and set that to
18983 * the current. */
18984 current = string;
18985 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
18986 /* If we hit string content and the current node is an
18987 * interpolated string, then we need to append the string
18988 * content to the list of child nodes. */
18989 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
18990 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
18991 /* If we hit string content and the current node is a string
18992 * node, then we need to convert the current node into an
18993 * interpolated string and add the string content to the
18994 * list of child nodes. */
18995 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
18996 pm_interpolated_string_node_append(parser, interpolated, current);
18997 pm_interpolated_string_node_append(parser, interpolated, string);
18998 current = UP(interpolated);
18999 } else {
19000 assert(false && "unreachable");
19001 }
19002
19003 break;
19004 }
19005 case PM_TOKEN_EMBVAR: {
19006 if (current == NULL) {
19007 /* If we hit an embedded variable and the current node is
19008 * NULL, then this is the start of a new string. We'll set
19009 * the current node to a new interpolated string. */
19010 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
19011 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19012 /* If we hit an embedded variable and the current node is a
19013 * string node, then we'll convert the current into an
19014 * interpolated string and add the string node to the list
19015 * of parts. */
19016 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
19017 pm_interpolated_string_node_append(parser, interpolated, current);
19018 current = UP(interpolated);
19019 } else {
19020 /* If we hit an embedded variable and the current node is an
19021 * interpolated string, then we'll just add the embedded
19022 * variable. */
19023 }
19024
19025 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19026 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, part);
19027 break;
19028 }
19029 case PM_TOKEN_EMBEXPR_BEGIN: {
19030 if (current == NULL) {
19031 /* If we hit an embedded expression and the current node is
19032 * NULL, then this is the start of a new string. We'll set
19033 * the current node to a new interpolated string. */
19034 current = UP(pm_interpolated_string_node_create(parser, NULL, NULL, NULL));
19035 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
19036 /* If we hit an embedded expression and the current node is
19037 * a string node, then we'll convert the current into an
19038 * interpolated string and add the string node to the list
19039 * of parts. */
19040 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
19041 pm_interpolated_string_node_append(parser, interpolated, current);
19042 current = UP(interpolated);
19043 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
19044 /* If we hit an embedded expression and the current node is
19045 * an interpolated string, then we'll just continue on. */
19046 } else {
19047 assert(false && "unreachable");
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 default:
19055 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_UPPER_ELEMENT);
19056 parser_lex(parser);
19057 break;
19058 }
19059 }
19060
19061 /* If we have a current node, then we need to append it to the list. */
19062 if (current) {
19063 pm_array_node_elements_append(parser->arena, array, current);
19064 }
19065
19066 pm_token_t closing = parser->current;
19067 if (match1(parser, PM_TOKEN_EOF)) {
19068 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_UPPER_TERM);
19069 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19070 } else {
19071 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_UPPER_TERM);
19072 }
19073
19074 pm_array_node_close_set(parser, array, &closing);
19075 return UP(array);
19076}
19077
19081static pm_node_t *
19082parse_symbol_array(pm_parser_t *parser, uint16_t depth) {
19083 parser_lex(parser);
19084 pm_token_t opening = parser->previous;
19085 pm_array_node_t *array = pm_array_node_create(parser, &opening);
19086
19087 /* This is the current node that we are parsing that will be added to the
19088 * list of elements. */
19089 pm_node_t *current = NULL;
19090
19091 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
19092 switch (parser->current.type) {
19093 case PM_TOKEN_WORDS_SEP: {
19094 /* Reset the explicit encoding if we hit a separator since each
19095 * element can have its own encoding. */
19096 parser->explicit_encoding = NULL;
19097
19098 if (current == NULL) {
19099 /* If we hit a separator before we have any content, then we
19100 * don't need to do anything. */
19101 } else {
19102 /* If we hit a separator after we've hit content, then we
19103 * need to append that content to the list and reset the
19104 * current node. */
19105 pm_array_node_elements_append(parser->arena, array, current);
19106 current = NULL;
19107 }
19108
19109 parser_lex(parser);
19110 break;
19111 }
19112 case PM_TOKEN_STRING_CONTENT: {
19113 if (current == NULL) {
19114 /* If we hit content and the current node is NULL, then this
19115 * is the first string content we've seen. In that case
19116 * we're going to create a new string node and set that to
19117 * the current. */
19118 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
19119 parser_lex(parser);
19120 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
19121 /* If we hit string content and the current node is an
19122 * interpolated string, then we need to append the string
19123 * content to the list of child nodes. */
19124 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
19125 parser_lex(parser);
19126
19127 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
19128 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19129 /* If we hit string content and the current node is a symbol
19130 * node, then we need to convert the current node into an
19131 * interpolated string and add the string content to the
19132 * list of child nodes. */
19133 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
19134 pm_token_t content = {
19135 .type = PM_TOKEN_STRING_CONTENT,
19136 .start = parser->start + cast->value_loc.start,
19137 .end = parser->start + cast->value_loc.start + cast->value_loc.length
19138 };
19139
19140 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
19141 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
19142 parser_lex(parser);
19143
19144 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
19145 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
19146 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
19147
19148 current = UP(interpolated);
19149 } else {
19150 assert(false && "unreachable");
19151 }
19152
19153 break;
19154 }
19155 case PM_TOKEN_EMBVAR: {
19156 bool start_location_set = false;
19157 if (current == NULL) {
19158 /* If we hit an embedded variable and the current node is
19159 * NULL, then this is the start of a new string. We'll set
19160 * the current node to a new interpolated string. */
19161 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
19162 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19163 /* If we hit an embedded variable and the current node is a
19164 * string node, then we'll convert the current into an
19165 * interpolated string and add the string node to the list
19166 * of parts. */
19167 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
19168
19169 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
19170 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
19171 PM_NODE_START_SET_NODE(interpolated, current);
19172 start_location_set = true;
19173 current = UP(interpolated);
19174 } else {
19175 /* If we hit an embedded variable and the current node is an
19176 * interpolated string, then we'll just add the embedded
19177 * variable. */
19178 }
19179
19180 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19181 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
19182 if (!start_location_set) {
19183 PM_NODE_START_SET_NODE(current, part);
19184 }
19185 break;
19186 }
19187 case PM_TOKEN_EMBEXPR_BEGIN: {
19188 bool start_location_set = false;
19189 if (current == NULL) {
19190 /* If we hit an embedded expression and the current node is
19191 * NULL, then this is the start of a new string. We'll set
19192 * the current node to a new interpolated string. */
19193 current = UP(pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL));
19194 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
19195 /* If we hit an embedded expression and the current node is
19196 * a string node, then we'll convert the current into an
19197 * interpolated string and add the string node to the list
19198 * of parts. */
19199 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
19200
19201 current = UP(pm_symbol_node_to_string_node(parser, (pm_symbol_node_t *) current));
19202 pm_interpolated_symbol_node_append(parser->arena, interpolated, current);
19203 PM_NODE_START_SET_NODE(interpolated, current);
19204 start_location_set = true;
19205 current = UP(interpolated);
19206 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
19207 /* If we hit an embedded expression and the current node is
19208 * an interpolated string, then we'll just continue on. */
19209 } else {
19210 assert(false && "unreachable");
19211 }
19212
19213 pm_node_t *part = parse_string_part(parser, (uint16_t) (depth + 1));
19214 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, part);
19215 if (!start_location_set) {
19216 PM_NODE_START_SET_NODE(current, part);
19217 }
19218 break;
19219 }
19220 default:
19221 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_UPPER_ELEMENT);
19222 parser_lex(parser);
19223 break;
19224 }
19225 }
19226
19227 /* If we have a current node, then we need to append it to the list. */
19228 if (current) {
19229 pm_array_node_elements_append(parser->arena, array, current);
19230 }
19231
19232 pm_token_t closing = parser->current;
19233 if (match1(parser, PM_TOKEN_EOF)) {
19234 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_UPPER_TERM);
19235 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
19236 } else {
19237 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_UPPER_TERM);
19238 }
19239 pm_array_node_close_set(parser, array, &closing);
19240
19241 return UP(array);
19242}
19243
19248static pm_node_t *
19249parse_parentheses(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, uint16_t depth) {
19250 pm_token_t opening = parser->current;
19251 pm_node_flags_t paren_flags = 0;
19252
19253 pm_node_list_t current_block_exits = { 0 };
19254 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
19255
19256 parser_lex(parser);
19257 while (true) {
19258 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19259 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19260 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19261 break;
19262 }
19263 }
19264
19265 /* If this is the end of the file or we match a right parenthesis, then we
19266 * have an empty parentheses node, and we can immediately return. */
19267 if (match2(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_EOF)) {
19268 /* A command argument group sets EXPR_ENDARG before its ')' is
19269 * consumed, even when the group is empty, so that a following '{' is
19270 * scanned as a block brace. */
19271 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT) && opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19272 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19273 }
19274
19275 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19276 pop_block_exits(parser, previous_block_exits);
19277 return UP(pm_parentheses_node_create(parser, &opening, NULL, &parser->previous, paren_flags));
19278 }
19279
19280 /* Otherwise, we're going to parse the first statement in the list of
19281 * statements within the parentheses. */
19282 context_push(parser, PM_CONTEXT_PARENS);
19283 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));
19284 context_pop(parser);
19285
19286 /* Determine if this statement is followed by a terminator. In the case of a
19287 * single statement, this is fine. But in the case of multiple statements
19288 * it's required. */
19289 bool terminator_found = false;
19290
19291 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19292 terminator_found = true;
19293 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19294 } else if (accept1(parser, PM_TOKEN_NEWLINE)) {
19295 terminator_found = true;
19296 }
19297
19298 if (terminator_found) {
19299 while (true) {
19300 if (accept1(parser, PM_TOKEN_SEMICOLON)) {
19301 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19302 } else if (!accept1(parser, PM_TOKEN_NEWLINE)) {
19303 break;
19304 }
19305 }
19306 }
19307
19308 /* If we hit a right parenthesis, then we're done parsing the parentheses
19309 * node, and we can check which kind of node we should return. */
19310 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19311 if (opening.type == PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
19312 lex_state_set(parser, PM_LEX_STATE_ENDARG);
19313 }
19314
19315 parser_lex(parser);
19316 pop_block_exits(parser, previous_block_exits);
19317
19318 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) || PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19319 /* If we have a single statement and are ending on a right
19320 * parenthesis, then we need to check if this is possibly a multiple
19321 * target node. */
19322 pm_multi_target_node_t *multi_target;
19323
19324 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE) && ((pm_multi_target_node_t *) statement)->lparen_loc.length == 0) {
19325 multi_target = (pm_multi_target_node_t *) statement;
19326 } else {
19327 multi_target = pm_multi_target_node_create(parser);
19328 pm_multi_target_node_targets_append(parser, multi_target, statement);
19329 }
19330
19331 multi_target->lparen_loc = TOK2LOC(parser, &opening);
19332 multi_target->rparen_loc = TOK2LOC(parser, &parser->previous);
19333 PM_NODE_START_SET_TOKEN(parser, multi_target, &opening);
19334 PM_NODE_LENGTH_SET_TOKEN(parser, multi_target, &parser->previous);
19335
19336 pm_node_t *result;
19337 if (match1(parser, PM_TOKEN_COMMA) && (binding_power == PM_BINDING_POWER_STATEMENT)) {
19338 result = parse_targets(parser, UP(multi_target), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19339 accept1(parser, PM_TOKEN_NEWLINE);
19340 } else {
19341 result = UP(multi_target);
19342 }
19343
19344 if (context_p(parser, PM_CONTEXT_MULTI_TARGET)) {
19345 /* All set, this is explicitly allowed by the parent context. */
19346 } else if (context_p(parser, PM_CONTEXT_FOR_INDEX) && match2(parser, PM_TOKEN_KEYWORD_IN, PM_TOKEN_COMMA)) {
19347 /* All set, we're inside a for loop and we're parsing multiple
19348 * targets. A comma continues the index target list, as in
19349 * `for (a, b), c in ...`. */
19350 } else if (flags & PM_PARSE_ACCEPTS_STATEMENT) {
19351 /* The rescue-modifier value parser promotes this target on a
19352 * following `=` or comma. Reject any other binary operator that
19353 * would otherwise consume the target list (e.g. `(a, b) + c`). */
19354 if (pm_binding_powers[parser->current.type].binary && !match1(parser, PM_TOKEN_EQUAL)) {
19355 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19356 }
19357 } else if (binding_power != PM_BINDING_POWER_STATEMENT) {
19358 /* Multi targets are not allowed when it's not a statement
19359 * level. */
19360 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19361 } else if (!match2(parser, PM_TOKEN_EQUAL, PM_TOKEN_PARENTHESIS_RIGHT)) {
19362 /* Multi targets must be followed by an equal sign in order to
19363 * be valid (or a right parenthesis if they are nested). */
19364 pm_parser_err_node(parser, result, PM_ERR_WRITE_TARGET_UNEXPECTED);
19365 }
19366
19367 return result;
19368 }
19369
19370 /* If we have a single statement and are ending on a right parenthesis
19371 * and we didn't return a multiple assignment node, then we can return a
19372 * regular parentheses node now. */
19373 pm_statements_node_t *statements = pm_statements_node_create(parser);
19374 pm_statements_node_body_append(parser, statements, statement, true);
19375
19376 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19377 }
19378
19379 /* If we have more than one statement in the set of parentheses, then we are
19380 * going to parse all of them as a list of statements. We'll do that here.
19381 */
19382 context_push(parser, PM_CONTEXT_PARENS);
19383 paren_flags |= PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS;
19384
19385 pm_statements_node_t *statements = pm_statements_node_create(parser);
19386 pm_statements_node_body_append(parser, statements, statement, true);
19387
19388 /* If we didn't find a terminator and we didn't find a right parenthesis,
19389 * then this is a syntax error. */
19390 if (!terminator_found && !match1(parser, PM_TOKEN_EOF)) {
19391 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19392 }
19393
19394 /* Parse each statement within the parentheses. */
19395 while (true) {
19396 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));
19397 pm_statements_node_body_append(parser, statements, node, true);
19398
19399 /* If we're recovering from a syntax error, then we need to stop parsing
19400 * the statements now. */
19401 if (parser->recovering) {
19402 /* If this is the level of context where the recovery has happened,
19403 * then we can mark the parser as done recovering. */
19404 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false;
19405 break;
19406 }
19407
19408 /* If we couldn't parse an expression at all, then we need to bail out
19409 * of the loop. */
19410 if (PM_NODE_TYPE_P(node, PM_ERROR_RECOVERY_NODE)) break;
19411
19412 /* If we successfully parsed a statement, then we are going to need a
19413 * terminator to delimit them. */
19414 if (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON)) {
19415 while (accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON));
19416 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) break;
19417 } else if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19418 break;
19419 } else if (!match1(parser, PM_TOKEN_EOF)) {
19420 /* If we're at the end of the file, then we're going to add an error
19421 * after this for the ) anyway. */
19422 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
19423 }
19424 }
19425
19426 context_pop(parser);
19427 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19428
19429 /* When we're parsing multi targets, we allow them to be followed by a right
19430 * parenthesis if they are at the statement level. This is only possible if
19431 * they are the final statement in a parentheses. We need to explicitly
19432 * reject that here. */
19433 {
19434 pm_node_t *statement = statements->body.nodes[statements->body.size - 1];
19435
19436 if (PM_NODE_TYPE_P(statement, PM_SPLAT_NODE)) {
19437 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
19438 pm_multi_target_node_targets_append(parser, multi_target, statement);
19439
19440 statement = UP(multi_target);
19441 statements->body.nodes[statements->body.size - 1] = statement;
19442 }
19443
19444 if (PM_NODE_TYPE_P(statement, PM_MULTI_TARGET_NODE)) {
19445 const uint8_t *offset = parser->start + PM_NODE_END(statement);
19446 pm_token_t operator = { .type = PM_TOKEN_EQUAL, .start = offset, .end = offset };
19447 pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_END(statement), 0));
19448
19449 statement = UP(pm_multi_write_node_create(parser, (pm_multi_target_node_t *) statement, &operator, value));
19450 statements->body.nodes[statements->body.size - 1] = statement;
19451
19452 pm_parser_err_node(parser, statement, PM_ERR_WRITE_TARGET_UNEXPECTED);
19453 }
19454 }
19455
19456 pop_block_exits(parser, previous_block_exits);
19457 pm_void_statements_check(parser, statements, true);
19458 return UP(pm_parentheses_node_create(parser, &opening, UP(statements), &parser->previous, paren_flags));
19459}
19460
19466static pm_node_t *
19467parse_splat(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
19468 pm_token_t operator = parser->previous;
19469 pm_node_t *name = NULL;
19470
19471 if (token_begins_expression_p(parser->current.type)) {
19472 name = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_STAR, (uint16_t) (depth + 1));
19473 }
19474
19475 return UP(pm_splat_node_create(parser, &operator, name));
19476}
19477
19481static PRISM_INLINE pm_node_t *
19482parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
19483 switch (parser->current.type) {
19484 case PM_TOKEN_BRACKET_LEFT_ARRAY: {
19485 parser_lex(parser);
19486
19487 pm_array_node_t *array = pm_array_node_create(parser, &parser->previous);
19488 bool parsed_bare_hash = false;
19489
19490 while (!match2(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_EOF)) {
19491 bool accepted_newline = accept1(parser, PM_TOKEN_NEWLINE);
19492
19493 // Handle the case where we don't have a comma and we have a
19494 // newline followed by a right bracket.
19495 if (accepted_newline && match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19496 break;
19497 }
19498
19499 // Ensure that we have a comma between elements in the array.
19500 if (array->elements.size > 0) {
19501 if (accept1(parser, PM_TOKEN_COMMA)) {
19502 // If there was a comma but we also accepts a newline,
19503 // then this is a syntax error.
19504 if (accepted_newline) {
19505 pm_parser_err_previous(parser, PM_ERR_INVALID_COMMA);
19506 }
19507 } else {
19508 // If there was no comma, then we need to add a syntax
19509 // error.
19510 PM_PARSER_ERR_FORMAT(parser, PM_TOKEN_END(parser, &parser->previous), 0, PM_ERR_ARRAY_SEPARATOR, pm_token_str(parser->current.type));
19511 parser->previous.start = parser->previous.end;
19512 parser->previous.type = 0;
19513 }
19514 }
19515
19516 // If we have a right bracket immediately following a comma,
19517 // this is allowed since it's a trailing comma. In this case we
19518 // can break out of the loop.
19519 if (match1(parser, PM_TOKEN_BRACKET_RIGHT)) break;
19520
19521 pm_node_t *element;
19522
19523 if (accept1(parser, PM_TOKEN_USTAR)) {
19524 pm_token_t operator = parser->previous;
19525 pm_node_t *expression = NULL;
19526
19527 if (match3(parser, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_COMMA, PM_TOKEN_EOF)) {
19528 pm_parser_scope_forwarding_positionals_check(parser, &operator);
19529 } else {
19530 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));
19531 }
19532
19533 element = UP(pm_splat_node_create(parser, &operator, expression));
19534 } else if (match2(parser, PM_TOKEN_LABEL, PM_TOKEN_USTAR_STAR)) {
19535 if (parsed_bare_hash) {
19536 pm_parser_err_current(parser, PM_ERR_EXPRESSION_BARE_HASH);
19537 }
19538
19539 element = UP(pm_keyword_hash_node_create(parser));
19540 pm_static_literals_t hash_keys = { 0 };
19541
19542 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)) {
19543 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
19544 }
19545
19546 pm_static_literals_free(&hash_keys);
19547 parsed_bare_hash = true;
19548 } else {
19549 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));
19550
19551 if (pm_symbol_node_label_p(parser, element) || accept1(parser, PM_TOKEN_EQUAL_GREATER)) {
19552 if (parsed_bare_hash) {
19553 pm_parser_err_previous(parser, PM_ERR_EXPRESSION_BARE_HASH);
19554 }
19555
19556 pm_keyword_hash_node_t *hash = pm_keyword_hash_node_create(parser);
19557 pm_static_literals_t hash_keys = { 0 };
19558 pm_hash_key_static_literals_add(parser, &hash_keys, element);
19559
19560 pm_token_t operator = { 0 };
19561 if (parser->previous.type == PM_TOKEN_EQUAL_GREATER) {
19562 operator = parser->previous;
19563 }
19564
19565 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));
19566 pm_node_t *assoc = UP(pm_assoc_node_create(parser, element, NTOK2PTR(operator), value));
19567 pm_keyword_hash_node_elements_append(parser->arena, hash, assoc);
19568
19569 element = UP(hash);
19570 if (accept1(parser, PM_TOKEN_COMMA) && !match1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19571 parse_assocs(parser, &hash_keys, element, (uint16_t) (depth + 1));
19572 }
19573
19574 pm_static_literals_free(&hash_keys);
19575 parsed_bare_hash = true;
19576 }
19577 }
19578
19579 pm_array_node_elements_append(parser->arena, array, element);
19580 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
19581 }
19582
19583 accept1(parser, PM_TOKEN_NEWLINE);
19584
19585 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
19586 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_ARRAY_TERM, pm_token_str(parser->current.type));
19587 parser->previous.start = parser->previous.end;
19588 parser->previous.type = 0;
19589 }
19590
19591 pm_array_node_close_set(parser, array, &parser->previous);
19592
19593 return UP(array);
19594 }
19595 case PM_TOKEN_PARENTHESIS_LEFT_GROUPING:
19596 case PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES:
19597 return parse_parentheses(parser, binding_power, flags, depth);
19598 case PM_TOKEN_BRACE_LEFT_HASH: {
19599 parser_lex(parser);
19600
19601 pm_token_t opening = parser->previous;
19602 pm_hash_node_t *node = pm_hash_node_create(parser, &opening);
19603
19604 if (!match2(parser, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_EOF)) {
19605 pm_static_literals_t hash_keys = { 0 };
19606 parse_assocs(parser, &hash_keys, UP(node), (uint16_t) (depth + 1));
19607 pm_static_literals_free(&hash_keys);
19608
19609 accept1(parser, PM_TOKEN_NEWLINE);
19610 }
19611
19612 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_HASH_TERM, &opening);
19613 pm_hash_node_closing_loc_set(parser, node, &parser->previous);
19614
19615 return UP(node);
19616 }
19617 case PM_TOKEN_CHARACTER_LITERAL: {
19618 pm_node_t *node = UP(pm_string_node_create_current_string(
19619 parser,
19620 &(pm_token_t) {
19621 .type = PM_TOKEN_STRING_BEGIN,
19622 .start = parser->current.start,
19623 .end = parser->current.start + 1
19624 },
19625 &(pm_token_t) {
19626 .type = PM_TOKEN_STRING_CONTENT,
19627 .start = parser->current.start + 1,
19628 .end = parser->current.end
19629 },
19630 NULL
19631 ));
19632
19633 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
19634
19635 // Skip past the character literal here, since now we have handled
19636 // parser->explicit_encoding correctly.
19637 parser_lex(parser);
19638
19639 // Characters can be followed by strings in which case they are
19640 // automatically concatenated.
19641 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
19642 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
19643 }
19644
19645 return node;
19646 }
19647 case PM_TOKEN_CLASS_VARIABLE: {
19648 parser_lex(parser);
19649 pm_node_t *node = UP(pm_class_variable_read_node_create(parser, &parser->previous));
19650
19651 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19652 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19653 }
19654
19655 return node;
19656 }
19657 case PM_TOKEN_CONSTANT: {
19658 parser_lex(parser);
19659 pm_token_t constant = parser->previous;
19660
19661 // If a constant is immediately followed by parentheses, then this is in
19662 // fact a method call, not a constant read.
19663 if (
19664 match1(parser, PM_TOKEN_PARENTHESIS_LEFT) ||
19665 ((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))) ||
19666 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
19667 match1(parser, PM_TOKEN_BRACE_LEFT)
19668 ) {
19669 pm_arguments_t arguments = { 0 };
19670 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
19671 return UP(pm_call_node_fcall_create(parser, &constant, &arguments));
19672 }
19673
19674 pm_node_t *node = UP(pm_constant_read_node_create(parser, &parser->previous));
19675
19676 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19677 // If we get here, then we have a comma immediately following a
19678 // constant, so we're going to parse this as a multiple assignment.
19679 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19680 }
19681
19682 return node;
19683 }
19684 case PM_TOKEN_UCOLON_COLON: {
19685 parser_lex(parser);
19686 pm_token_t delimiter = parser->previous;
19687
19688 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
19689 pm_node_t *node = UP(pm_constant_path_node_create(parser, NULL, &delimiter, &parser->previous));
19690
19691 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19692 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19693 }
19694
19695 return node;
19696 }
19697 case PM_TOKEN_UDOT_DOT:
19698 case PM_TOKEN_UDOT_DOT_DOT: {
19699 pm_token_t operator = parser->current;
19700 parser_lex(parser);
19701
19702 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));
19703
19704 // Unary .. and ... are special because these are non-associative
19705 // operators that can also be unary operators. In this case we need
19706 // to explicitly reject code that has a .. or ... that follows this
19707 // expression.
19708 if (match2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
19709 pm_parser_err_current(parser, PM_ERR_UNEXPECTED_RANGE_OPERATOR);
19710 }
19711
19712 return UP(pm_range_node_create(parser, NULL, &operator, right));
19713 }
19714 case PM_TOKEN_FLOAT:
19715 parser_lex(parser);
19716 return UP(pm_float_node_create(parser, &parser->previous));
19717 case PM_TOKEN_FLOAT_IMAGINARY:
19718 parser_lex(parser);
19719 return UP(pm_float_node_imaginary_create(parser, &parser->previous));
19720 case PM_TOKEN_FLOAT_RATIONAL:
19721 parser_lex(parser);
19722 return UP(pm_float_node_rational_create(parser, &parser->previous));
19723 case PM_TOKEN_FLOAT_RATIONAL_IMAGINARY:
19724 parser_lex(parser);
19725 return UP(pm_float_node_rational_imaginary_create(parser, &parser->previous));
19726 case PM_TOKEN_NUMBERED_REFERENCE: {
19727 parser_lex(parser);
19728 pm_node_t *node = UP(pm_numbered_reference_read_node_create(parser, &parser->previous));
19729
19730 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19731 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19732 }
19733
19734 return node;
19735 }
19736 case PM_TOKEN_GLOBAL_VARIABLE: {
19737 parser_lex(parser);
19738 pm_node_t *node = UP(pm_global_variable_read_node_create(parser, &parser->previous));
19739
19740 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19741 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19742 }
19743
19744 return node;
19745 }
19746 case PM_TOKEN_BACK_REFERENCE: {
19747 parser_lex(parser);
19748 pm_node_t *node = UP(pm_back_reference_read_node_create(parser, &parser->previous));
19749
19750 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19751 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19752 }
19753
19754 return node;
19755 }
19756 case PM_TOKEN_IDENTIFIER:
19757 case PM_TOKEN_METHOD_NAME: {
19758 parser_lex(parser);
19759 pm_token_t identifier = parser->previous;
19760 pm_node_t *node = parse_variable_call(parser);
19761
19762 if (PM_NODE_TYPE_P(node, PM_CALL_NODE)) {
19763 // If parse_variable_call returned with a call node, then we
19764 // know the identifier is not in the local table. In that case
19765 // we need to check if there are arguments following the
19766 // identifier.
19767 pm_call_node_t *call = (pm_call_node_t *) node;
19768 pm_arguments_t arguments = { 0 };
19769
19770 if (parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1))) {
19771 // Since we found arguments, we need to turn off the
19772 // variable call bit in the flags.
19773 pm_node_flag_unset(UP(call), PM_CALL_NODE_FLAGS_VARIABLE_CALL);
19774
19775 call->opening_loc = arguments.opening_loc;
19776 call->arguments = arguments.arguments;
19777 call->closing_loc = arguments.closing_loc;
19778 call->block = arguments.block;
19779
19780 const pm_location_t *end = pm_arguments_end(&arguments);
19781 if (end == NULL) {
19782 PM_NODE_LENGTH_SET_LOCATION(call, &call->message_loc);
19783 } else {
19784 PM_NODE_LENGTH_SET_LOCATION(call, end);
19785 }
19786 }
19787 } else {
19788 // Otherwise, we know the identifier is in the local table. This
19789 // can still be a method call if it is followed by arguments or
19790 // a block, so we need to check for that here.
19791 if (
19792 ((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))) ||
19793 (pm_accepts_block_stack_p(parser) && match1(parser, PM_TOKEN_KEYWORD_DO)) ||
19794 match1(parser, PM_TOKEN_BRACE_LEFT)
19795 ) {
19796 pm_arguments_t arguments = { 0 };
19797 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
19798 pm_call_node_t *fcall = pm_call_node_fcall_create(parser, &identifier, &arguments);
19799
19800 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
19801 // If we're about to convert an 'it' implicit local
19802 // variable read into a method call, we need to remove
19803 // it from the list of implicit local variables.
19804 pm_node_unreference(parser, node);
19805 } else {
19806 // Otherwise, we're about to convert a regular local
19807 // variable read into a method call, in which case we
19808 // need to indicate that this was not a read for the
19809 // purposes of warnings.
19810 assert(PM_NODE_TYPE_P(node, PM_LOCAL_VARIABLE_READ_NODE));
19811
19812 if (pm_token_is_numbered_parameter(parser, PM_TOKEN_START(parser, &identifier), PM_TOKEN_LENGTH(&identifier))) {
19813 pm_node_unreference(parser, node);
19814 } else {
19816 pm_locals_unread(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
19817 }
19818 }
19819
19820 return UP(fcall);
19821 }
19822 }
19823
19824 if ((binding_power == PM_BINDING_POWER_STATEMENT) && match1(parser, PM_TOKEN_COMMA)) {
19825 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19826 }
19827
19828 return node;
19829 }
19830 case PM_TOKEN_HEREDOC_START: {
19831 // Here we have found a heredoc. We'll parse it and add it to the
19832 // list of strings.
19833 assert(parser->lex_modes.current->mode == PM_LEX_HEREDOC);
19834 pm_heredoc_lex_mode_t lex_mode = parser->lex_modes.current->as.heredoc.base;
19835
19836 size_t common_whitespace = (size_t) -1;
19837 parser->lex_modes.current->as.heredoc.common_whitespace = &common_whitespace;
19838
19839 parser_lex(parser);
19840 pm_token_t opening = parser->previous;
19841
19842 pm_node_t *node;
19843 pm_node_t *part;
19844
19845 if (match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19846 // If we get here, then we have an empty heredoc. We'll create
19847 // an empty content token and return an empty string node.
19848 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19849 pm_token_t content = parse_strings_empty_content(parser->previous.start);
19850
19851 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19852 node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19853 } else {
19854 node = UP(pm_string_node_create_unescaped(parser, &opening, &content, &parser->previous, &PM_STRING_EMPTY));
19855 }
19856
19857 PM_NODE_LENGTH_SET_TOKEN(parser, node, &opening);
19858 } else if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) == NULL) {
19859 // If we get here, then we tried to find something in the
19860 // heredoc but couldn't actually parse anything, so we'll just
19861 // return a missing node.
19862 //
19863 // parse_string_part handles its own errors, so there is no need
19864 // for us to add one here.
19865 node = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
19866 } else if (PM_NODE_TYPE_P(part, PM_STRING_NODE) && match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19867 // If we get here, then the part that we parsed was plain string
19868 // content and we're at the end of the heredoc, so we can return
19869 // just a string node with the heredoc opening and closing as
19870 // its opening and closing.
19871 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
19872 pm_string_node_t *cast = (pm_string_node_t *) part;
19873
19874 cast->opening_loc = TOK2LOC(parser, &opening);
19875 cast->closing_loc = TOK2LOC(parser, &parser->current);
19876 cast->base.location = cast->opening_loc;
19877
19878 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19879 assert(sizeof(pm_string_node_t) == sizeof(pm_x_string_node_t));
19880 cast->base.type = PM_X_STRING_NODE;
19881 }
19882
19883 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19884 parse_heredoc_dedent_string(parser->arena, &cast->unescaped, common_whitespace);
19885 }
19886
19887 node = UP(cast);
19888 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19889 } else {
19890 // If we get here, then we have multiple parts in the heredoc,
19891 // so we'll need to create an interpolated string node to hold
19892 // them all.
19893 pm_node_list_t parts = { 0 };
19894 pm_node_list_append(parser->arena, &parts, part);
19895
19896 while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19897 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19898 pm_node_list_append(parser->arena, &parts, part);
19899 }
19900 }
19901
19902 // Now that we have all of the parts, create the correct type of
19903 // interpolated node.
19904 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19905 pm_interpolated_x_string_node_t *cast = pm_interpolated_xstring_node_create(parser, &opening, &opening);
19906 cast->parts = parts;
19907
19908 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19909 pm_interpolated_xstring_node_closing_set(parser, cast, &parser->previous);
19910
19911 cast->base.location = cast->opening_loc;
19912 node = UP(cast);
19913 } else {
19914 pm_interpolated_string_node_t *cast = pm_interpolated_string_node_create(parser, &opening, &parts, &opening);
19915
19916 expect1_heredoc_term(parser, lex_mode.ident_start, lex_mode.ident_length);
19917 pm_interpolated_string_node_closing_set(parser, cast, &parser->previous);
19918
19919 cast->base.location = cast->opening_loc;
19920 node = UP(cast);
19921 }
19922
19923 // If this is a heredoc that is indented with a ~, then we need
19924 // to dedent each line by the common leading whitespace.
19925 if (lex_mode.indent == PM_HEREDOC_INDENT_TILDE && (common_whitespace != (size_t) -1) && (common_whitespace != 0)) {
19926 pm_node_list_t *nodes;
19927 if (lex_mode.quote == PM_HEREDOC_QUOTE_BACKTICK) {
19928 nodes = &((pm_interpolated_x_string_node_t *) node)->parts;
19929 } else {
19930 nodes = &((pm_interpolated_string_node_t *) node)->parts;
19931 }
19932
19933 parse_heredoc_dedent(parser, nodes, common_whitespace);
19934 }
19935 }
19936
19937 /* If a missing terminator left this heredoc's lex mode on the
19938 * stack, it still points at our stack-local common_whitespace.
19939 * Clear the pointer so that subsequent lexing cannot read from
19940 * this function's dead stack frame. */
19941 pm_lex_mode_t *whitespace_mode = parser->lex_modes.current;
19942 do {
19943 if (whitespace_mode->mode == PM_LEX_HEREDOC && whitespace_mode->as.heredoc.common_whitespace == &common_whitespace) {
19944 whitespace_mode->as.heredoc.common_whitespace = NULL;
19945 }
19946 whitespace_mode = whitespace_mode->prev;
19947 } while (whitespace_mode != NULL);
19948
19949 if (match1(parser, PM_TOKEN_STRING_BEGIN)) {
19950 return parse_strings(parser, node, false, (uint16_t) (depth + 1));
19951 }
19952
19953 return node;
19954 }
19955 case PM_TOKEN_INSTANCE_VARIABLE: {
19956 parser_lex(parser);
19957 pm_node_t *node = UP(pm_instance_variable_read_node_create(parser, &parser->previous));
19958
19959 if (binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
19960 node = parse_targets_validate(parser, node, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
19961 }
19962
19963 return node;
19964 }
19965 case PM_TOKEN_INTEGER: {
19966 pm_node_flags_t base = parser->integer.base;
19967 parser_lex(parser);
19968 return UP(pm_integer_node_create(parser, base, &parser->previous));
19969 }
19970 case PM_TOKEN_INTEGER_IMAGINARY: {
19971 pm_node_flags_t base = parser->integer.base;
19972 parser_lex(parser);
19973 return UP(pm_integer_node_imaginary_create(parser, base, &parser->previous));
19974 }
19975 case PM_TOKEN_INTEGER_RATIONAL: {
19976 pm_node_flags_t base = parser->integer.base;
19977 parser_lex(parser);
19978 return UP(pm_integer_node_rational_create(parser, base, &parser->previous));
19979 }
19980 case PM_TOKEN_INTEGER_RATIONAL_IMAGINARY: {
19981 pm_node_flags_t base = parser->integer.base;
19982 parser_lex(parser);
19983 return UP(pm_integer_node_rational_imaginary_create(parser, base, &parser->previous));
19984 }
19985 case PM_TOKEN_KEYWORD___ENCODING__:
19986 parser_lex(parser);
19987 return UP(pm_source_encoding_node_create(parser, &parser->previous));
19988 case PM_TOKEN_KEYWORD___FILE__:
19989 parser_lex(parser);
19990 return UP(pm_source_file_node_create(parser, &parser->previous));
19991 case PM_TOKEN_KEYWORD___LINE__:
19992 parser_lex(parser);
19993 return UP(pm_source_line_node_create(parser, &parser->previous));
19994 case PM_TOKEN_KEYWORD_ALIAS: {
19995 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
19996 pm_parser_err_current(parser, PM_ERR_STATEMENT_ALIAS);
19997 }
19998
19999 parser_lex(parser);
20000 pm_token_t keyword = parser->previous;
20001
20002 pm_node_t *new_name = parse_alias_argument(parser, true, (uint16_t) (depth + 1));
20003 pm_node_t *old_name = parse_alias_argument(parser, false, (uint16_t) (depth + 1));
20004
20005 switch (PM_NODE_TYPE(new_name)) {
20006 case PM_BACK_REFERENCE_READ_NODE:
20007 case PM_NUMBERED_REFERENCE_READ_NODE:
20008 case PM_GLOBAL_VARIABLE_READ_NODE: {
20009 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)) {
20010 if (PM_NODE_TYPE_P(old_name, PM_NUMBERED_REFERENCE_READ_NODE)) {
20011 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT_NUMBERED_REFERENCE);
20012 }
20013 } else if (!PM_NODE_TYPE_P(old_name, PM_ERROR_RECOVERY_NODE)) {
20014 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
20015 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
20016 }
20017
20018 return UP(pm_alias_global_variable_node_create(parser, &keyword, new_name, old_name));
20019 }
20020 case PM_SYMBOL_NODE:
20021 case PM_INTERPOLATED_SYMBOL_NODE: {
20022 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)) {
20023 pm_parser_err_node(parser, old_name, PM_ERR_ALIAS_ARGUMENT);
20024 old_name = UP(pm_error_recovery_node_create_unexpected(parser, old_name));
20025 }
20026 }
20028 default:
20029 return UP(pm_alias_method_node_create(parser, &keyword, new_name, old_name));
20030 }
20031 }
20032 case PM_TOKEN_KEYWORD_CASE:
20033 return parse_case(parser, flags, depth);
20034 case PM_TOKEN_KEYWORD_BEGIN: {
20035 size_t opening_newline_index = token_newline_index(parser);
20036 parser_lex(parser);
20037
20038 pm_token_t begin_keyword = parser->previous;
20039 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20040
20041 pm_node_list_t current_block_exits = { 0 };
20042 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
20043 pm_statements_node_t *begin_statements = NULL;
20044
20045 if (!match4(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE, PM_TOKEN_KEYWORD_ELSE, PM_TOKEN_KEYWORD_END)) {
20046 pm_accepts_block_stack_push(parser, true);
20047 begin_statements = parse_statements(parser, PM_CONTEXT_BEGIN, (uint16_t) (depth + 1));
20048 pm_accepts_block_stack_pop(parser);
20049 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20050 }
20051
20052 pm_begin_node_t *begin_node = pm_begin_node_create(parser, &begin_keyword, begin_statements);
20053 parse_rescues(parser, opening_newline_index, &begin_keyword, begin_node, PM_RESCUES_BEGIN, (uint16_t) (depth + 1));
20054 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_BEGIN_TERM, &begin_keyword);
20055
20056 PM_NODE_LENGTH_SET_TOKEN(parser, begin_node, &parser->previous);
20057 pm_begin_node_end_keyword_set(parser, begin_node, &parser->previous);
20058 pop_block_exits(parser, previous_block_exits);
20059 return UP(begin_node);
20060 }
20061 case PM_TOKEN_KEYWORD_BEGIN_UPCASE: {
20062 pm_node_list_t current_block_exits = { 0 };
20063 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
20064
20065 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20066 pm_parser_err_current(parser, PM_ERR_STATEMENT_PREEXE_BEGIN);
20067 }
20068
20069 parser_lex(parser);
20070 pm_token_t keyword = parser->previous;
20071
20072 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_BEGIN_UPCASE_BRACE);
20073 pm_token_t opening = parser->previous;
20074 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_PREEXE, (uint16_t) (depth + 1));
20075
20076 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_BEGIN_UPCASE_TERM, &opening);
20077 pm_context_t context = parser->current_context->context;
20078 if ((context != PM_CONTEXT_MAIN) && (context != PM_CONTEXT_PREEXE)) {
20079 pm_parser_err_token(parser, &keyword, PM_ERR_BEGIN_UPCASE_TOPLEVEL);
20080 }
20081
20082 flush_block_exits(parser, previous_block_exits);
20083 return UP(pm_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous));
20084 }
20085 case PM_TOKEN_KEYWORD_BREAK:
20086 case PM_TOKEN_KEYWORD_NEXT:
20087 case PM_TOKEN_KEYWORD_RETURN: {
20088 parser_lex(parser);
20089
20090 pm_token_t keyword = parser->previous;
20091 pm_arguments_t arguments = { 0 };
20092
20093 if (
20094 token_begins_expression_p(parser->current.type) ||
20095 match2(parser, PM_TOKEN_USTAR, PM_TOKEN_USTAR_STAR)
20096 ) {
20097 pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
20098
20099 if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
20100 pm_token_t next = parser->current;
20101 parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, flags, (uint16_t) (depth + 1));
20102
20103 // Reject `foo && return bar`.
20104 if (!(flags & PM_PARSE_ACCEPTS_COMMAND_CALL) && arguments.arguments != NULL) {
20105 PM_PARSER_ERR_TOKEN_FORMAT(parser, &next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(next.type));
20106 }
20107
20108 // Reject a trailing comma, e.g. `return a,`. The arguments
20109 // parser silently accepts a trailing comma only when it is
20110 // immediately followed by the EOF terminator; in every other
20111 // case (e.g. `return a,;`) it reports the dangling comma
20112 // itself. We reject the accepted case here to stay in line
20113 // with the command call argument parsing above.
20114 if (parser->previous.type == PM_TOKEN_COMMA && match1(parser, PM_TOKEN_EOF)) {
20115 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_ARGUMENT, pm_token_str(parser->current.type));
20116 }
20117 }
20118
20119 // It's possible that we've parsed a block argument through our
20120 // call to parse_arguments. If we found one, we should mark it
20121 // as invalid and destroy it, as we don't have a place for it.
20122 if (arguments.block != NULL) {
20123 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20124 pm_node_unreference(parser, arguments.block);
20125 arguments.block = NULL;
20126 }
20127 }
20128
20129 switch (keyword.type) {
20130 case PM_TOKEN_KEYWORD_BREAK: {
20131 pm_node_t *node = UP(pm_break_node_create(parser, &keyword, arguments.arguments));
20132 if (!parser->partial_script) parse_block_exit(parser, node);
20133 return node;
20134 }
20135 case PM_TOKEN_KEYWORD_NEXT: {
20136 pm_node_t *node = UP(pm_next_node_create(parser, &keyword, arguments.arguments));
20137 if (!parser->partial_script) parse_block_exit(parser, node);
20138 return node;
20139 }
20140 case PM_TOKEN_KEYWORD_RETURN: {
20141 pm_node_t *node = UP(pm_return_node_create(parser, &keyword, arguments.arguments));
20142 parse_return(parser, node);
20143 return node;
20144 }
20145 default:
20146 assert(false && "unreachable");
20147 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20148 }
20149 }
20150 case PM_TOKEN_KEYWORD_SUPER: {
20151 parser_lex(parser);
20152
20153 pm_token_t keyword = parser->previous;
20154 pm_arguments_t arguments = { 0 };
20155 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
20156
20157 if (
20158 arguments.opening_loc.length == 0 &&
20159 arguments.arguments == NULL &&
20160 ((arguments.block == NULL) || PM_NODE_TYPE_P(arguments.block, PM_BLOCK_NODE))
20161 ) {
20162 return UP(pm_forwarding_super_node_create(parser, &keyword, &arguments));
20163 }
20164
20165 return UP(pm_super_node_create(parser, &keyword, &arguments));
20166 }
20167 case PM_TOKEN_KEYWORD_YIELD: {
20168 parser_lex(parser);
20169
20170 pm_token_t keyword = parser->previous;
20171 pm_arguments_t arguments = { 0 };
20172 parse_arguments_list(parser, &arguments, false, flags, (uint16_t) (depth + 1));
20173
20174 // It's possible that we've parsed a block argument through our
20175 // call to parse_arguments_list. If we found one, we should mark it
20176 // as invalid and destroy it, as we don't have a place for it on the
20177 // yield node.
20178 if (arguments.block != NULL) {
20179 pm_parser_err_node(parser, arguments.block, PM_ERR_UNEXPECTED_BLOCK_ARGUMENT);
20180 pm_node_unreference(parser, arguments.block);
20181 arguments.block = NULL;
20182 }
20183
20184 pm_node_t *node = UP(pm_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc));
20185 if (!parser->parsing_eval && !parser->partial_script) parse_yield(parser, node);
20186
20187 return node;
20188 }
20189 case PM_TOKEN_KEYWORD_CLASS:
20190 return parse_class(parser, flags, depth);
20191 case PM_TOKEN_KEYWORD_DEF:
20192 return parse_def(parser, binding_power, flags, depth);
20193 case PM_TOKEN_KEYWORD_DEFINED: {
20194 parser_lex(parser);
20195
20196 pm_token_t keyword = parser->previous;
20197 pm_token_t lparen = { 0 };
20198 pm_token_t rparen = { 0 };
20199 pm_node_t *expression;
20200
20201 context_push(parser, PM_CONTEXT_DEFINED);
20202 bool newline = accept1(parser, PM_TOKEN_NEWLINE);
20203
20204 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
20205 lparen = parser->previous;
20206
20207 if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20208 expression = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
20209 lparen = (pm_token_t) { 0 };
20210 } else {
20211 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));
20212
20213 if (!parser->recovering) {
20214 accept1(parser, PM_TOKEN_NEWLINE);
20215 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20216 rparen = parser->previous;
20217 }
20218 }
20219 } else {
20220 expression = parse_expression(parser, PM_BINDING_POWER_DEFINED, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
20221 }
20222
20223 context_pop(parser);
20224 return UP(pm_defined_node_create(
20225 parser,
20226 NTOK2PTR(lparen),
20227 expression,
20228 NTOK2PTR(rparen),
20229 &keyword
20230 ));
20231 }
20232 case PM_TOKEN_KEYWORD_END_UPCASE: {
20233 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20234 pm_parser_err_current(parser, PM_ERR_STATEMENT_POSTEXE_END);
20235 }
20236
20237 parser_lex(parser);
20238 pm_token_t keyword = parser->previous;
20239
20240 if (context_def_p(parser)) {
20241 pm_parser_warn_token(parser, &keyword, PM_WARN_END_IN_METHOD);
20242 }
20243
20244 expect1(parser, PM_TOKEN_BRACE_LEFT, PM_ERR_END_UPCASE_BRACE);
20245 pm_token_t opening = parser->previous;
20246 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_POSTEXE, (uint16_t) (depth + 1));
20247
20248 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_END_UPCASE_TERM, &opening);
20249 return UP(pm_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous));
20250 }
20251 case PM_TOKEN_KEYWORD_FALSE:
20252 parser_lex(parser);
20253 return UP(pm_false_node_create(parser, &parser->previous));
20254 case PM_TOKEN_KEYWORD_FOR: {
20255 size_t opening_newline_index = token_newline_index(parser);
20256 parser_lex(parser);
20257
20258 pm_token_t for_keyword = parser->previous;
20259 pm_node_t *index;
20260
20261 context_push(parser, PM_CONTEXT_FOR_INDEX);
20262
20263 // First, parse out the first index expression.
20264 if (accept1(parser, PM_TOKEN_USTAR)) {
20265 index = parse_splat(parser, flags, depth);
20266 } else if (token_begins_expression_p(parser->current.type)) {
20267 index = parse_expression(parser, PM_BINDING_POWER_INDEX, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
20268 } else {
20269 pm_parser_err_token(parser, &for_keyword, PM_ERR_FOR_INDEX);
20270 index = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &for_keyword), PM_TOKEN_LENGTH(&for_keyword)));
20271 }
20272
20273 // Now, if there are multiple index expressions, parse them out.
20274 if (match1(parser, PM_TOKEN_COMMA)) {
20275 index = parse_targets(parser, index, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20276 } else {
20277 index = parse_target(parser, index, false, false);
20278 }
20279
20280 context_pop(parser);
20281 pm_do_loop_stack_push(parser, true);
20282
20283 expect1(parser, PM_TOKEN_KEYWORD_IN, PM_ERR_FOR_IN);
20284 pm_token_t in_keyword = parser->previous;
20285
20286 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));
20287 pm_do_loop_stack_pop(parser);
20288
20289 pm_token_t do_keyword = { 0 };
20290 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20291 do_keyword = parser->previous;
20292 } else {
20293 if (!match2(parser, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE)) {
20294 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_FOR_DELIMITER, pm_token_str(parser->current.type));
20295 }
20296 }
20297
20298 pm_statements_node_t *statements = NULL;
20299 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20300 statements = parse_statements(parser, PM_CONTEXT_FOR, (uint16_t) (depth + 1));
20301 }
20302
20303 parser_warn_indentation_mismatch(parser, opening_newline_index, &for_keyword, false, false);
20304 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_FOR_TERM, &for_keyword);
20305
20306 return UP(pm_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, NTOK2PTR(do_keyword), &parser->previous));
20307 }
20308 case PM_TOKEN_KEYWORD_IF:
20309 if (parser_end_of_line_p(parser)) {
20310 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_KEYWORD_EOL);
20311 }
20312
20313 size_t opening_newline_index = token_newline_index(parser);
20314 bool if_after_else = parser->previous.type == PM_TOKEN_KEYWORD_ELSE;
20315 parser_lex(parser);
20316
20317 return parse_conditional(parser, PM_CONTEXT_IF, opening_newline_index, if_after_else, (uint16_t) (depth + 1));
20318 case PM_TOKEN_KEYWORD_UNDEF: {
20319 if (binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
20320 pm_parser_err_current(parser, PM_ERR_STATEMENT_UNDEF);
20321 }
20322
20323 parser_lex(parser);
20324 pm_undef_node_t *undef = pm_undef_node_create(parser, &parser->previous);
20325 pm_node_t *name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20326
20327 if (PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20328 } else {
20329 pm_undef_node_append(parser->arena, undef, name);
20330
20331 while (match1(parser, PM_TOKEN_COMMA)) {
20332 lex_state_set(parser, PM_LEX_STATE_FNAME | PM_LEX_STATE_FITEM);
20333 parser_lex(parser);
20334 name = parse_undef_argument(parser, (uint16_t) (depth + 1));
20335
20336 if (PM_NODE_TYPE_P(name, PM_ERROR_RECOVERY_NODE)) {
20337 break;
20338 }
20339
20340 pm_undef_node_append(parser->arena, undef, name);
20341 }
20342 }
20343
20344 return UP(undef);
20345 }
20346 case PM_TOKEN_KEYWORD_NOT: {
20347 parser_lex(parser);
20348
20349 pm_token_t message = parser->previous;
20350 pm_arguments_t arguments = { 0 };
20351 pm_node_t *receiver = NULL;
20352
20353 // The `not` keyword without parentheses is only valid in contexts
20354 // where it would be parsed as an expression (i.e., at or below
20355 // the `not` binding power level). In other contexts (e.g., method
20356 // arguments, array elements, assignment right-hand sides),
20357 // parentheses are required: `not(x)`. An exception is made for
20358 // endless def bodies, where `not` is valid as both `arg` and
20359 // `command` (e.g., `def f = not 1`, `def f = not foo bar`).
20360 if (binding_power > PM_BINDING_POWER_NOT && !(flags & PM_PARSE_IN_ENDLESS_DEF) && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
20361 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) {
20362 pm_parser_err(parser, PM_TOKEN_END(parser, &parser->previous), 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN);
20363 } else {
20364 accept1(parser, PM_TOKEN_NEWLINE);
20365 pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER);
20366 }
20367
20368 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
20369 }
20370
20371 accept1(parser, PM_TOKEN_NEWLINE);
20372
20373 if (accept2(parser, PM_TOKEN_PARENTHESIS_LEFT, PM_TOKEN_PARENTHESIS_LEFT_GROUPING)) {
20374 pm_token_t lparen = parser->previous;
20375
20376 if (accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20377 receiver = UP(pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0));
20378 } else {
20379 arguments.opening_loc = TOK2LOC(parser, &lparen);
20380 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));
20381
20382 if (!parser->recovering) {
20383 accept1(parser, PM_TOKEN_NEWLINE);
20384 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20385 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
20386 }
20387 }
20388 } else {
20389 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));
20390 }
20391
20392 return UP(pm_call_node_not_create(parser, receiver, &message, &arguments));
20393 }
20394 case PM_TOKEN_KEYWORD_UNLESS: {
20395 size_t opening_newline_index = token_newline_index(parser);
20396 parser_lex(parser);
20397
20398 return parse_conditional(parser, PM_CONTEXT_UNLESS, opening_newline_index, false, (uint16_t) (depth + 1));
20399 }
20400 case PM_TOKEN_KEYWORD_MODULE:
20401 return parse_module(parser, flags, depth);
20402 case PM_TOKEN_KEYWORD_NIL:
20403 parser_lex(parser);
20404 return UP(pm_nil_node_create(parser, &parser->previous));
20405 case PM_TOKEN_KEYWORD_REDO: {
20406 parser_lex(parser);
20407
20408 pm_node_t *node = UP(pm_redo_node_create(parser, &parser->previous));
20409 if (!parser->partial_script) parse_block_exit(parser, node);
20410
20411 return node;
20412 }
20413 case PM_TOKEN_KEYWORD_RETRY: {
20414 parser_lex(parser);
20415
20416 pm_node_t *node = UP(pm_retry_node_create(parser, &parser->previous));
20417 parse_retry(parser, node);
20418
20419 return node;
20420 }
20421 case PM_TOKEN_KEYWORD_SELF:
20422 parser_lex(parser);
20423 return UP(pm_self_node_create(parser, &parser->previous));
20424 case PM_TOKEN_KEYWORD_TRUE:
20425 parser_lex(parser);
20426 return UP(pm_true_node_create(parser, &parser->previous));
20427 case PM_TOKEN_KEYWORD_UNTIL: {
20428 size_t opening_newline_index = token_newline_index(parser);
20429
20430 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
20431 pm_do_loop_stack_push(parser, true);
20432
20433 parser_lex(parser);
20434 pm_token_t keyword = parser->previous;
20435 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));
20436
20437 pm_do_loop_stack_pop(parser);
20438 context_pop(parser);
20439
20440 pm_token_t do_keyword = { 0 };
20441 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20442 do_keyword = parser->previous;
20443 } else {
20444 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_UNTIL_PREDICATE);
20445 }
20446
20447 pm_statements_node_t *statements = NULL;
20448 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20449 pm_accepts_block_stack_push(parser, true);
20450 statements = parse_statements(parser, PM_CONTEXT_UNTIL, (uint16_t) (depth + 1));
20451 pm_accepts_block_stack_pop(parser);
20452 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20453 }
20454
20455 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
20456 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_UNTIL_TERM, &keyword);
20457
20458 return UP(pm_until_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0));
20459 }
20460 case PM_TOKEN_KEYWORD_WHILE: {
20461 size_t opening_newline_index = token_newline_index(parser);
20462
20463 context_push(parser, PM_CONTEXT_LOOP_PREDICATE);
20464 pm_do_loop_stack_push(parser, true);
20465
20466 parser_lex(parser);
20467 pm_token_t keyword = parser->previous;
20468 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));
20469
20470 pm_do_loop_stack_pop(parser);
20471 context_pop(parser);
20472
20473 pm_token_t do_keyword = { 0 };
20474 if (accept1(parser, PM_TOKEN_KEYWORD_DO_LOOP)) {
20475 do_keyword = parser->previous;
20476 } else {
20477 expect2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON, PM_ERR_CONDITIONAL_WHILE_PREDICATE);
20478 }
20479
20480 pm_statements_node_t *statements = NULL;
20481 if (!match1(parser, PM_TOKEN_KEYWORD_END)) {
20482 pm_accepts_block_stack_push(parser, true);
20483 statements = parse_statements(parser, PM_CONTEXT_WHILE, (uint16_t) (depth + 1));
20484 pm_accepts_block_stack_pop(parser);
20485 accept2(parser, PM_TOKEN_NEWLINE, PM_TOKEN_SEMICOLON);
20486 }
20487
20488 parser_warn_indentation_mismatch(parser, opening_newline_index, &keyword, false, false);
20489 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_WHILE_TERM, &keyword);
20490
20491 return UP(pm_while_node_create(parser, &keyword, NTOK2PTR(do_keyword), &parser->previous, predicate, statements, 0));
20492 }
20493 case PM_TOKEN_PERCENT_LOWER_I: {
20494 parser_lex(parser);
20495 pm_token_t opening = parser->previous;
20496 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20497 pm_node_t *current = NULL;
20498
20499 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20500 accept1(parser, PM_TOKEN_WORDS_SEP);
20501 if (match1(parser, PM_TOKEN_STRING_END)) break;
20502
20503 // Interpolation is not possible but nested heredocs can still lead to
20504 // consecutive (disjoint) string tokens when the final newline is escaped.
20505 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20506 // Record the string node, moving to interpolation if needed.
20507 if (current == NULL) {
20508 current = UP(pm_symbol_node_create_current_string(parser, NULL, &parser->current, NULL));
20509 parser_lex(parser);
20510 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_SYMBOL_NODE)) {
20511 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20512 parser_lex(parser);
20513 pm_interpolated_symbol_node_append(parser->arena, (pm_interpolated_symbol_node_t *) current, string);
20514 } else if (PM_NODE_TYPE_P(current, PM_SYMBOL_NODE)) {
20515 pm_symbol_node_t *cast = (pm_symbol_node_t *) current;
20516 pm_token_t content = { .type = PM_TOKEN_STRING_CONTENT, .start = parser->start + cast->value_loc.start, .end = parser->start + cast->value_loc.start + cast->value_loc.length };
20517 pm_node_t *first_string = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &cast->unescaped));
20518 pm_node_t *second_string = UP(pm_string_node_create_current_string(parser, NULL, &parser->previous, NULL));
20519 parser_lex(parser);
20520
20521 pm_interpolated_symbol_node_t *interpolated = pm_interpolated_symbol_node_create(parser, NULL, NULL, NULL);
20522 pm_interpolated_symbol_node_append(parser->arena, interpolated, first_string);
20523 pm_interpolated_symbol_node_append(parser->arena, interpolated, second_string);
20524
20525 // current is arena-allocated so no explicit free is needed.
20526 current = UP(interpolated);
20527 } else {
20528 assert(false && "unreachable");
20529 }
20530 }
20531
20532 if (current) {
20533 pm_array_node_elements_append(parser->arena, array, current);
20534 current = NULL;
20535 } else {
20536 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_I_LOWER_ELEMENT);
20537 }
20538 }
20539
20540 pm_token_t closing = parser->current;
20541 if (match1(parser, PM_TOKEN_EOF)) {
20542 pm_parser_err_token(parser, &opening, PM_ERR_LIST_I_LOWER_TERM);
20543 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20544 } else {
20545 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_I_LOWER_TERM);
20546 }
20547 pm_array_node_close_set(parser, array, &closing);
20548
20549 return UP(array);
20550 }
20551 case PM_TOKEN_PERCENT_UPPER_I:
20552 return parse_symbol_array(parser, depth);
20553 case PM_TOKEN_PERCENT_LOWER_W: {
20554 parser_lex(parser);
20555 pm_token_t opening = parser->previous;
20556 pm_array_node_t *array = pm_array_node_create(parser, &opening);
20557 pm_node_t *current = NULL;
20558
20559 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20560 accept1(parser, PM_TOKEN_WORDS_SEP);
20561 if (match1(parser, PM_TOKEN_STRING_END)) break;
20562
20563 // Interpolation is not possible but nested heredocs can still lead to
20564 // consecutive (disjoint) string tokens when the final newline is escaped.
20565 while (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20566 pm_node_t *string = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
20567
20568 // Record the string node, moving to interpolation if needed.
20569 if (current == NULL) {
20570 current = string;
20571 } else if (PM_NODE_TYPE_P(current, PM_INTERPOLATED_STRING_NODE)) {
20572 pm_interpolated_string_node_append(parser, (pm_interpolated_string_node_t *) current, string);
20573 } else if (PM_NODE_TYPE_P(current, PM_STRING_NODE)) {
20574 pm_interpolated_string_node_t *interpolated = pm_interpolated_string_node_create(parser, NULL, NULL, NULL);
20575 pm_interpolated_string_node_append(parser, interpolated, current);
20576 pm_interpolated_string_node_append(parser, interpolated, string);
20577 current = UP(interpolated);
20578 } else {
20579 assert(false && "unreachable");
20580 }
20581 parser_lex(parser);
20582 }
20583
20584 if (current) {
20585 pm_array_node_elements_append(parser->arena, array, current);
20586 current = NULL;
20587 } else {
20588 expect1(parser, PM_TOKEN_STRING_CONTENT, PM_ERR_LIST_W_LOWER_ELEMENT);
20589 }
20590 }
20591
20592 pm_token_t closing = parser->current;
20593 if (match1(parser, PM_TOKEN_EOF)) {
20594 pm_parser_err_token(parser, &opening, PM_ERR_LIST_W_LOWER_TERM);
20595 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20596 } else {
20597 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_LIST_W_LOWER_TERM);
20598 }
20599
20600 pm_array_node_close_set(parser, array, &closing);
20601 return UP(array);
20602 }
20603 case PM_TOKEN_PERCENT_UPPER_W:
20604 return parse_string_array(parser, depth);
20605 case PM_TOKEN_REGEXP_BEGIN: {
20606 pm_token_t opening = parser->current;
20607 parser_lex(parser);
20608
20609 if (match1(parser, PM_TOKEN_REGEXP_END)) {
20610 // If we get here, then we have an end immediately after a start. In
20611 // that case we'll create an empty content token and return an
20612 // uninterpolated regular expression.
20613 pm_token_t content = (pm_token_t) {
20614 .type = PM_TOKEN_STRING_CONTENT,
20615 .start = parser->previous.end,
20616 .end = parser->previous.end
20617 };
20618
20619 parser_lex(parser);
20620
20621 pm_regular_expression_node_t *node = pm_regular_expression_node_create(parser, &opening, &content, &parser->previous);
20622 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
20623 return UP(node);
20624 }
20625
20627
20628 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20629 // In this case we've hit string content so we know the regular
20630 // expression at least has something in it. We'll need to check if the
20631 // following token is the end (in which case we can return a plain
20632 // regular expression) or if it's not then it has interpolation.
20633 pm_string_t unescaped = parser->current_string;
20634 pm_token_t content = parser->current;
20635 parser_lex(parser);
20636
20637 // If we hit an end, then we can create a regular expression
20638 // node without interpolation, which can be represented more
20639 // succinctly and more easily compiled.
20640 if (accept1(parser, PM_TOKEN_REGEXP_END)) {
20641 pm_regular_expression_node_t *node = (pm_regular_expression_node_t *) pm_regular_expression_node_create_unescaped(parser, &opening, &content, &parser->previous, &unescaped);
20642
20643 // If we're not immediately followed by a =~, then we
20644 // parse and validate now. If it is followed by a =~,
20645 // then it will get parsed in the =~ handler where
20646 // named captures can also be extracted.
20647 if (!match1(parser, PM_TOKEN_EQUAL_TILDE)) {
20648 pm_node_flag_set(UP(node), pm_regexp_parse(parser, node, NULL, NULL));
20649 }
20650
20651 return UP(node);
20652 }
20653
20654 // If we get here, then we have interpolation so we'll need to create
20655 // a regular expression node with interpolation.
20656 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
20657
20658 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
20659 if (parser->encoding == PM_ENCODING_US_ASCII_ENTRY) {
20660 // This is extremely strange, but the first string part of a
20661 // regular expression will always be tagged as binary if we
20662 // are in a US-ASCII file, no matter its contents.
20663 pm_node_flag_set(part, PM_STRING_FLAGS_FORCED_BINARY_ENCODING);
20664 }
20665
20666 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
20667 } else {
20668 // If the first part of the body of the regular expression is not a
20669 // string content, then we have interpolation and we need to create an
20670 // interpolated regular expression node.
20671 interpolated = pm_interpolated_regular_expression_node_create(parser, &opening);
20672 }
20673
20674 // Now that we're here and we have interpolation, we'll parse all of the
20675 // parts into the list.
20676 pm_node_t *part;
20677 while (!match2(parser, PM_TOKEN_REGEXP_END, PM_TOKEN_EOF)) {
20678 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
20679 pm_interpolated_regular_expression_node_append(parser->arena, interpolated, part);
20680 }
20681 }
20682
20683 pm_token_t closing = parser->current;
20684 if (match1(parser, PM_TOKEN_EOF)) {
20685 pm_parser_err_token(parser, &opening, PM_ERR_REGEXP_TERM);
20686 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20687 } else {
20688 expect1(parser, PM_TOKEN_REGEXP_END, PM_ERR_REGEXP_TERM);
20689 }
20690
20691 pm_interpolated_regular_expression_node_closing_set(parser, interpolated, &closing);
20692 return UP(interpolated);
20693 }
20694 case PM_TOKEN_XSTRING_BEGIN:
20695 case PM_TOKEN_PERCENT_LOWER_X: {
20696 parser_lex(parser);
20697 pm_token_t opening = parser->previous;
20698
20699 // When we get here, we don't know if this string is going to have
20700 // interpolation or not, even though it is allowed. Still, we want to be
20701 // able to return a string node without interpolation if we can since
20702 // it'll be faster.
20703 if (match1(parser, PM_TOKEN_STRING_END)) {
20704 // If we get here, then we have an end immediately after a start. In
20705 // that case we'll create an empty content token and return an
20706 // uninterpolated string.
20707 pm_token_t content = (pm_token_t) {
20708 .type = PM_TOKEN_STRING_CONTENT,
20709 .start = parser->previous.end,
20710 .end = parser->previous.end
20711 };
20712
20713 parser_lex(parser);
20714 return UP(pm_xstring_node_create(parser, &opening, &content, &parser->previous));
20715 }
20716
20718
20719 if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
20720 // In this case we've hit string content so we know the string
20721 // at least has something in it. We'll need to check if the
20722 // following token is the end (in which case we can return a
20723 // plain string) or if it's not then it has interpolation.
20724 pm_string_t unescaped = parser->current_string;
20725 pm_token_t content = parser->current;
20726 parser_lex(parser);
20727
20728 if (match1(parser, PM_TOKEN_STRING_END)) {
20729 pm_node_t *node = UP(pm_xstring_node_create_unescaped(parser, &opening, &content, &parser->current, &unescaped));
20730 pm_node_flag_set(node, parse_unescaped_encoding(parser, parser->explicit_encoding));
20731 parser_lex(parser);
20732 return node;
20733 }
20734
20735 // If we get here, then we have interpolation so we'll need to
20736 // create a string node with interpolation.
20737 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20738
20739 pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
20740 pm_node_flag_set(part, parse_unescaped_encoding(parser, parser->explicit_encoding));
20741
20742 pm_interpolated_xstring_node_append(parser->arena, node, part);
20743 } else {
20744 // If the first part of the body of the string is not a string
20745 // content, then we have interpolation and we need to create an
20746 // interpolated string node.
20747 node = pm_interpolated_xstring_node_create(parser, &opening, &opening);
20748 }
20749
20750 pm_node_t *part;
20751 while (!match2(parser, PM_TOKEN_STRING_END, PM_TOKEN_EOF)) {
20752 if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
20753 pm_interpolated_xstring_node_append(parser->arena, node, part);
20754 }
20755 }
20756
20757 pm_token_t closing = parser->current;
20758 if (match1(parser, PM_TOKEN_EOF)) {
20759 pm_parser_err_token(parser, &opening, PM_ERR_XSTRING_TERM);
20760 closing = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
20761 } else {
20762 expect1(parser, PM_TOKEN_STRING_END, PM_ERR_XSTRING_TERM);
20763 }
20764 pm_interpolated_xstring_node_closing_set(parser, node, &closing);
20765
20766 return UP(node);
20767 }
20768 case PM_TOKEN_USTAR: {
20769 parser_lex(parser);
20770
20771 // * operators at the beginning of expressions are only valid in the
20772 // context of a multiple assignment. We enforce that here. We'll
20773 // still lex past it though and create a missing node place.
20774 if (binding_power != PM_BINDING_POWER_STATEMENT) {
20775 pm_parser_err_prefix(parser, diag_id);
20776 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
20777 }
20778
20779 pm_node_t *splat = parse_splat(parser, flags, depth);
20780
20781 if (match1(parser, PM_TOKEN_COMMA)) {
20782 return parse_targets_validate(parser, splat, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
20783 } else {
20784 return parse_target_validate(parser, splat, true);
20785 }
20786 }
20787 case PM_TOKEN_BANG: {
20788 if (binding_power > PM_BINDING_POWER_UNARY) {
20789 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20790 }
20791
20792 parser_lex(parser);
20793
20794 pm_token_t operator = parser->previous;
20795 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));
20796 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "!");
20797
20798 pm_conditional_predicate(parser, receiver, PM_CONDITIONAL_PREDICATE_TYPE_NOT);
20799 return UP(node);
20800 }
20801 case PM_TOKEN_TILDE: {
20802 if (binding_power > PM_BINDING_POWER_UNARY) {
20803 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20804 }
20805 parser_lex(parser);
20806
20807 pm_token_t operator = parser->previous;
20808 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));
20809 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "~");
20810
20811 return UP(node);
20812 }
20813 case PM_TOKEN_UMINUS: {
20814 if (binding_power > PM_BINDING_POWER_UNARY) {
20815 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
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, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_UNARY_RECEIVER, (uint16_t) (depth + 1));
20821 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "-@");
20822
20823 return UP(node);
20824 }
20825 case PM_TOKEN_UMINUS_NUM: {
20826 parser_lex(parser);
20827
20828 pm_token_t operator = parser->previous;
20829 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));
20830
20831 if (accept1(parser, PM_TOKEN_STAR_STAR)) {
20832 pm_token_t exponent_operator = parser->previous;
20833 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));
20834 node = UP(pm_call_node_binary_create(parser, node, &exponent_operator, exponent, 0));
20835 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20836 } else {
20837 switch (PM_NODE_TYPE(node)) {
20838 case PM_INTEGER_NODE:
20839 case PM_FLOAT_NODE:
20840 case PM_RATIONAL_NODE:
20841 case PM_IMAGINARY_NODE:
20842 parse_negative_numeric(node);
20843 break;
20844 default:
20845 node = UP(pm_call_node_unary_create(parser, &operator, node, "-@"));
20846 break;
20847 }
20848 }
20849
20850 return node;
20851 }
20852 case PM_TOKEN_MINUS_GREATER: {
20853 int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting;
20854 parser->lambda_enclosure_nesting = parser->enclosure_nesting;
20855
20856 size_t opening_newline_index = token_newline_index(parser);
20857 parser_lex(parser);
20858
20859 pm_token_t operator = parser->previous;
20860 pm_parser_scope_push(parser, false);
20861
20862 pm_block_parameters_node_t *block_parameters;
20863
20864 switch (parser->current.type) {
20865 case PM_TOKEN_PARENTHESIS_LEFT: {
20866 pm_token_t opening = parser->current;
20867 parser_lex(parser);
20868
20869 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
20870 block_parameters = pm_block_parameters_node_create(parser, NULL, &opening);
20871 } else {
20872 block_parameters = parse_block_parameters(parser, false, &opening, true, true, (uint16_t) (depth + 1));
20873 }
20874
20875 accept1(parser, PM_TOKEN_NEWLINE);
20876 expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
20877
20878 pm_block_parameters_node_closing_set(parser, block_parameters, &parser->previous);
20879 break;
20880 }
20881 case PM_CASE_PARAMETER: {
20882 block_parameters = parse_block_parameters(parser, false, NULL, true, false, (uint16_t) (depth + 1));
20883 break;
20884 }
20885 default: {
20886 block_parameters = NULL;
20887 break;
20888 }
20889 }
20890
20891 pm_token_t opening;
20892 pm_node_t *body = NULL;
20893
20894 if (accept1(parser, PM_TOKEN_LAMBDA_BEGIN)) {
20895 opening = parser->previous;
20896
20897 if (!match1(parser, PM_TOKEN_BRACE_RIGHT)) {
20898 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_BRACES, (uint16_t) (depth + 1)));
20899 }
20900
20901 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
20902
20903 /* Restore the enclosing lambda's nesting now that the body has
20904 * been parsed, so that the token following the closing `}` is
20905 * lexed in the enclosing context. During the body the nesting
20906 * held this lambda's own level, which every token inside the
20907 * braces sits above. This mirrors parse.y restoring
20908 * `p->lex.lpar_beg` after `lambda_body`. */
20909 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
20910 expect1_opening(parser, PM_TOKEN_BRACE_RIGHT, PM_ERR_LAMBDA_TERM_BRACE, &opening);
20911 } else {
20912 /* A `-> { }` body is delimited by `{`/`}`, whose block-accepting
20913 * frame the lexer manages. A `-> do end` body is delimited by
20914 * keywords, so push the frame here and pop it before `end`. The
20915 * push must precede consuming the `do`, which lexes the first
20916 * token of the body; this matches parse.y's CMDARG_PUSH(0)
20917 * before `lambda_body`. */
20918 pm_accepts_block_stack_push(parser, true);
20919 expect1(parser, PM_TOKEN_KEYWORD_DO_LAMBDA, PM_ERR_LAMBDA_OPEN);
20920 opening = parser->previous;
20921
20922 /* The lexer cleared the nesting when it produced the `do`. If
20923 * it was missing entirely, clear it here so that the body is
20924 * recovered the same way it would have been parsed: no token
20925 * within it sits at the beginning of a lambda. */
20926 parser->lambda_enclosure_nesting = -1;
20927
20928 if (!match3(parser, PM_TOKEN_KEYWORD_END, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
20929 body = UP(parse_statements(parser, PM_CONTEXT_LAMBDA_DO_END, (uint16_t) (depth + 1)));
20930 }
20931
20932 if (match2(parser, PM_TOKEN_KEYWORD_RESCUE, PM_TOKEN_KEYWORD_ENSURE)) {
20933 assert(body == NULL || PM_NODE_TYPE_P(body, PM_STATEMENTS_NODE));
20934 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)));
20935 } else {
20936 parser_warn_indentation_mismatch(parser, opening_newline_index, &operator, false, false);
20937 }
20938
20939 pm_accepts_block_stack_pop(parser);
20940
20941 /* As with the brace branch above, restore the nesting before
20942 * consuming the closing `end`, which lexes the token that
20943 * follows it. */
20944 parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
20945 expect1_opening(parser, PM_TOKEN_KEYWORD_END, PM_ERR_LAMBDA_TERM_END, &operator);
20946 }
20947
20948 pm_constant_id_list_t locals;
20949 pm_locals_order(parser, &parser->current_scope->locals, &locals, pm_parser_scope_toplevel_p(parser));
20950 pm_node_t *parameters = parse_blocklike_parameters(parser, UP(block_parameters), &operator, &parser->previous);
20951
20952 pm_parser_scope_pop(parser);
20953
20954 return UP(pm_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, parameters, body));
20955 }
20956 case PM_TOKEN_UPLUS: {
20957 if (binding_power > PM_BINDING_POWER_UNARY) {
20958 pm_parser_err_prefix(parser, PM_ERR_UNARY_DISALLOWED);
20959 }
20960 parser_lex(parser);
20961
20962 pm_token_t operator = parser->previous;
20963 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));
20964 pm_call_node_t *node = pm_call_node_unary_create(parser, &operator, receiver, "+@");
20965
20966 return UP(node);
20967 }
20968 case PM_TOKEN_STRING_BEGIN:
20969 return parse_strings(parser, NULL, flags & PM_PARSE_ACCEPTS_LABEL, (uint16_t) (depth + 1));
20970 case PM_TOKEN_SYMBOL_BEGIN: {
20971 pm_lex_mode_t lex_mode = *parser->lex_modes.current;
20972 parser_lex(parser);
20973
20974 return parse_symbol(parser, &lex_mode, PM_LEX_STATE_END, (uint16_t) (depth + 1));
20975 }
20976 default: {
20977 pm_context_t recoverable = context_recoverable(parser, &parser->current);
20978
20979 if (recoverable != PM_CONTEXT_NONE) {
20980 parser->recovering = true;
20981
20982 // If the given error is not the generic one, then we'll add it
20983 // here because it will provide more context in addition to the
20984 // recoverable error that we will also add.
20985 if (diag_id != PM_ERR_CANNOT_PARSE_EXPRESSION) {
20986 pm_parser_err_prefix(parser, diag_id);
20987 }
20988
20989 // If we get here, then we are assuming this token is closing a
20990 // parent context, so we'll indicate that to the user so that
20991 // they know how we behaved.
20992 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT, pm_token_str(parser->current.type), context_human(recoverable));
20993 } else if (diag_id == PM_ERR_CANNOT_PARSE_EXPRESSION) {
20994 // We're going to make a special case here, because "cannot
20995 // parse expression" is pretty generic, and we know here that we
20996 // have an unexpected token.
20997 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_UNEXPECTED_TOKEN_IGNORE, pm_token_str(parser->current.type));
20998 } else {
20999 pm_parser_err_prefix(parser, diag_id);
21000 }
21001
21002 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->previous), PM_TOKEN_LENGTH(&parser->previous)));
21003 }
21004 }
21005}
21006
21007static pm_node_t *
21008parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth);
21009
21017static void
21018parse_rescue_modifier_terminator(pm_parser_t *parser, uint8_t flags, uint16_t depth) {
21019 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
21020 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21021 parser_lex(parser);
21022 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));
21023 }
21024}
21025
21035static pm_node_t *
21036parse_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) {
21037 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));
21038
21039 // Assignments whose value is a command call (e.g., a = b c) can only
21040 // be followed by modifiers (if/unless/while/until/rescue) and not by
21041 // operators with higher binding power. If we find one, emit an error
21042 // and skip the operator and its right-hand side.
21043 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER && (pm_command_call_value_p(parser, value) || pm_block_call_p(value))) {
21044 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21045 parser_lex(parser);
21046 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));
21047 }
21048
21049 // Contradicting binding powers, the right-hand-side value of the assignment
21050 // allows the `rescue` modifier.
21051 if (match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21052 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21053
21054 pm_token_t rescue = parser->current;
21055 parser_lex(parser);
21056
21057 // As in parse_assignment_values, the resbody is a `stmt` (permitting a
21058 // multiple assignment / command call) when the rescued value is itself a
21059 // command call, and a plain `arg` otherwise.
21060 bool statement_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21061 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21062
21063 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21064 context_pop(parser);
21065
21066 // A pattern-match resbody is a statement, but here the rescue is nested
21067 // in an assignment value where parse_expression_terminator cannot see
21068 // it, so reject a trailing operator above the modifier level directly.
21069 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21070 parse_rescue_modifier_terminator(parser, flags, depth);
21071 }
21072
21073 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21074 }
21075
21076 return value;
21077}
21078
21083static void
21084parse_assignment_value_local(pm_parser_t *parser, const pm_node_t *node) {
21085 switch (PM_NODE_TYPE(node)) {
21086 case PM_BEGIN_NODE: {
21087 const pm_begin_node_t *cast = (const pm_begin_node_t *) node;
21088 if (cast->statements != NULL) parse_assignment_value_local(parser, (const pm_node_t *) cast->statements);
21089 break;
21090 }
21091 case PM_LOCAL_VARIABLE_WRITE_NODE: {
21093 pm_locals_read(&pm_parser_scope_find(parser, cast->depth)->locals, cast->name);
21094 break;
21095 }
21096 case PM_PARENTHESES_NODE: {
21097 const pm_parentheses_node_t *cast = (const pm_parentheses_node_t *) node;
21098 if (cast->body != NULL) parse_assignment_value_local(parser, cast->body);
21099 break;
21100 }
21101 case PM_STATEMENTS_NODE: {
21102 const pm_statements_node_t *cast = (const pm_statements_node_t *) node;
21103 const pm_node_t *statement;
21104
21105 PM_NODE_LIST_FOREACH(&cast->body, index, statement) {
21106 parse_assignment_value_local(parser, statement);
21107 }
21108 break;
21109 }
21110 default:
21111 break;
21112 }
21113}
21114
21127static pm_node_t *
21128parse_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) {
21129 bool statement_level = (previous_binding_power == PM_BINDING_POWER_STATEMENT) || (flags & PM_PARSE_ACCEPTS_STATEMENT);
21130
21131 bool permitted = true;
21132 if (!statement_level && match1(parser, PM_TOKEN_USTAR)) permitted = false;
21133
21134 // A command call (e.g. `x = y z`) is permitted as the value when assigning
21135 // directly (carrying the caller's flag), or in any statement-level context
21136 // — which includes a rescue modifier value via the flag.
21137 uint8_t command_call_flag = (previous_binding_power == PM_BINDING_POWER_ASSIGNMENT)
21138 ? (uint8_t) (flags & PM_PARSE_ACCEPTS_COMMAND_CALL)
21139 : ((previous_binding_power < PM_BINDING_POWER_MODIFIER || statement_level) ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0);
21140
21141 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));
21142 if (!permitted) pm_parser_err_node(parser, value, PM_ERR_UNEXPECTED_MULTI_WRITE);
21143
21144 parse_assignment_value_local(parser, value);
21145 bool single_value = true;
21146
21147 // Block calls (command call + do block, e.g., `foo bar do end`) cannot
21148 // be followed by a comma to form a multi-value RHS because each element
21149 // of a multi-value assignment must be an `arg`, not a `block_call`.
21150 if (statement_level && !pm_block_call_p(value) && (PM_NODE_TYPE_P(value, PM_SPLAT_NODE) || match1(parser, PM_TOKEN_COMMA))) {
21151 single_value = false;
21152
21153 pm_array_node_t *array = pm_array_node_create(parser, NULL);
21154 pm_array_node_elements_append(parser->arena, array, value);
21155 value = UP(array);
21156
21157 while (accept1(parser, PM_TOKEN_COMMA)) {
21158 pm_node_t *element = parse_starred_expression(parser, binding_power, false, PM_ERR_ARRAY_ELEMENT, (uint16_t) (depth + 1));
21159
21160 pm_array_node_elements_append(parser->arena, array, element);
21161 if (PM_NODE_TYPE_P(element, PM_ERROR_RECOVERY_NODE)) break;
21162
21163 parse_assignment_value_local(parser, element);
21164 }
21165 }
21166
21167 // Assignments whose value is a command call (e.g., a = b c) can only
21168 // be followed by modifiers (if/unless/while/until/rescue) and not by
21169 // operators with higher binding power. If we find one, emit an error
21170 // and skip the operator and its right-hand side.
21171 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))) {
21172 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(parser->current.type));
21173 parser_lex(parser);
21174 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));
21175 }
21176
21177 // Contradicting binding powers, the right-hand-side value of the assignment
21178 // allows the `rescue` modifier.
21179 bool multiple_assignment = (binding_power == (PM_BINDING_POWER_MULTI_ASSIGNMENT + 1));
21180 if ((single_value || multiple_assignment) && match1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
21181 bool command_value = pm_command_call_value_p(parser, value) || pm_block_call_p(value);
21182
21183 // A multiple assignment whose value is a command call (`x, y = foo
21184 // bar`) is a complete statement (parse.y: `mlhs '='
21185 // command_call_value`, which has no rescue), so a trailing `rescue`
21186 // modifies the whole assignment rather than the value. Leave it for the
21187 // statement-level rescue instead of binding it to the value here. For a
21188 // non-command value the rescue does bind to the value (parse.y:
21189 // `mlhs '=' mrhs_arg modifier_rescue stmt`).
21190 if (multiple_assignment && command_value) return value;
21191
21192 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
21193
21194 pm_token_t rescue = parser->current;
21195 parser_lex(parser);
21196
21197 // The resbody is a `stmt` (parse.y: `command_rhs`/`mlhs '=' mrhs_arg`),
21198 // which permits a multiple assignment and a command call, when this is a
21199 // multiple assignment or the rescued value is itself a command call.
21200 // Otherwise it is a plain `arg` (parse.y: `arg_rhs`).
21201 bool statement_value = multiple_assignment || command_value;
21202 uint8_t rescue_flags = (uint8_t) ((flags & PM_PARSE_ACCEPTS_DO_BLOCK) | (statement_value ? PM_PARSE_ACCEPTS_COMMAND_CALL : 0));
21203
21204 pm_node_t *right = parse_rescue_modifier_value(parser, rescue_flags, statement_value, (uint16_t) (depth + 1));
21205 context_pop(parser);
21206
21207 // A pattern-match resbody is a statement, but here the rescue is nested
21208 // in an assignment value where parse_expression_terminator cannot see
21209 // it, so reject a trailing operator above the modifier level directly.
21210 if (PM_NODE_TYPE_P(right, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(right, PM_MATCH_PREDICATE_NODE)) {
21211 parse_rescue_modifier_terminator(parser, flags, depth);
21212 }
21213
21214 return UP(pm_rescue_modifier_node_create(parser, value, &rescue, right));
21215 }
21216
21217 return value;
21218}
21219
21232static pm_node_t *
21233parse_rescue_modifier_value(pm_parser_t *parser, uint8_t flags, bool statement, uint16_t depth) {
21234 if (statement) {
21235 pm_node_t *value;
21236 bool multiple;
21237
21238 if (match1(parser, PM_TOKEN_USTAR)) {
21239 // A leading splat can only begin a multiple assignment target list.
21240 parser_lex(parser);
21241 value = parse_splat(parser, flags, depth);
21242 multiple = true;
21243 } else {
21244 // The flag lets a single-target assignment take a multiple-value or
21245 // splat right-hand side (`b = c, d` / `b = *c`); a comma _before_ an
21246 // `=`, or a parenthesized target list (`(b, c), d = 1`), instead
21247 // promotes to a multiple assignment target list below.
21248 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));
21249 multiple = match1(parser, PM_TOKEN_COMMA) || PM_NODE_TYPE_P(value, PM_MULTI_TARGET_NODE);
21250 }
21251
21252 if (multiple) {
21253 pm_node_t *target = parse_targets_validate(parser, value, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
21254
21255 // A promoted target list is only a valid rescue value as part of a
21256 // complete `targets = values`. parse_targets_validate already
21257 // reports a missing `=` for every terminator except `)` (which it
21258 // permits for an enclosing mlhs paren that does not apply here), so
21259 // reject that case.
21260 if (!match1(parser, PM_TOKEN_EQUAL)) {
21261 if (match1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) pm_parser_err_node(parser, target, PM_ERR_WRITE_TARGET_UNEXPECTED);
21262 return target;
21263 }
21264
21265 pm_token_t operator = parser->current;
21266 parser_lex(parser);
21267
21268 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));
21269 value = parse_write(parser, target, &operator, values);
21270 }
21271
21272 // Reject a trailing operator that cannot follow a statement resbody.
21273 // Pattern-match handlers are statements too, but for the bare statement
21274 // form they are reported by parse_expression_terminator instead (which
21275 // keeps its existing error-recovery), so they are excluded here.
21276 if (!PM_NODE_TYPE_P(value, PM_MATCH_REQUIRED_NODE) && !PM_NODE_TYPE_P(value, PM_MATCH_PREDICATE_NODE)) {
21277 parse_rescue_modifier_terminator(parser, flags, depth);
21278 }
21279
21280 return value;
21281 }
21282
21283 // Otherwise the resbody is a plain `arg` (parse.y: `arg modifier_rescue
21284 // arg`), parsed above the `and`/`or`/`not` level so those stay outside it.
21285 return parse_expression(parser, PM_BINDING_POWER_DEFINED, flags, PM_ERR_RESCUE_MODIFIER_VALUE, (uint16_t) (depth + 1));
21286}
21287
21295static void
21296parse_call_operator_write(pm_parser_t *parser, pm_call_node_t *call_node, const pm_token_t *operator) {
21297 if (call_node->arguments != NULL) {
21298 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_ARGUMENTS);
21299 pm_node_unreference(parser, UP(call_node->arguments));
21300 call_node->arguments = NULL;
21301 }
21302
21303 if (call_node->block != NULL) {
21304 pm_parser_err_token(parser, operator, PM_ERR_OPERATOR_WRITE_BLOCK);
21305 pm_node_unreference(parser, UP(call_node->block));
21306 call_node->block = NULL;
21307 }
21308}
21309
21310static PRISM_INLINE const uint8_t *
21311pm_named_capture_escape_hex(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21312 cursor++;
21313
21314 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21315 uint8_t value = escape_hexadecimal_digit(*cursor);
21316 cursor++;
21317
21318 if (cursor < end && pm_char_is_hexadecimal_digit(*cursor)) {
21319 value = (uint8_t) ((value << 4) | escape_hexadecimal_digit(*cursor));
21320 cursor++;
21321 }
21322
21323 pm_buffer_append_byte(unescaped, value);
21324 } else {
21325 pm_buffer_append_string(unescaped, "\\x", 2);
21326 }
21327
21328 return cursor;
21329}
21330
21331static PRISM_INLINE const uint8_t *
21332pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) {
21333 uint8_t value = (uint8_t) (*cursor - '0');
21334 cursor++;
21335
21336 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21337 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21338 cursor++;
21339
21340 if (cursor < end && pm_char_is_octal_digit(*cursor)) {
21341 value = ((uint8_t) (value << 3)) | ((uint8_t) (*cursor - '0'));
21342 cursor++;
21343 }
21344 }
21345
21346 pm_buffer_append_byte(unescaped, value);
21347 return cursor;
21348}
21349
21350static PRISM_INLINE const uint8_t *
21351pm_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) {
21352 const uint8_t *start = cursor - 1;
21353 cursor++;
21354
21355 if (cursor >= end) {
21356 pm_buffer_append_string(unescaped, "\\u", 2);
21357 return cursor;
21358 }
21359
21360 if (*cursor != '{') {
21361 size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4));
21362 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21363
21364 if (!pm_buffer_append_unicode_codepoint(unescaped, value)) {
21365 pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start));
21366 }
21367
21368 return cursor + length;
21369 }
21370
21371 cursor++;
21372 for (;;) {
21373 while (cursor < end && *cursor == ' ') cursor++;
21374
21375 if (cursor >= end) break;
21376 if (*cursor == '}') {
21377 cursor++;
21378 break;
21379 }
21380
21381 size_t length = pm_strspn_hexadecimal_digit(cursor, end - cursor);
21382 if (length == 0) {
21383 break;
21384 }
21385 uint32_t value = escape_unicode(parser, cursor, length, error_location, 0);
21386
21387 (void) pm_buffer_append_unicode_codepoint(unescaped, value);
21388 cursor += length;
21389 }
21390
21391 return cursor;
21392}
21393
21394static void
21395pm_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) {
21396 const uint8_t *end = source + length;
21397 pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source));
21398
21399 for (;;) {
21400 if (++cursor >= end) {
21401 pm_buffer_append_byte(unescaped, '\\');
21402 return;
21403 }
21404
21405 switch (*cursor) {
21406 case 'x':
21407 cursor = pm_named_capture_escape_hex(unescaped, cursor, end);
21408 break;
21409 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
21410 cursor = pm_named_capture_escape_octal(unescaped, cursor, end);
21411 break;
21412 case 'u':
21413 cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location);
21414 break;
21415 default:
21416 pm_buffer_append_byte(unescaped, '\\');
21417 break;
21418 }
21419
21420 const uint8_t *next_cursor = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, parser->encoding);
21421 if (next_cursor == NULL) break;
21422
21423 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (next_cursor - cursor));
21424 cursor = next_cursor;
21425 }
21426
21427 pm_buffer_append_string(unescaped, (const char *) cursor, (size_t) (end - cursor));
21428}
21429
21434static void
21435parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *capture, bool shared, pm_regexp_name_data_t *callback_data) {
21436 pm_call_node_t *call = callback_data->call;
21437 pm_constant_id_list_t *names = &callback_data->names;
21438
21439 const uint8_t *source = pm_string_source(capture);
21440 size_t length = pm_string_length(capture);
21441 pm_buffer_t unescaped = { 0 };
21442
21443 // First, we need to handle escapes within the name of the capture group.
21444 // This is because regular expressions have three different representations
21445 // in prism. The first is the plain source code. The second is the
21446 // representation that will be sent to the regular expression engine, which
21447 // is the value of the "unescaped" field. This is poorly named, because it
21448 // actually still contains escapes, just a subset of them that the regular
21449 // expression engine knows how to handle. The third representation is fully
21450 // unescaped, which is what we need.
21451 const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding);
21452 if (PRISM_UNLIKELY(cursor != NULL)) {
21453 pm_named_capture_escape(parser, &unescaped, source, length, cursor, shared ? NULL : &call->receiver->location);
21454 source = (const uint8_t *) pm_buffer_value(&unescaped);
21455 length = pm_buffer_length(&unescaped);
21456 }
21457
21458 const uint8_t *start;
21459 const uint8_t *end;
21460 pm_constant_id_t name;
21461
21462 // If the name of the capture group isn't a valid identifier, we do
21463 // not add it to the local table.
21464 if (!pm_slice_is_valid_local(parser, source, source + length)) {
21465 pm_buffer_cleanup(&unescaped);
21466 return;
21467 }
21468
21469 if (shared) {
21470 // If the unescaped string is a slice of the source, then we can
21471 // copy the names directly. The pointers will line up.
21472 start = source;
21473 end = source + length;
21474 name = pm_parser_constant_id_raw(parser, start, end);
21475 } else {
21476 // Otherwise, the name is a slice of the malloc-ed owned string,
21477 // in which case we need to copy it out into a new string.
21478 start = parser->start + PM_NODE_START(call->receiver);
21479 end = parser->start + PM_NODE_END(call->receiver);
21480
21481 uint8_t *memory = (uint8_t *) pm_arena_alloc(parser->arena, length, 1);
21482 memcpy(memory, source, length);
21483 name = pm_parser_constant_id_owned(parser, memory, length);
21484 }
21485
21486 // Add this name to the list of constants if it is valid, not duplicated,
21487 // and not a keyword.
21488 if (name != 0 && !pm_constant_id_list_includes(names, name)) {
21489 pm_constant_id_list_append(parser->arena, names, name);
21490
21491 int depth;
21492 if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
21493 // If the local is not already a local but it is a keyword, then we
21494 // do not want to add a capture for this.
21495 if (pm_local_is_keyword((const char *) source, length)) {
21496 pm_buffer_cleanup(&unescaped);
21497 return;
21498 }
21499
21500 // If the identifier is not already a local, then we will add it to
21501 // the local table.
21502 pm_parser_local_add(parser, name, start, end, 0);
21503 }
21504
21505 // Here we lazily create the MatchWriteNode since we know we're
21506 // about to add a target.
21507 if (callback_data->match == NULL) {
21508 callback_data->match = pm_match_write_node_create(parser, call);
21509 }
21510
21511 // Next, create the local variable target and add it to the list of
21512 // targets for the match.
21513 pm_token_t token = { .type = 0, .start = start, .end = end };
21514 pm_location_t token_loc = TOK2LOC(parser, &token);
21515 pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &token_loc, name, depth == -1 ? 0 : (uint32_t) depth));
21516 pm_node_list_append(parser->arena, &callback_data->match->targets, target);
21517 }
21518
21519 pm_buffer_cleanup(&unescaped);
21520}
21521
21527static pm_node_t *
21528parse_interpolated_regular_expression_named_captures(pm_parser_t *parser, const pm_string_t *content, pm_call_node_t *call, bool extended_mode) {
21529 pm_regexp_name_data_t callback_data = {
21530 .call = call,
21531 .match = NULL,
21532 .names = { 0 },
21533 };
21534
21535 pm_regexp_parse_named_captures(parser, pm_string_source(content), pm_string_length(content), false, extended_mode, parse_regular_expression_named_capture, &callback_data);
21536
21537 if (callback_data.match != NULL) {
21538 return UP(callback_data.match);
21539 } else {
21540 return UP(call);
21541 }
21542}
21543
21544static PRISM_INLINE pm_node_t *
21545parse_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) {
21546 pm_token_t token = parser->current;
21547
21548 switch (token.type) {
21549 case PM_TOKEN_EQUAL: {
21550 switch (PM_NODE_TYPE(node)) {
21551 case PM_CALL_NODE: {
21552 // If we have no arguments to the call node and we need this
21553 // to be a target then this is either a method call or a
21554 // local variable write. This _must_ happen before the value
21555 // is parsed because it could be referenced in the value.
21556 pm_call_node_t *call_node = (pm_call_node_t *) node;
21557 if (PM_NODE_FLAG_P(call_node, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21558 pm_parser_local_add_location(parser, &call_node->message_loc, 0);
21559 }
21560 }
21562 case PM_CASE_WRITABLE: {
21563 // When we have `it = value`, we need to add `it` as a local
21564 // variable before parsing the value, in case the value
21565 // references the variable.
21566 if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
21567 pm_parser_local_add_location(parser, &node->location, 0);
21568 }
21569
21570 parser_lex(parser);
21571 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));
21572
21573 if (PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) && previous_binding_power != PM_BINDING_POWER_STATEMENT && !(flags & PM_PARSE_ACCEPTS_STATEMENT)) {
21574 pm_parser_err_node(parser, node, PM_ERR_UNEXPECTED_MULTI_WRITE);
21575 }
21576
21577 return parse_write(parser, node, &token, value);
21578 }
21579 case PM_SPLAT_NODE: {
21580 pm_multi_target_node_t *multi_target = pm_multi_target_node_create(parser);
21581 pm_multi_target_node_targets_append(parser, multi_target, node);
21582
21583 parser_lex(parser);
21584 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));
21585 return parse_write(parser, UP(multi_target), &token, value);
21586 }
21587 case PM_SOURCE_ENCODING_NODE:
21588 case PM_FALSE_NODE:
21589 case PM_SOURCE_FILE_NODE:
21590 case PM_SOURCE_LINE_NODE:
21591 case PM_NIL_NODE:
21592 case PM_SELF_NODE:
21593 case PM_TRUE_NODE: {
21594 // In these special cases, we have specific error messages
21595 // and we will replace them with local variable writes.
21596 parser_lex(parser);
21597 pm_node_t *value = parse_assignment_values(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
21598 return parse_unwriteable_write(parser, node, &token, value);
21599 }
21600 default:
21601 // In this case we have an = sign, but we don't know what
21602 // it's for. We need to treat it as an error. We'll mark it
21603 // as an error and skip past it.
21604 parser_lex(parser);
21605 pm_parser_err_token(parser, &token, PM_ERR_EXPRESSION_NOT_WRITABLE);
21606 return node;
21607 }
21608 }
21609 case PM_TOKEN_AMPERSAND_AMPERSAND_EQUAL: {
21610 switch (PM_NODE_TYPE(node)) {
21611 case PM_BACK_REFERENCE_READ_NODE:
21612 case PM_NUMBERED_REFERENCE_READ_NODE:
21613 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21615 case PM_GLOBAL_VARIABLE_READ_NODE: {
21616 parser_lex(parser);
21617
21618 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21619 pm_node_t *result = UP(pm_global_variable_and_write_node_create(parser, node, &token, value));
21620
21621 return result;
21622 }
21623 case PM_CLASS_VARIABLE_READ_NODE: {
21624 parser_lex(parser);
21625
21626 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21627 pm_node_t *result = UP(pm_class_variable_and_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21628
21629 return result;
21630 }
21631 case PM_CONSTANT_PATH_NODE: {
21632 parser_lex(parser);
21633
21634 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21635 pm_node_t *write = UP(pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21636
21637 return parse_shareable_constant_write(parser, write);
21638 }
21639 case PM_CONSTANT_READ_NODE: {
21640 parser_lex(parser);
21641
21642 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21643 pm_node_t *write = UP(pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21644
21645 if (context_def_p(parser)) {
21646 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21647 }
21648
21649 return parse_shareable_constant_write(parser, write);
21650 }
21651 case PM_INSTANCE_VARIABLE_READ_NODE: {
21652 parser_lex(parser);
21653
21654 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21655 pm_node_t *result = UP(pm_instance_variable_and_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21656
21657 return result;
21658 }
21659 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21660 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21661 parser_lex(parser);
21662
21663 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21664 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, name, 0));
21665
21666 pm_node_unreference(parser, node);
21667 return result;
21668 }
21669 case PM_LOCAL_VARIABLE_READ_NODE: {
21670 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21671 PM_PARSER_ERR_FORMAT(parser, node->location.start, node->location.length, PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + node->location.start);
21672 pm_node_unreference(parser, node);
21673 }
21674
21676 parser_lex(parser);
21677
21678 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21679 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21680
21681 return result;
21682 }
21683 case PM_CALL_NODE: {
21684 pm_call_node_t *cast = (pm_call_node_t *) node;
21685
21686 // If we have a vcall (a method with no arguments and no
21687 // receiver that could have been a local variable) then we
21688 // will transform it into a local variable write.
21689 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21690 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
21691 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
21692 parser_lex(parser);
21693
21694 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21695 pm_node_t *result = UP(pm_local_variable_and_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21696
21697 return result;
21698 }
21699
21700 // Move past the token here so that we have already added
21701 // the local variable by this point.
21702 parser_lex(parser);
21703
21704 // If there is no call operator and the message is "[]" then
21705 // this is an aref expression, and we can transform it into
21706 // an aset expression.
21707 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
21708 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21709 return UP(pm_index_and_write_node_create(parser, cast, &token, value));
21710 }
21711
21712 // If this node cannot be writable, then we have an error.
21713 if (pm_call_node_writable_p(parser, cast)) {
21714 parse_write_name(parser, &cast->name);
21715 } else {
21716 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
21717 }
21718
21719 parse_call_operator_write(parser, cast, &token);
21720 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ, (uint16_t) (depth + 1));
21721 return UP(pm_call_and_write_node_create(parser, cast, &token, value));
21722 }
21723 case PM_MULTI_WRITE_NODE: {
21724 parser_lex(parser);
21725 pm_parser_err_token(parser, &token, PM_ERR_AMPAMPEQ_MULTI_ASSIGN);
21726 return node;
21727 }
21728 default:
21729 parser_lex(parser);
21730
21731 // In this case we have an &&= sign, but we don't know what it's for.
21732 // We need to treat it as an error. For now, we'll mark it as an error
21733 // and just skip right past it.
21734 pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
21735 return node;
21736 }
21737 }
21738 case PM_TOKEN_PIPE_PIPE_EQUAL: {
21739 switch (PM_NODE_TYPE(node)) {
21740 case PM_BACK_REFERENCE_READ_NODE:
21741 case PM_NUMBERED_REFERENCE_READ_NODE:
21742 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21744 case PM_GLOBAL_VARIABLE_READ_NODE: {
21745 parser_lex(parser);
21746
21747 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21748 pm_node_t *result = UP(pm_global_variable_or_write_node_create(parser, node, &token, value));
21749
21750 return result;
21751 }
21752 case PM_CLASS_VARIABLE_READ_NODE: {
21753 parser_lex(parser);
21754
21755 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21756 pm_node_t *result = UP(pm_class_variable_or_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21757
21758 return result;
21759 }
21760 case PM_CONSTANT_PATH_NODE: {
21761 parser_lex(parser);
21762
21763 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21764 pm_node_t *write = UP(pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21765
21766 return parse_shareable_constant_write(parser, write);
21767 }
21768 case PM_CONSTANT_READ_NODE: {
21769 parser_lex(parser);
21770
21771 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21772 pm_node_t *write = UP(pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21773
21774 if (context_def_p(parser)) {
21775 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21776 }
21777
21778 return parse_shareable_constant_write(parser, write);
21779 }
21780 case PM_INSTANCE_VARIABLE_READ_NODE: {
21781 parser_lex(parser);
21782
21783 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21784 pm_node_t *result = UP(pm_instance_variable_or_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21785
21786 return result;
21787 }
21788 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21789 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21790 parser_lex(parser);
21791
21792 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21793 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, name, 0));
21794
21795 pm_node_unreference(parser, node);
21796 return result;
21797 }
21798 case PM_LOCAL_VARIABLE_READ_NODE: {
21799 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21800 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
21801 pm_node_unreference(parser, node);
21802 }
21803
21805 parser_lex(parser);
21806
21807 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21808 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21809
21810 return result;
21811 }
21812 case PM_CALL_NODE: {
21813 pm_call_node_t *cast = (pm_call_node_t *) node;
21814
21815 // If we have a vcall (a method with no arguments and no
21816 // receiver that could have been a local variable) then we
21817 // will transform it into a local variable write.
21818 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21819 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
21820 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
21821 parser_lex(parser);
21822
21823 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21824 pm_node_t *result = UP(pm_local_variable_or_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21825
21826 return result;
21827 }
21828
21829 // Move past the token here so that we have already added
21830 // the local variable by this point.
21831 parser_lex(parser);
21832
21833 // If there is no call operator and the message is "[]" then
21834 // this is an aref expression, and we can transform it into
21835 // an aset expression.
21836 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
21837 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21838 return UP(pm_index_or_write_node_create(parser, cast, &token, value));
21839 }
21840
21841 // If this node cannot be writable, then we have an error.
21842 if (pm_call_node_writable_p(parser, cast)) {
21843 parse_write_name(parser, &cast->name);
21844 } else {
21845 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
21846 }
21847
21848 parse_call_operator_write(parser, cast, &token);
21849 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ, (uint16_t) (depth + 1));
21850 return UP(pm_call_or_write_node_create(parser, cast, &token, value));
21851 }
21852 case PM_MULTI_WRITE_NODE: {
21853 parser_lex(parser);
21854 pm_parser_err_token(parser, &token, PM_ERR_PIPEPIPEEQ_MULTI_ASSIGN);
21855 return node;
21856 }
21857 default:
21858 parser_lex(parser);
21859
21860 // In this case we have an ||= sign, but we don't know what it's for.
21861 // We need to treat it as an error. For now, we'll mark it as an error
21862 // and just skip right past it.
21863 pm_parser_err_token(parser, &token, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
21864 return node;
21865 }
21866 }
21867 case PM_TOKEN_AMPERSAND_EQUAL:
21868 case PM_TOKEN_CARET_EQUAL:
21869 case PM_TOKEN_GREATER_GREATER_EQUAL:
21870 case PM_TOKEN_LESS_LESS_EQUAL:
21871 case PM_TOKEN_MINUS_EQUAL:
21872 case PM_TOKEN_PERCENT_EQUAL:
21873 case PM_TOKEN_PIPE_EQUAL:
21874 case PM_TOKEN_PLUS_EQUAL:
21875 case PM_TOKEN_SLASH_EQUAL:
21876 case PM_TOKEN_STAR_EQUAL:
21877 case PM_TOKEN_STAR_STAR_EQUAL: {
21878 switch (PM_NODE_TYPE(node)) {
21879 case PM_BACK_REFERENCE_READ_NODE:
21880 case PM_NUMBERED_REFERENCE_READ_NODE:
21881 PM_PARSER_ERR_NODE_FORMAT_CONTENT(parser, node, PM_ERR_WRITE_TARGET_READONLY);
21883 case PM_GLOBAL_VARIABLE_READ_NODE: {
21884 parser_lex(parser);
21885
21886 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21887 pm_node_t *result = UP(pm_global_variable_operator_write_node_create(parser, node, &token, value));
21888
21889 return result;
21890 }
21891 case PM_CLASS_VARIABLE_READ_NODE: {
21892 parser_lex(parser);
21893
21894 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21895 pm_node_t *result = UP(pm_class_variable_operator_write_node_create(parser, (pm_class_variable_read_node_t *) node, &token, value));
21896
21897 return result;
21898 }
21899 case PM_CONSTANT_PATH_NODE: {
21900 parser_lex(parser);
21901
21902 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21903 pm_node_t *write = UP(pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value));
21904
21905 return parse_shareable_constant_write(parser, write);
21906 }
21907 case PM_CONSTANT_READ_NODE: {
21908 parser_lex(parser);
21909
21910 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21911 pm_node_t *write = UP(pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value));
21912
21913 if (context_def_p(parser)) {
21914 pm_parser_err_node(parser, write, PM_ERR_WRITE_TARGET_IN_METHOD);
21915 }
21916
21917 return parse_shareable_constant_write(parser, write);
21918 }
21919 case PM_INSTANCE_VARIABLE_READ_NODE: {
21920 parser_lex(parser);
21921
21922 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21923 pm_node_t *result = UP(pm_instance_variable_operator_write_node_create(parser, (pm_instance_variable_read_node_t *) node, &token, value));
21924
21925 return result;
21926 }
21927 case PM_IT_LOCAL_VARIABLE_READ_NODE: {
21928 pm_constant_id_t name = pm_parser_local_add_constant(parser, "it", 2);
21929 parser_lex(parser);
21930
21931 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21932 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, name, 0));
21933
21934 pm_node_unreference(parser, node);
21935 return result;
21936 }
21937 case PM_LOCAL_VARIABLE_READ_NODE: {
21938 if (pm_token_is_numbered_parameter(parser, PM_NODE_START(node), PM_NODE_LENGTH(node))) {
21939 PM_PARSER_ERR_FORMAT(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PARAMETER_NUMBERED_RESERVED, parser->start + PM_NODE_START(node));
21940 pm_node_unreference(parser, node);
21941 }
21942
21944 parser_lex(parser);
21945
21946 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21947 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, node, &token, value, cast->name, cast->depth));
21948
21949 return result;
21950 }
21951 case PM_CALL_NODE: {
21952 parser_lex(parser);
21953 pm_call_node_t *cast = (pm_call_node_t *) node;
21954
21955 // If we have a vcall (a method with no arguments and no
21956 // receiver that could have been a local variable) then we
21957 // will transform it into a local variable write.
21958 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
21959 pm_refute_numbered_parameter(parser, cast->message_loc.start, cast->message_loc.length);
21960 pm_constant_id_t constant_id = pm_parser_local_add_location(parser, &cast->message_loc, 1);
21961 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21962 pm_node_t *result = UP(pm_local_variable_operator_write_node_create(parser, UP(cast), &token, value, constant_id, 0));
21963
21964 return result;
21965 }
21966
21967 // If there is no call operator and the message is "[]" then
21968 // this is an aref expression, and we can transform it into
21969 // an aset expression.
21970 if (PM_NODE_FLAG_P(cast, PM_CALL_NODE_FLAGS_INDEX)) {
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 return UP(pm_index_operator_write_node_create(parser, cast, &token, value));
21973 }
21974
21975 // If this node cannot be writable, then we have an error.
21976 if (pm_call_node_writable_p(parser, cast)) {
21977 parse_write_name(parser, &cast->name);
21978 } else {
21979 pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_UNEXPECTED);
21980 }
21981
21982 parse_call_operator_write(parser, cast, &token);
21983 pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, flags, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
21984 return UP(pm_call_operator_write_node_create(parser, cast, &token, value));
21985 }
21986 case PM_MULTI_WRITE_NODE: {
21987 parser_lex(parser);
21988 pm_parser_err_token(parser, &token, PM_ERR_OPERATOR_MULTI_ASSIGN);
21989 return node;
21990 }
21991 default:
21992 parser_lex(parser);
21993
21994 // In this case we have an operator but we don't know what it's for.
21995 // We need to treat it as an error. For now, we'll mark it as an error
21996 // and just skip right past it.
21997 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->previous, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, pm_token_str(parser->current.type));
21998 return node;
21999 }
22000 }
22001 case PM_TOKEN_AMPERSAND_AMPERSAND:
22002 case PM_TOKEN_KEYWORD_AND: {
22003 parser_lex(parser);
22004
22005 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));
22006 return UP(pm_and_node_create(parser, node, &token, right));
22007 }
22008 case PM_TOKEN_KEYWORD_OR:
22009 case PM_TOKEN_PIPE_PIPE: {
22010 parser_lex(parser);
22011
22012 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));
22013 return UP(pm_or_node_create(parser, node, &token, right));
22014 }
22015 case PM_TOKEN_EQUAL_TILDE: {
22016 // Note that we _must_ parse the value before adding the local
22017 // variables in order to properly mirror the behavior of Ruby. For
22018 // example,
22019 //
22020 // /(?<foo>bar)/ =~ foo
22021 //
22022 // In this case, `foo` should be a method call and not a local yet.
22023 parser_lex(parser);
22024 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));
22025
22026 // By default, we're going to create a call node and then return it.
22027 pm_call_node_t *call = pm_call_node_binary_create(parser, node, &token, argument, 0);
22028 pm_node_t *result = UP(call);
22029
22030 // If the receiver of this =~ is a regular expression node, then we
22031 // need to introduce local variables for it based on its named
22032 // capture groups.
22033 if (PM_NODE_TYPE_P(node, PM_INTERPOLATED_REGULAR_EXPRESSION_NODE)) {
22034 // It's possible to have an interpolated regular expression node
22035 // that only contains strings. This is because it can be split
22036 // up by a heredoc. In this case we need to concat the unescaped
22037 // strings together and then parse them as a regular expression.
22039
22040 bool interpolated = false;
22041 size_t total_length = 0;
22042
22043 pm_node_t *part;
22044 PM_NODE_LIST_FOREACH(parts, index, part) {
22045 if (PM_NODE_TYPE_P(part, PM_STRING_NODE)) {
22046 total_length += pm_string_length(&((pm_string_node_t *) part)->unescaped);
22047 } else {
22048 interpolated = true;
22049 break;
22050 }
22051 }
22052
22053 if (!interpolated && total_length > 0) {
22054 void *memory = xmalloc(total_length);
22055 if (!memory) abort();
22056
22057 uint8_t *cursor = memory;
22058 PM_NODE_LIST_FOREACH(parts, index, part) {
22059 pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
22060 size_t length = pm_string_length(unescaped);
22061
22062 memcpy(cursor, pm_string_source(unescaped), length);
22063 cursor += length;
22064 }
22065
22066 pm_string_t owned;
22067 pm_string_owned_init(&owned, (uint8_t *) memory, total_length);
22068
22069 result = parse_interpolated_regular_expression_named_captures(parser, &owned, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
22070 pm_string_cleanup(&owned);
22071 }
22072 } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
22073 // If we have a regular expression node, then we can parse
22074 // the named captures and validate encoding in one pass.
22076
22077 pm_regexp_name_data_t name_data = {
22078 .call = call,
22079 .match = NULL,
22080 .names = { 0 },
22081 };
22082
22083 pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data));
22084
22085 if (name_data.match != NULL) {
22086 result = UP(name_data.match);
22087 }
22088 }
22089
22090 return result;
22091 }
22092 case PM_TOKEN_UAMPERSAND:
22093 case PM_TOKEN_USTAR:
22094 case PM_TOKEN_USTAR_STAR:
22095 // The only times this will occur are when we are in an error state,
22096 // but we'll put them in here so that errors can propagate.
22097 case PM_TOKEN_BANG_EQUAL:
22098 case PM_TOKEN_BANG_TILDE:
22099 case PM_TOKEN_EQUAL_EQUAL:
22100 case PM_TOKEN_EQUAL_EQUAL_EQUAL:
22101 case PM_TOKEN_LESS_EQUAL_GREATER:
22102 case PM_TOKEN_CARET:
22103 case PM_TOKEN_PIPE:
22104 case PM_TOKEN_AMPERSAND:
22105 case PM_TOKEN_GREATER_GREATER:
22106 case PM_TOKEN_LESS_LESS:
22107 case PM_TOKEN_MINUS:
22108 case PM_TOKEN_PLUS:
22109 case PM_TOKEN_PERCENT:
22110 case PM_TOKEN_SLASH:
22111 case PM_TOKEN_STAR:
22112 case PM_TOKEN_STAR_STAR: {
22113 parser_lex(parser);
22114 pm_token_t operator = parser->previous;
22115 switch (PM_NODE_TYPE(node)) {
22116 case PM_RESCUE_MODIFIER_NODE: {
22118 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22119 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22120 }
22121 break;
22122 }
22123 case PM_AND_NODE: {
22124 pm_and_node_t *cast = (pm_and_node_t *) node;
22125 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22126 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22127 }
22128 break;
22129 }
22130 case PM_OR_NODE: {
22131 pm_or_node_t *cast = (pm_or_node_t *) node;
22132 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22133 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22134 }
22135 break;
22136 }
22137 default:
22138 break;
22139 }
22140
22141 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));
22142 return UP(pm_call_node_binary_create(parser, node, &token, argument, 0));
22143 }
22144 case PM_TOKEN_GREATER:
22145 case PM_TOKEN_GREATER_EQUAL:
22146 case PM_TOKEN_LESS:
22147 case PM_TOKEN_LESS_EQUAL: {
22148 if (PM_NODE_TYPE_P(node, PM_CALL_NODE) && PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_COMPARISON)) {
22149 PM_PARSER_WARN_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_WARN_COMPARISON_AFTER_COMPARISON);
22150 }
22151
22152 parser_lex(parser);
22153 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));
22154 return UP(pm_call_node_binary_create(parser, node, &token, argument, PM_CALL_NODE_FLAGS_COMPARISON));
22155 }
22156 case PM_TOKEN_AMPERSAND_DOT:
22157 case PM_TOKEN_DOT: {
22158 parser_lex(parser);
22159 pm_token_t operator = parser->previous;
22160 pm_arguments_t arguments = { 0 };
22161
22162 // This if statement handles the foo.() syntax.
22163 if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
22164 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
22165 return UP(pm_call_node_shorthand_create(parser, node, &operator, &arguments));
22166 }
22167
22168 switch (PM_NODE_TYPE(node)) {
22169 case PM_RESCUE_MODIFIER_NODE: {
22171 if (PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->rescue_expression, PM_MATCH_REQUIRED_NODE)) {
22172 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22173 }
22174 break;
22175 }
22176 case PM_AND_NODE: {
22177 pm_and_node_t *cast = (pm_and_node_t *) node;
22178 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22179 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22180 }
22181 break;
22182 }
22183 case PM_OR_NODE: {
22184 pm_or_node_t *cast = (pm_or_node_t *) node;
22185 if (PM_NODE_TYPE_P(cast->right, PM_MATCH_PREDICATE_NODE) || PM_NODE_TYPE_P(cast->right, PM_MATCH_REQUIRED_NODE)) {
22186 PM_PARSER_ERR_TOKEN_FORMAT(parser, &operator, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_str(operator.type));
22187 }
22188 break;
22189 }
22190 default:
22191 break;
22192 }
22193
22194 pm_token_t message;
22195
22196 switch (parser->current.type) {
22197 case PM_CASE_OPERATOR:
22198 case PM_CASE_KEYWORD:
22199 case PM_TOKEN_CONSTANT:
22200 case PM_TOKEN_IDENTIFIER:
22201 case PM_TOKEN_METHOD_NAME: {
22202 parser_lex(parser);
22203 message = parser->previous;
22204 break;
22205 }
22206 default: {
22207 PM_PARSER_ERR_TOKEN_FORMAT(parser, &parser->current, PM_ERR_EXPECT_MESSAGE, pm_token_str(parser->current.type));
22208 message = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
22209 }
22210 }
22211
22212 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22213 pm_call_node_t *call = pm_call_node_call_create(parser, node, &operator, &message, &arguments);
22214
22215 if (
22216 (previous_binding_power == PM_BINDING_POWER_STATEMENT) &&
22217 arguments.arguments == NULL &&
22218 arguments.opening_loc.length == 0 &&
22219 match1(parser, PM_TOKEN_COMMA)
22220 ) {
22221 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22222 } else {
22223 return UP(call);
22224 }
22225 }
22226 case PM_TOKEN_DOT_DOT:
22227 case PM_TOKEN_DOT_DOT_DOT: {
22228 parser_lex(parser);
22229
22230 pm_node_t *right = NULL;
22231 if (token_begins_expression_p(parser->current.type)) {
22232 right = parse_expression(parser, binding_power, flags & PM_PARSE_ACCEPTS_DO_BLOCK, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR, (uint16_t) (depth + 1));
22233 }
22234
22235 return UP(pm_range_node_create(parser, node, &token, right));
22236 }
22237 case PM_TOKEN_KEYWORD_IF_MODIFIER: {
22238 pm_token_t keyword = parser->current;
22239 parser_lex(parser);
22240
22241 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));
22242 return UP(pm_if_node_modifier_create(parser, node, &keyword, predicate));
22243 }
22244 case PM_TOKEN_KEYWORD_UNLESS_MODIFIER: {
22245 pm_token_t keyword = parser->current;
22246 parser_lex(parser);
22247
22248 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));
22249 return UP(pm_unless_node_modifier_create(parser, node, &keyword, predicate));
22250 }
22251 case PM_TOKEN_KEYWORD_UNTIL_MODIFIER: {
22252 parser_lex(parser);
22253 pm_statements_node_t *statements = pm_statements_node_create(parser);
22254 pm_statements_node_body_append(parser, statements, node, true);
22255
22256 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));
22257 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));
22258 }
22259 case PM_TOKEN_KEYWORD_WHILE_MODIFIER: {
22260 parser_lex(parser);
22261 pm_statements_node_t *statements = pm_statements_node_create(parser);
22262 pm_statements_node_body_append(parser, statements, node, true);
22263
22264 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));
22265 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));
22266 }
22267 case PM_TOKEN_QUESTION_MARK: {
22268 context_push(parser, PM_CONTEXT_TERNARY);
22269 pm_node_list_t current_block_exits = { 0 };
22270 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
22271
22272 pm_token_t qmark = parser->current;
22273 parser_lex(parser);
22274
22275 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));
22276
22277 if (parser->recovering) {
22278 // If parsing the true expression of this ternary resulted in a syntax
22279 // error that we can recover from, then we're going to put missing nodes
22280 // and tokens into the remaining places. We want to be sure to do this
22281 // before the `expect` function call to make sure it doesn't
22282 // accidentally move past a ':' token that occurs after the syntax
22283 // error.
22284 pm_token_t colon = (pm_token_t) { .type = 0, .start = parser->previous.end, .end = parser->previous.end };
22285 pm_node_t *false_expression = UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &colon), PM_TOKEN_LENGTH(&colon)));
22286
22287 context_pop(parser);
22288 pop_block_exits(parser, previous_block_exits);
22289 return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression));
22290 }
22291
22292 accept1(parser, PM_TOKEN_NEWLINE);
22293 expect1(parser, PM_TOKEN_COLON, PM_ERR_TERNARY_COLON);
22294
22295 pm_token_t colon = parser->previous;
22296 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));
22297
22298 context_pop(parser);
22299 pop_block_exits(parser, previous_block_exits);
22300 return UP(pm_if_node_ternary_create(parser, node, &qmark, true_expression, &colon, false_expression));
22301 }
22302 case PM_TOKEN_COLON_COLON: {
22303 parser_lex(parser);
22304 pm_token_t delimiter = parser->previous;
22305
22306 switch (parser->current.type) {
22307 case PM_TOKEN_CONSTANT: {
22308 parser_lex(parser);
22309 pm_node_t *path;
22310
22311 if (
22312 (parser->current.type == PM_TOKEN_PARENTHESIS_LEFT) ||
22313 ((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)))
22314 ) {
22315 // If we have a constant immediately following a '::' operator, then
22316 // this can either be a constant path or a method call, depending on
22317 // what follows the constant.
22318 //
22319 // If we have parentheses, then this is a method call. That would
22320 // look like Foo::Bar().
22321 pm_token_t message = parser->previous;
22322 pm_arguments_t arguments = { 0 };
22323
22324 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22325 path = UP(pm_call_node_call_create(parser, node, &delimiter, &message, &arguments));
22326 } else {
22327 // Otherwise, this is a constant path. That would look like Foo::Bar.
22328 path = UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
22329 }
22330
22331 // If this is followed by a comma then it is a multiple assignment.
22332 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22333 return parse_targets_validate(parser, path, PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22334 }
22335
22336 return path;
22337 }
22338 case PM_CASE_OPERATOR:
22339 case PM_CASE_KEYWORD:
22340 case PM_TOKEN_IDENTIFIER:
22341 case PM_TOKEN_METHOD_NAME: {
22342 parser_lex(parser);
22343 pm_token_t message = parser->previous;
22344
22345 // If we have an identifier following a '::' operator, then it is for
22346 // sure a method call.
22347 pm_arguments_t arguments = { 0 };
22348 parse_arguments_list(parser, &arguments, true, flags, (uint16_t) (depth + 1));
22349 pm_call_node_t *call = pm_call_node_call_create(parser, node, &delimiter, &message, &arguments);
22350
22351 // If this is followed by a comma then it is a multiple assignment.
22352 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22353 return parse_targets_validate(parser, UP(call), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22354 }
22355
22356 return UP(call);
22357 }
22358 case PM_TOKEN_PARENTHESIS_LEFT: {
22359 // If we have a parenthesis following a '::' operator, then it is the
22360 // method call shorthand. That would look like Foo::(bar).
22361 pm_arguments_t arguments = { 0 };
22362 parse_arguments_list(parser, &arguments, true, false, (uint16_t) (depth + 1));
22363
22364 return UP(pm_call_node_shorthand_create(parser, node, &delimiter, &arguments));
22365 }
22366 default: {
22367 expect1(parser, PM_TOKEN_CONSTANT, PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT);
22368 return UP(pm_constant_path_node_create(parser, node, &delimiter, &parser->previous));
22369 }
22370 }
22371 }
22372 case PM_TOKEN_KEYWORD_RESCUE_MODIFIER: {
22373 context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
22374 parser_lex(parser);
22375 accept1(parser, PM_TOKEN_NEWLINE);
22376
22377 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));
22378 context_pop(parser);
22379
22380 return UP(pm_rescue_modifier_node_create(parser, node, &token, value));
22381 }
22382 case PM_TOKEN_BRACKET_LEFT: {
22383 parser_lex(parser);
22384
22385 pm_arguments_t arguments = { 0 };
22386 arguments.opening_loc = TOK2LOC(parser, &parser->previous);
22387
22388 if (!accept1(parser, PM_TOKEN_BRACKET_RIGHT)) {
22389 parse_arguments(parser, &arguments, false, PM_TOKEN_BRACKET_RIGHT, (uint8_t) (flags & ~PM_PARSE_ACCEPTS_DO_BLOCK), (uint16_t) (depth + 1));
22390 expect1(parser, PM_TOKEN_BRACKET_RIGHT, PM_ERR_EXPECT_RBRACKET);
22391 }
22392
22393 arguments.closing_loc = TOK2LOC(parser, &parser->previous);
22394
22395 // If we have a comma after the closing bracket then this is a multiple
22396 // assignment and we should parse the targets.
22397 if (previous_binding_power == PM_BINDING_POWER_STATEMENT && match1(parser, PM_TOKEN_COMMA)) {
22398 pm_call_node_t *aref = pm_call_node_aref_create(parser, node, &arguments);
22399 return parse_targets_validate(parser, UP(aref), PM_BINDING_POWER_INDEX, (uint16_t) (depth + 1));
22400 }
22401
22402 // If we're at the end of the arguments, we can now check if there is a
22403 // block node that starts with a {. If there is, then we can parse it and
22404 // add it to the arguments.
22405 pm_block_node_t *block = NULL;
22406 if (accept1(parser, PM_TOKEN_BRACE_LEFT)) {
22407 block = parse_block(parser, (uint16_t) (depth + 1));
22408 pm_arguments_validate_block(parser, &arguments, block);
22409 } else if (pm_accepts_block_stack_p(parser) && accept1(parser, PM_TOKEN_KEYWORD_DO)) {
22410 block = parse_block(parser, (uint16_t) (depth + 1));
22411 }
22412
22413 if (block != NULL) {
22414 if (arguments.block != NULL) {
22415 pm_parser_err_node(parser, UP(block), PM_ERR_ARGUMENT_AFTER_BLOCK);
22416 if (arguments.arguments == NULL) {
22417 arguments.arguments = pm_arguments_node_create(parser);
22418 }
22419 pm_arguments_node_arguments_append(parser->arena, arguments.arguments, arguments.block);
22420 }
22421
22422 arguments.block = UP(block);
22423 }
22424
22425 return UP(pm_call_node_aref_create(parser, node, &arguments));
22426 }
22427 case PM_TOKEN_KEYWORD_IN: {
22428 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22429 parser->pattern_matching_newlines = true;
22430
22431 pm_token_t operator = parser->current;
22432 parser->command_start = false;
22433 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22434 parser_lex(parser);
22435
22436 pm_constant_id_list_t captures = { 0 };
22437 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));
22438
22439 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22440
22441 return UP(pm_match_predicate_node_create(parser, node, pattern, &operator));
22442 }
22443 case PM_TOKEN_EQUAL_GREATER: {
22444 bool previous_pattern_matching_newlines = parser->pattern_matching_newlines;
22445 parser->pattern_matching_newlines = true;
22446
22447 pm_token_t operator = parser->current;
22448 parser->command_start = false;
22449 lex_state_set(parser, PM_LEX_STATE_BEG | PM_LEX_STATE_LABEL);
22450 parser_lex(parser);
22451
22452 pm_constant_id_list_t captures = { 0 };
22453 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));
22454
22455 parser->pattern_matching_newlines = previous_pattern_matching_newlines;
22456
22457 return UP(pm_match_required_node_create(parser, node, pattern, &operator));
22458 }
22459 default:
22460 assert(false && "unreachable");
22461 return NULL;
22462 }
22463}
22464
22465#undef PM_PARSE_PATTERN_SINGLE
22466#undef PM_PARSE_PATTERN_TOP
22467#undef PM_PARSE_PATTERN_MULTI
22468
22481static bool
22482parse_expression_terminator(pm_parser_t *parser, pm_node_t *node) {
22483 pm_binding_power_t left = pm_binding_powers[parser->current.type].left;
22484
22485 switch (PM_NODE_TYPE(node)) {
22486 case PM_MULTI_WRITE_NODE:
22487 case PM_RETURN_NODE:
22488 case PM_BREAK_NODE:
22489 case PM_NEXT_NODE:
22490 return left > PM_BINDING_POWER_MODIFIER;
22491 case PM_CLASS_VARIABLE_WRITE_NODE:
22492 case PM_CONSTANT_PATH_WRITE_NODE:
22493 case PM_CONSTANT_WRITE_NODE:
22494 case PM_GLOBAL_VARIABLE_WRITE_NODE:
22495 case PM_INSTANCE_VARIABLE_WRITE_NODE:
22496 case PM_LOCAL_VARIABLE_WRITE_NODE:
22497 return PM_NODE_FLAG_P(node, PM_WRITE_NODE_FLAGS_IMPLICIT_ARRAY) && left > PM_BINDING_POWER_MODIFIER;
22498 case PM_CALL_NODE: {
22499 // Calls with an implicit array on the right-hand side are
22500 // statements and can only be followed by modifiers.
22501 if (PM_NODE_FLAG_P(node, PM_CALL_NODE_FLAGS_IMPLICIT_ARRAY)) {
22502 return left > PM_BINDING_POWER_MODIFIER;
22503 }
22504
22505 // Command-style calls (including block commands like
22506 // `foo bar do end`) can only be followed by composition
22507 // (and/or) and modifier (if/unless/etc.) operators.
22508 if (pm_command_call_value_p(parser, node)) {
22509 return left > PM_BINDING_POWER_COMPOSITION;
22510 }
22511
22512 // A block call (command with do-block, or any call chained
22513 // from one) can only be followed by call chaining (., ::,
22514 // &.), composition (and/or), and modifier operators.
22515 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22516 }
22517 case PM_SUPER_NODE:
22518 case PM_YIELD_NODE:
22519 // Command-style super/yield (without parens) can only be followed
22520 // by composition and modifier operators.
22521 if (pm_command_call_value_p(parser, node)) {
22522 return left > PM_BINDING_POWER_COMPOSITION;
22523 }
22524
22525 /* A super carrying a do-block is a block call, so it may also be
22526 * followed by call chaining (`.`, `::`, `&.`). */
22527 return left > PM_BINDING_POWER_COMPOSITION && left < PM_BINDING_POWER_CALL && pm_block_call_p(node);
22528 case PM_DEF_NODE:
22529 // An endless method whose body is a command-style call (e.g.,
22530 // `def f = foo bar`) is a command assignment and can only be
22531 // followed by modifiers.
22532 return left > PM_BINDING_POWER_MODIFIER && pm_command_call_value_p(parser, node);
22533 case PM_RESCUE_MODIFIER_NODE:
22534 // A rescue modifier whose handler is a pattern match (=> or in)
22535 // produces a statement and cannot be followed by operators above
22536 // the modifier level.
22537 if (left > PM_BINDING_POWER_MODIFIER) {
22539 pm_node_t *rescue_expression = cast->rescue_expression;
22540 return PM_NODE_TYPE_P(rescue_expression, PM_MATCH_REQUIRED_NODE) || PM_NODE_TYPE_P(rescue_expression, PM_MATCH_PREDICATE_NODE);
22541 }
22542 return false;
22543 default:
22544 return false;
22545 }
22546}
22547
22556static pm_node_t *
22557parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, uint8_t flags, pm_diagnostic_id_t diag_id, uint16_t depth) {
22558 if (PRISM_UNLIKELY(depth >= PRISM_DEPTH_MAXIMUM)) {
22559 pm_parser_err_current(parser, PM_ERR_NESTING_TOO_DEEP);
22560 return UP(pm_error_recovery_node_create(parser, PM_TOKEN_START(parser, &parser->current), PM_TOKEN_LENGTH(&parser->current)));
22561 }
22562
22563 pm_node_t *node = parse_expression_prefix(parser, binding_power, flags, diag_id, depth);
22564
22565 // Some prefix nodes are statements and can only be followed by modifiers
22566 // (if/unless/while/until/rescue) or nothing at all. We check these cheaply
22567 // here before entering the infix loop.
22568 switch (PM_NODE_TYPE(node)) {
22569 case PM_ERROR_RECOVERY_NODE:
22570 return node;
22571 case PM_PRE_EXECUTION_NODE:
22572 return node;
22573 case PM_POST_EXECUTION_NODE:
22574 case PM_ALIAS_GLOBAL_VARIABLE_NODE:
22575 case PM_ALIAS_METHOD_NODE:
22576 case PM_UNDEF_NODE:
22577 if (pm_binding_powers[parser->current.type].left > PM_BINDING_POWER_MODIFIER) {
22578 return node;
22579 }
22580 break;
22581 case PM_CALL_NODE:
22582 case PM_SUPER_NODE:
22583 case PM_YIELD_NODE:
22584 case PM_DEF_NODE:
22585 if (parse_expression_terminator(parser, node)) {
22586 return node;
22587 }
22588 break;
22589 case PM_SYMBOL_NODE:
22590 if (pm_symbol_node_label_p(parser, node)) {
22591 return node;
22592 }
22593 break;
22594 default:
22595 break;
22596 }
22597
22598 // Look and see if the next token can be parsed as an infix operator. If it
22599 // can, then we'll parse it using parse_expression_infix.
22600 pm_binding_powers_t current_binding_powers;
22601 pm_token_type_t current_token_type;
22602
22603 while (
22604 current_token_type = parser->current.type,
22605 current_binding_powers = pm_binding_powers[current_token_type],
22606 binding_power <= current_binding_powers.left &&
22607 current_binding_powers.binary
22608 ) {
22609 node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, flags, (uint16_t) (depth + 1));
22610 if (parse_expression_terminator(parser, node)) return node;
22611
22612 // If the operator is nonassoc and we should not be able to parse the
22613 // upcoming infix operator, break.
22614 if (current_binding_powers.nonassoc) {
22615 // If we are about to parse another non-associative operator at the
22616 // same precedence as the one we just parsed, then we need to add an
22617 // error. This covers chaining the same operator (`1 == 2 == 3`) as
22618 // well as different operators that share a precedence, since they
22619 // are equally non-associative with one another (`1 == 2 != 3`,
22620 // `1...2..3`).
22621 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22622 if (next_binding_powers.nonassoc && next_binding_powers.left == current_binding_powers.left) {
22623 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));
22624 break;
22625 }
22626
22627 // If this is an endless range, then we need to reject a couple of
22628 // additional operators because it violates the normal operator
22629 // precedence rules. Those patterns are:
22630 //
22631 // 1.. & 2
22632 // 1.. * 2
22633 //
22634 if (PM_NODE_TYPE_P(node, PM_RANGE_NODE) && ((pm_range_node_t *) node)->right == NULL) {
22635 if (match4(parser, PM_TOKEN_UAMPERSAND, PM_TOKEN_USTAR, PM_TOKEN_DOT, PM_TOKEN_AMPERSAND_DOT)) {
22636 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));
22637 break;
22638 }
22639
22640 if (PM_BINDING_POWER_TERM <= next_binding_powers.left) {
22641 break;
22642 }
22643 } else if (current_binding_powers.left <= next_binding_powers.left) {
22644 break;
22645 }
22646 }
22647
22648 if (flags & PM_PARSE_ACCEPTS_COMMAND_CALL) {
22649 // A command-style method call is only accepted on method chains.
22650 // Thus, we check whether the parsed node can continue method chains.
22651 // The method chain can continue if the parsed node is one of the following five kinds:
22652 // (1) index access: foo[1]
22653 // (2) attribute access: foo.bar
22654 // (3) method call with parenthesis: foo.bar(1)
22655 // (4) method call with a block: foo.bar do end
22656 // (5) constant path: foo::Bar
22657 switch (node->type) {
22658 case PM_CALL_NODE: {
22659 pm_call_node_t *cast = (pm_call_node_t *)node;
22660 if (
22661 // (1) foo[1]
22662 !(
22663 cast->call_operator_loc.length == 0 &&
22664 cast->message_loc.length > 0 &&
22665 parser->start[cast->message_loc.start] == '[' &&
22666 parser->start[cast->message_loc.start + cast->message_loc.length - 1] == ']'
22667 ) &&
22668 // (2) foo.bar
22669 !(
22670 cast->call_operator_loc.length > 0 &&
22671 cast->arguments == NULL &&
22672 cast->block == NULL &&
22673 cast->opening_loc.length == 0
22674 ) &&
22675 // (3) foo.bar(1)
22676 !(
22677 cast->call_operator_loc.length > 0 &&
22678 cast->opening_loc.length > 0
22679 ) &&
22680 // (4) foo.bar do end
22681 !(
22682 cast->block != NULL && PM_NODE_TYPE_P(cast->block, PM_BLOCK_NODE)
22683 )
22684 ) {
22685 flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
22686 }
22687 break;
22688 }
22689 // (5) foo::Bar
22690 case PM_CONSTANT_PATH_NODE:
22691 break;
22692 default:
22693 flags &= (uint8_t) ~PM_PARSE_ACCEPTS_COMMAND_CALL;
22694 break;
22695 }
22696 }
22697
22698 if (context_terminator(parser->current_context->context, &parser->current)) {
22699 pm_binding_powers_t next_binding_powers = pm_binding_powers[parser->current.type];
22700 if (
22701 !next_binding_powers.binary ||
22702 binding_power > next_binding_powers.left ||
22703 (PM_NODE_TYPE_P(node, PM_CALL_NODE) && pm_call_node_command_p((pm_call_node_t *) node))
22704 ) {
22705 return node;
22706 }
22707 }
22708 }
22709
22710 return node;
22711}
22712
22717static pm_statements_node_t *
22718wrap_statements(pm_parser_t *parser, pm_statements_node_t *statements) {
22719 if (PM_PARSER_COMMAND_LINE_OPTION_P(parser)) {
22720 if (statements == NULL) {
22721 statements = pm_statements_node_create(parser);
22722 }
22723
22724 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22725 pm_arguments_node_arguments_append(
22726 parser->arena,
22727 arguments,
22728 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2)))
22729 );
22730
22731 pm_statements_node_body_append(parser, statements, UP(pm_call_node_fcall_synthesized_create(
22732 parser,
22733 arguments,
22734 pm_parser_constant_id_constant(parser, "print", 5)
22735 )), true);
22736 }
22737
22738 if (PM_PARSER_COMMAND_LINE_OPTION_N(parser)) {
22739 if (PM_PARSER_COMMAND_LINE_OPTION_A(parser)) {
22740 if (statements == NULL) {
22741 statements = pm_statements_node_create(parser);
22742 }
22743
22744 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22745 pm_arguments_node_arguments_append(
22746 parser->arena,
22747 arguments,
22748 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$;", 2)))
22749 );
22750
22751 pm_global_variable_read_node_t *receiver = pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$_", 2));
22752 pm_call_node_t *call = pm_call_node_call_synthesized_create(parser, UP(receiver), "split", arguments);
22753
22754 pm_global_variable_write_node_t *write = pm_global_variable_write_node_synthesized_create(
22755 parser,
22756 pm_parser_constant_id_constant(parser, "$F", 2),
22757 UP(call)
22758 );
22759
22760 pm_statements_node_body_prepend(parser->arena, statements, UP(write));
22761 }
22762
22763 pm_arguments_node_t *arguments = pm_arguments_node_create(parser);
22764 pm_arguments_node_arguments_append(
22765 parser->arena,
22766 arguments,
22767 UP(pm_global_variable_read_node_synthesized_create(parser, pm_parser_constant_id_constant(parser, "$/", 2)))
22768 );
22769
22770 if (PM_PARSER_COMMAND_LINE_OPTION_L(parser)) {
22771 pm_keyword_hash_node_t *keywords = pm_keyword_hash_node_create(parser);
22772 pm_keyword_hash_node_elements_append(parser->arena, keywords, UP(pm_assoc_node_create(
22773 parser,
22774 UP(pm_symbol_node_synthesized_create(parser, "chomp")),
22775 NULL,
22776 UP(pm_true_node_synthesized_create(parser))
22777 )));
22778
22779 pm_arguments_node_arguments_append(parser->arena, arguments, UP(keywords));
22780 pm_node_flag_set(UP(arguments), PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS);
22781 }
22782
22783 pm_statements_node_t *wrapped_statements = pm_statements_node_create(parser);
22784 pm_statements_node_body_append(parser, wrapped_statements, UP(pm_while_node_synthesized_create(
22785 parser,
22786 UP(pm_call_node_fcall_synthesized_create(parser, arguments, pm_parser_constant_id_constant(parser, "gets", 4))),
22787 statements
22788 )), true);
22789
22790 statements = wrapped_statements;
22791 }
22792
22793 return statements;
22794}
22795
22799static pm_node_t *
22800parse_program(pm_parser_t *parser) {
22801 // If the current scope is NULL, then we want to push a new top level scope.
22802 // The current scope could exist in the event that we are parsing an eval
22803 // and the user has passed into scopes that already exist.
22804 if (parser->current_scope == NULL) {
22805 pm_parser_scope_push(parser, true);
22806 }
22807
22808 pm_node_list_t current_block_exits = { 0 };
22809 pm_node_list_t *previous_block_exits = push_block_exits(parser, &current_block_exits);
22810
22811 parser_lex(parser);
22812 pm_statements_node_t *statements = parse_statements(parser, PM_CONTEXT_MAIN, 0);
22813
22814 if (statements != NULL && !parser->parsing_eval) {
22815 // If we have statements, then the top-level statement should be
22816 // explicitly checked as well. We have to do this here because
22817 // everywhere else we check all but the last statement.
22818 assert(statements->body.size > 0);
22819 pm_void_statement_check(parser, statements->body.nodes[statements->body.size - 1]);
22820 }
22821
22822 pm_constant_id_list_t locals;
22823 pm_locals_order(parser, &parser->current_scope->locals, &locals, true);
22824 pm_parser_scope_pop(parser);
22825
22826 // At the top level, see if we need to wrap the statements in a program
22827 // node with a while loop based on the options.
22828 if (parser->command_line & (PM_OPTIONS_COMMAND_LINE_P | PM_OPTIONS_COMMAND_LINE_N)) {
22829 statements = wrap_statements(parser, statements);
22830 } else {
22831 flush_block_exits(parser, previous_block_exits);
22832 }
22833
22834 // If this is an empty file, then we're still going to parse all of the
22835 // statements in order to gather up all of the comments and such. Here we'll
22836 // correct the location information.
22837 if (statements == NULL) {
22838 statements = pm_statements_node_create(parser);
22839 statements->base.location = (pm_location_t) { 0 };
22840 }
22841
22842 return UP(pm_program_node_create(parser, &locals, statements));
22843}
22844
22845/******************************************************************************/
22846/* External functions */
22847/******************************************************************************/
22848
22858static const char *
22859pm_strnstr(const char *big, const char *little, size_t big_length) {
22860 size_t little_length = strlen(little);
22861
22862 for (const char *max = big + big_length - little_length; big <= max; big++) {
22863 if (*big == *little && memcmp(big, little, little_length) == 0) return big;
22864 }
22865
22866 return NULL;
22867}
22868
22869#ifdef _WIN32
22870#define pm_parser_warn_shebang_carriage_return(parser, start, length) ((void) 0)
22871#else
22877static void
22878pm_parser_warn_shebang_carriage_return(pm_parser_t *parser, const uint8_t *start, size_t length) {
22879 if (length > 2 && start[length - 2] == '\r' && start[length - 1] == '\n') {
22880 pm_parser_warn(parser, U32(start - parser->start), U32(length), PM_WARN_SHEBANG_CARRIAGE_RETURN);
22881 }
22882}
22883#endif
22884
22889static void
22890pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const char *engine, size_t length) {
22891 const char *switches = pm_strnstr(engine, " -", length);
22892 if (switches == NULL) return;
22893
22894 pm_options_t next_options = *options;
22895 options->shebang_callback(
22896 &next_options,
22897 (const uint8_t *) (switches + 1),
22898 length - ((size_t) (switches - engine)) - 1,
22899 options->shebang_callback_data
22900 );
22901
22902 size_t encoding_length;
22903 if ((encoding_length = pm_string_length(&next_options.encoding)) > 0) {
22904 const uint8_t *encoding_source = pm_string_source(&next_options.encoding);
22905 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
22906 }
22907
22908 parser->command_line = next_options.command_line;
22909 parser->frozen_string_literal = next_options.frozen_string_literal;
22910}
22911
22915void
22916pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, size_t size, const pm_options_t *options) {
22917 assert(arena != NULL);
22918 assert(source != NULL);
22919
22920 *parser = (pm_parser_t) {
22921 .arena = arena,
22922 .metadata_arena = { 0 },
22923 .node_id = 0,
22924 .lex_state = PM_LEX_STATE_BEG,
22925 .enclosure_nesting = 0,
22926 .lambda_enclosure_nesting = -1,
22927 .brace_nesting = 0,
22928 .do_loop_stack = 0,
22929 .accepts_block_stack = 0,
22930 .lex_modes = {
22931 .index = 0,
22932 .stack = {{ .mode = PM_LEX_DEFAULT }},
22933 .current = &parser->lex_modes.stack[0],
22934 },
22935 .start = source,
22936 .end = source + size,
22937 .previous = { .type = PM_TOKEN_EOF, .start = source, .end = source },
22938 .current = { .type = PM_TOKEN_EOF, .start = source, .end = source },
22939 .next_start = NULL,
22940 .heredoc_end = NULL,
22941 .data_loc = { 0 },
22942 .comment_list = { 0 },
22943 .magic_comment_list = { 0 },
22944 .warning_list = { 0 },
22945 .error_list = { 0 },
22946 .current_scope = NULL,
22947 .current_context = NULL,
22948 .encoding = PM_ENCODING_UTF_8_ENTRY,
22949 .encoding_changed_callback = NULL,
22950 .encoding_comment_start = source,
22951 .lex_callback = { 0 },
22952 .filepath = { 0 },
22953 .constant_pool = { 0 },
22954 .line_offsets = { 0 },
22955 .integer = { 0 },
22956 .current_string = PM_STRING_EMPTY,
22957 .start_line = 1,
22958 .explicit_encoding = NULL,
22959 .command_line = 0,
22960 .parsing_eval = false,
22961 .partial_script = false,
22962 .command_start = true,
22963 .recovering = false,
22964 .continuable = true,
22965 .encoding_locked = false,
22966 .encoding_changed = false,
22967 .pattern_matching_newlines = false,
22968 .in_keyword_arg = false,
22969 .current_block_exits = NULL,
22970 .semantic_token_seen = false,
22971 .frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET,
22972 .warn_mismatched_indentation = true
22973 };
22974
22975 /* Pre-size the arenas based on input size to reduce the number of block
22976 * allocations (and the kernel page zeroing they trigger). The ratios were
22977 * measured empirically: AST arena ~3.3x input, metadata arena ~1.1x input.
22978 * The reserve call is a no-op when the capacity is at or below the default
22979 * arena block size, so small inputs don't waste an extra allocation. */
22980 if (size <= SIZE_MAX / 4) pm_arena_reserve(arena, size * 4);
22981 if (size <= SIZE_MAX / 5 * 4) pm_arena_reserve(&parser->metadata_arena, size + size / 4);
22982
22983 /* Initialize the constant pool. Measured across 1532 Ruby stdlib files, the
22984 * bytes/constant ratio has a median of ~56 and a 90th percentile of ~135.
22985 * We use 120 as a balance between over-allocation waste and resize
22986 * frequency. Resizes are cheap with arena allocation, so we lean toward
22987 * under-estimating. */
22988 uint32_t constant_size = ((uint32_t) size) / 120;
22989 pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size < 4 ? 4 : constant_size);
22990
22991 /* Initialize the line offset list. Similar to the constant pool, we are
22992 * going to estimate the number of newlines that we will need based on the
22993 * size of the input. */
22994 size_t newline_size = size / 22;
22995 pm_line_offset_list_init(&parser->metadata_arena, &parser->line_offsets, newline_size < 4 ? 4 : newline_size);
22996
22997 // If options were provided to this parse, establish them here.
22998 if (options != NULL) {
22999 // filepath option
23000 parser->filepath = options->filepath;
23001
23002 // line option
23003 parser->start_line = options->line;
23004
23005 // encoding option
23006 size_t encoding_length = pm_string_length(&options->encoding);
23007 if (encoding_length > 0) {
23008 const uint8_t *encoding_source = pm_string_source(&options->encoding);
23009 parser_lex_magic_comment_encoding_value(parser, encoding_source, encoding_source + encoding_length);
23010 }
23011
23012 // encoding_locked option
23013 parser->encoding_locked = options->encoding_locked;
23014
23015 // frozen_string_literal option
23016 parser->frozen_string_literal = options->frozen_string_literal;
23017
23018 // command_line option
23019 parser->command_line = options->command_line;
23020
23021 // version option
23022 parser->version = options->version;
23023
23024 // partial_script
23025 parser->partial_script = options->partial_script;
23026
23027 // scopes option
23028 parser->parsing_eval = options->scopes_count > 0;
23029 if (parser->parsing_eval) parser->warn_mismatched_indentation = false;
23030
23031 for (size_t scope_index = 0; scope_index < options->scopes_count; scope_index++) {
23032 const pm_options_scope_t *scope = pm_options_scope(options, scope_index);
23033 pm_parser_scope_push(parser, scope_index == 0);
23034
23035 // Scopes given from the outside are not allowed to have numbered
23036 // parameters.
23037 parser->current_scope->parameters = ((pm_scope_parameters_t) scope->forwarding) | PM_SCOPE_PARAMETERS_IMPLICIT_DISALLOWED;
23038
23039 for (size_t local_index = 0; local_index < scope->locals_count; local_index++) {
23040 const pm_string_t *local = pm_options_scope_local(scope, local_index);
23041
23042 const uint8_t *source = pm_string_source(local);
23043 size_t length = pm_string_length(local);
23044
23045 uint8_t *allocated = (uint8_t *) pm_arena_alloc(&parser->metadata_arena, length, 1);
23046 memcpy(allocated, source, length);
23047 pm_parser_local_add_owned(parser, allocated, length);
23048 }
23049 }
23050 }
23051
23052 // Now that we have established the user-provided options, check if
23053 // a version was given and parse as the latest version otherwise.
23054 if (parser->version == PM_OPTIONS_VERSION_UNSET) {
23055 parser->version = PM_OPTIONS_VERSION_LATEST;
23056 }
23057
23058 pm_accepts_block_stack_push(parser, true);
23059
23060 // Skip past the UTF-8 BOM if it exists.
23061 if (size >= 3 && source[0] == 0xef && source[1] == 0xbb && source[2] == 0xbf) {
23062 parser->current.end += 3;
23063 parser->encoding_comment_start += 3;
23064
23065 if (parser->encoding != PM_ENCODING_UTF_8_ENTRY) {
23066 parser->encoding = PM_ENCODING_UTF_8_ENTRY;
23067 if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser);
23068 }
23069 }
23070
23071 // If the -x command line flag is set, or the first shebang of the file does
23072 // not include "ruby", then we'll search for a shebang that does include
23073 // "ruby" and start parsing from there.
23074 bool search_shebang = PM_PARSER_COMMAND_LINE_OPTION_X(parser);
23075
23076 // If the first two bytes of the source are a shebang, then we will do a bit
23077 // of extra processing.
23078 //
23079 // First, we'll indicate that the encoding comment is at the end of the
23080 // shebang. This means that when a shebang is present the encoding comment
23081 // can begin on the second line.
23082 //
23083 // Second, we will check if the shebang includes "ruby". If it does, then we
23084 // we will start parsing from there. We will also potentially warning the
23085 // user if there is a carriage return at the end of the shebang. We will
23086 // also potentially call the shebang callback if this is the main script to
23087 // allow the caller to parse the shebang and find any command-line options.
23088 // If the shebang does not include "ruby" and this is the main script being
23089 // parsed, then we will start searching the file for a shebang that does
23090 // contain "ruby" as if -x were passed on the command line.
23091 const uint8_t *newline = next_newline(parser->current.end, parser->end - parser->current.end);
23092 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - parser->current.end);
23093
23094 if (length > 2 && parser->current.end[0] == '#' && parser->current.end[1] == '!') {
23095 const char *engine;
23096
23097 if ((engine = pm_strnstr((const char *) parser->start, "ruby", length)) != NULL) {
23098 if (newline != NULL) {
23099 parser->encoding_comment_start = newline + 1;
23100
23101 if (options == NULL || options->main_script) {
23102 pm_parser_warn_shebang_carriage_return(parser, parser->start, length + 1);
23103 }
23104 }
23105
23106 if (options != NULL && options->main_script && options->shebang_callback != NULL) {
23107 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) parser->start)));
23108 }
23109
23110 search_shebang = false;
23111 } else if (options != NULL && options->main_script && !parser->parsing_eval) {
23112 search_shebang = true;
23113 }
23114 }
23115
23116 // Here we're going to find the first shebang that includes "ruby" and start
23117 // parsing from there.
23118 if (search_shebang) {
23119 // If a shebang that includes "ruby" is not found, then we're going to a
23120 // a load error to the list of errors on the parser.
23121 bool found_shebang = false;
23122
23123 // This is going to point to the start of each line as we check it.
23124 // We'll maintain a moving window looking at each line at they come.
23125 const uint8_t *cursor = parser->start;
23126
23127 // The newline pointer points to the end of the current line that we're
23128 // considering. If it is NULL, then we're at the end of the file.
23129 const uint8_t *newline = next_newline(cursor, parser->end - cursor);
23130
23131 while (newline != NULL) {
23132 pm_line_offset_list_append(&parser->metadata_arena, &parser->line_offsets, U32(newline - parser->start + 1));
23133
23134 cursor = newline + 1;
23135 newline = next_newline(cursor, parser->end - cursor);
23136
23137 size_t length = (size_t) ((newline != NULL ? newline : parser->end) - cursor);
23138 if (length > 2 && cursor[0] == '#' && cursor[1] == '!') {
23139 const char *engine;
23140 if ((engine = pm_strnstr((const char *) cursor, "ruby", length)) != NULL) {
23141 found_shebang = true;
23142
23143 if (newline != NULL) {
23144 pm_parser_warn_shebang_carriage_return(parser, cursor, length + 1);
23145 parser->encoding_comment_start = newline + 1;
23146 }
23147
23148 if (options != NULL && options->shebang_callback != NULL) {
23149 pm_parser_init_shebang(parser, options, engine, length - ((size_t) (engine - (const char *) cursor)));
23150 }
23151
23152 break;
23153 }
23154 }
23155 }
23156
23157 if (found_shebang) {
23158 parser->previous = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23159 parser->current = (pm_token_t) { .type = PM_TOKEN_EOF, .start = cursor, .end = cursor };
23160 } else {
23161 pm_parser_err(parser, 0, 0, PM_ERR_SCRIPT_NOT_FOUND);
23162 pm_line_offset_list_clear(&parser->line_offsets);
23163 }
23164 }
23165
23166 // The encoding comment can start after any amount of inline whitespace, so
23167 // here we'll advance it to the first non-inline-whitespace character so
23168 // that it is ready for future comparisons.
23169 parser->encoding_comment_start += pm_strspn_inline_whitespace(parser->encoding_comment_start, parser->end - parser->encoding_comment_start);
23170}
23171
23180pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) {
23181 pm_parser_t *parser = (pm_parser_t *) xmalloc(sizeof(pm_parser_t));
23182 if (parser == NULL) abort();
23183
23184 pm_parser_init(arena, parser, source, size, options);
23185 return parser;
23186}
23187
23191void
23192pm_parser_cleanup(pm_parser_t *parser) {
23193 pm_string_cleanup(&parser->filepath);
23194 pm_arena_cleanup(&parser->metadata_arena);
23195
23196 while (parser->current_scope != NULL) {
23197 // Normally, popping the scope doesn't free the locals since it is
23198 // assumed that ownership has transferred to the AST. However if we have
23199 // scopes while we're freeing the parser, it's likely they came from
23200 // eval scopes and we need to free them explicitly here.
23201 pm_parser_scope_pop(parser);
23202 }
23203
23204 while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
23205 lex_mode_pop(parser);
23206 }
23207}
23208
23212void
23214 pm_parser_cleanup(parser);
23215 xfree_sized(parser, sizeof(pm_parser_t));
23216}
23217
23223static bool
23224pm_parse_err_is_fatal(pm_diagnostic_id_t diag_id) {
23225 switch (diag_id) {
23226 case PM_ERR_ARRAY_EXPRESSION_AFTER_STAR:
23227 case PM_ERR_BEGIN_UPCASE_BRACE:
23228 case PM_ERR_CLASS_VARIABLE_BARE:
23229 case PM_ERR_END_UPCASE_BRACE:
23230 case PM_ERR_ESCAPE_INVALID_HEXADECIMAL:
23231 case PM_ERR_ESCAPE_INVALID_UNICODE_LIST:
23232 case PM_ERR_ESCAPE_INVALID_UNICODE_SHORT:
23233 case PM_ERR_EXPRESSION_NOT_WRITABLE:
23234 case PM_ERR_EXPRESSION_NOT_WRITABLE_SELF:
23235 case PM_ERR_FLOAT_PARSE:
23236 case PM_ERR_GLOBAL_VARIABLE_BARE:
23237 case PM_ERR_HASH_KEY:
23238 case PM_ERR_HEREDOC_IDENTIFIER:
23239 case PM_ERR_INSTANCE_VARIABLE_BARE:
23240 case PM_ERR_INVALID_BLOCK_EXIT:
23241 case PM_ERR_INVALID_ENCODING_MAGIC_COMMENT:
23242 case PM_ERR_INVALID_FLOAT_EXPONENT:
23243 case PM_ERR_INVALID_NUMBER_BINARY:
23244 case PM_ERR_INVALID_NUMBER_DECIMAL:
23245 case PM_ERR_INVALID_NUMBER_HEXADECIMAL:
23246 case PM_ERR_INVALID_NUMBER_OCTAL:
23247 case PM_ERR_INVALID_NUMBER_UNDERSCORE_TRAILING:
23248 case PM_ERR_NO_LOCAL_VARIABLE:
23249 case PM_ERR_PARAMETER_ORDER:
23250 case PM_ERR_STATEMENT_UNDEF:
23251 case PM_ERR_VOID_EXPRESSION:
23252 return true;
23253 default:
23254 return false;
23255 }
23256}
23257
23291static void
23292pm_parse_continuable(pm_parser_t *parser) {
23293 // If there are no errors then there is nothing to continue.
23294 if (parser->error_list.size == 0) {
23295 parser->continuable = false;
23296 return;
23297 }
23298
23299 if (!parser->continuable) return;
23300
23301 size_t source_length = (size_t) (parser->end - parser->start);
23302
23303 // First pass: check if there are any non-stray, non-fatal errors.
23304 bool has_non_stray_error = false;
23305 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23306 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)) {
23307 has_non_stray_error = true;
23308 break;
23309 }
23310 }
23311
23312 // Second pass: check each error. We track the minimum source position
23313 // among non-stray, non-fatal errors seen so far in list order, which
23314 // lets us detect cascade stray tokens.
23315 size_t non_stray_min_start = SIZE_MAX;
23316
23317 for (pm_diagnostic_t *error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
23318 size_t error_start = (size_t) error->location.start;
23319 size_t error_end = error_start + (size_t) error->location.length;
23320 bool at_eof = error_end >= source_length;
23321
23322 // Fatal errors are non-continuable unless they occur at EOF.
23323 if (pm_parse_err_is_fatal(error->diag_id) && !at_eof) {
23324 parser->continuable = false;
23325 return;
23326 }
23327
23328 // Track non-stray, non-fatal error positions in list order.
23329 if (error->diag_id != PM_ERR_UNEXPECTED_TOKEN_IGNORE &&
23330 error->diag_id != PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT) {
23331 if (error_start < non_stray_min_start) non_stray_min_start = error_start;
23332 continue;
23333 }
23334
23335 // This is a stray token. Determine if it is a cascade effect
23336 // of a preceding error or genuinely stray.
23337
23338 // Rule (a): a non-stray error was seen earlier in the list at a
23339 // strictly earlier position — this stray is a cascade effect.
23340 if (non_stray_min_start < error_start) continue;
23341
23342 // Rule (b): this stray is at EOF with valid code before it.
23343 // Single-byte stray tokens at EOF (like `\` for line continuation)
23344 // are likely truncated tokens. Multi-byte stray tokens (like the
23345 // keyword `end`) need additional evidence that they are cascade
23346 // effects (i.e. non-stray errors exist elsewhere).
23347 if (at_eof && error_start > 0) {
23348 // Exception: closing delimiters at EOF are genuinely stray.
23349 if (error->location.length == 1) {
23350 const uint8_t *byte = parser->start + error_start;
23351 if (*byte == ')' || *byte == ']' || *byte == '}') {
23352 parser->continuable = false;
23353 return;
23354 }
23355
23356 // Single-byte non-delimiter stray at EOF: cascade.
23357 continue;
23358 }
23359
23360 // Multi-byte stray at EOF: cascade only if there are
23361 // non-stray errors (evidence of a preceding parse failure).
23362 if (has_non_stray_error) continue;
23363 }
23364
23365 // Rule (c): a stray `=` at the start of a line could be the
23366 // beginning of an embedded document (`=begin`). The remaining
23367 // bytes after `=` parse as an identifier, so the error is not
23368 // at EOF, but the construct is genuinely incomplete.
23369 if (error->location.length == 1) {
23370 const uint8_t *byte = parser->start + error_start;
23371 if (*byte == '=' && (error_start == 0 || *(byte - 1) == '\n')) continue;
23372 }
23373
23374 // This stray token is genuinely non-continuable.
23375 parser->continuable = false;
23376 return;
23377 }
23378}
23379
23383pm_node_t *
23385 pm_node_t *node = parse_program(parser);
23386 pm_parse_continuable(parser);
23387 return node;
23388}
23389
23396pm_node_t *
23397pm_parse_stream(pm_parser_t **parser, pm_arena_t *arena, pm_source_t *source, const pm_options_t *options) {
23398 bool eof = pm_source_stream_read(source);
23399
23400 pm_parser_t *tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23401 pm_node_t *node = pm_parse(tmp);
23402
23403 while (!eof && tmp->error_list.size > 0) {
23404 eof = pm_source_stream_read(source);
23405
23406 pm_parser_free(tmp);
23407 pm_arena_cleanup(arena);
23408
23409 tmp = pm_parser_new(arena, pm_source_source(source), pm_source_length(source), options);
23410 node = pm_parse(tmp);
23411 }
23412
23413 *parser = tmp;
23414 return node;
23415}
23416
23417#undef PM_CASE_KEYWORD
23418#undef PM_CASE_OPERATOR
23419#undef PM_CASE_WRITABLE
23420#undef PM_STRING_EMPTY
23421
23422// We optionally support serializing to a binary string. For systems that don't
23423// want or need this functionality, it can be turned off with the
23424// PRISM_EXCLUDE_SERIALIZATION define.
23425#ifndef PRISM_EXCLUDE_SERIALIZATION
23426
23427static PRISM_INLINE void
23428pm_serialize_header(pm_buffer_t *buffer) {
23429 pm_buffer_append_string(buffer, "PRISM", 5);
23430 pm_buffer_append_byte(buffer, PRISM_VERSION_MAJOR);
23431 pm_buffer_append_byte(buffer, PRISM_VERSION_MINOR);
23432 pm_buffer_append_byte(buffer, PRISM_VERSION_PATCH);
23433 pm_buffer_append_byte(buffer, PRISM_SERIALIZE_ONLY_SEMANTICS_FIELDS ? 1 : 0);
23434}
23435
23439void
23440pm_serialize(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) {
23441 pm_serialize_header(buffer);
23442 pm_serialize_content(parser, node, buffer);
23443 pm_buffer_append_byte(buffer, '\0');
23444}
23445
23450void
23451pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23452 pm_options_t options = { 0 };
23453 pm_options_read(&options, data);
23454
23455 pm_arena_t arena = { 0 };
23456 pm_parser_t parser;
23457 pm_parser_init(&arena, &parser, source, size, &options);
23458
23459 pm_node_t *node = pm_parse(&parser);
23460
23461 pm_serialize_header(buffer);
23462 pm_serialize_content(&parser, node, buffer);
23463 pm_buffer_append_byte(buffer, '\0');
23464
23465 pm_parser_cleanup(&parser);
23466 pm_arena_cleanup(&arena);
23467 pm_options_cleanup(&options);
23468}
23469
23474void
23475pm_serialize_parse_stream(pm_buffer_t *buffer, pm_source_t *source, const char *data) {
23476 pm_arena_t arena = { 0 };
23477 pm_parser_t *parser;
23478 pm_options_t options = { 0 };
23479 pm_options_read(&options, data);
23480
23481 pm_node_t *node = pm_parse_stream(&parser, &arena, source, &options);
23482 pm_serialize_header(buffer);
23483 pm_serialize_content(parser, node, buffer);
23484 pm_buffer_append_byte(buffer, '\0');
23485
23486 pm_parser_free(parser);
23487 pm_arena_cleanup(&arena);
23488 pm_options_cleanup(&options);
23489}
23490
23499int8_t
23500pm_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) {
23501 pm_options_t options = { 0 };
23502 pm_options_read(&options, data);
23503
23504 pm_arena_t arena = { 0 };
23505 pm_parser_t parser;
23506 pm_parser_init(&arena, &parser, source, size, &options);
23507
23508 pm_parse(&parser);
23509
23510 int8_t result = -1;
23511 if (parser.error_list.size > 0) {
23512 const char *encoding_name = parser.encoding->name;
23513 pm_buffer_append_string(buffer, encoding_name, strlen(encoding_name));
23514 pm_buffer_append_byte(buffer, '\0');
23515
23516 result = (int8_t) pm_errors_format(&parser, buffer, format_type);
23517 }
23518
23519 pm_parser_cleanup(&parser);
23520 pm_arena_cleanup(&arena);
23521 pm_options_cleanup(&options);
23522
23523 return result;
23524}
23525
23529void
23530pm_serialize_parse_comments(pm_buffer_t *buffer, const uint8_t *source, size_t size, const char *data) {
23531 pm_options_t options = { 0 };
23532 pm_options_read(&options, data);
23533
23534 pm_arena_t arena = { 0 };
23535 pm_parser_t parser;
23536 pm_parser_init(&arena, &parser, source, size, &options);
23537
23538 pm_parse(&parser);
23539 pm_serialize_header(buffer);
23540 pm_serialize_encoding(parser.encoding, buffer);
23541 pm_buffer_append_varsint(buffer, parser.start_line);
23542 pm_serialize_line_offset_list(&parser.line_offsets, buffer);
23543 pm_serialize_comment_list(&parser.comment_list, buffer);
23544
23545 pm_parser_cleanup(&parser);
23546 pm_arena_cleanup(&arena);
23547 pm_options_cleanup(&options);
23548}
23549
23550#endif
#define PRISM_ALIGNOF
Get the alignment requirement of a type.
Definition align.h:15
pm_comment_type_t
This is the type of a comment that we've found while parsing.
Definition comments.h:18
uint32_t pm_constant_id_t
A constant id is a unique identifier for a constant in the constant pool.
pm_errors_format_type_t
The type of formatting to use when formatting errors.
A header file that defines macros to exclude certain features of the prism library.
#define PRISM_FALLTHROUGH
We use -Wimplicit-fallthrough to guard potentially unintended fall-through between cases of a switch.
Definition fallthrough.h:15
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define xcalloc
Old name of ruby_xcalloc.
Definition xmalloc.h:55
int len
Length of the buffer.
Definition io.h:8
#define PRISM_INLINE
Old Visual Studio versions do not support the inline keyword, so we need to define it to be __inline.
Definition inline.h:12
VALUE type(ANYARGS)
ANYARGS-ed function type.
static const uint8_t PM_OPTIONS_COMMAND_LINE_N
A bit representing whether or not the command line -n option was set.
Definition options.h:96
#define PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED
String literals should not be frozen.
Definition options.h:31
#define PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED
String literals should be made frozen.
Definition options.h:42
#define PM_OPTIONS_FROZEN_STRING_LITERAL_UNSET
String literals may be frozen or mutable depending on the implementation default.
Definition options.h:37
static const uint8_t PM_OPTIONS_COMMAND_LINE_P
A bit representing whether or not the command line -p option was set.
Definition options.h:102
PRISM_EXPORTED_FUNCTION PRISM_NODISCARD pm_parser_t * pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_options_t *options) PRISM_NONNULL(1)
Allocate and initialize a parser with the given start and end pointers.
Definition prism.c:23180
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:23213
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:23384
#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:12612
bool binary
Whether or not this token can be used as a binary operator.
Definition prism.c:12620
pm_binding_power_t left
The left binding power.
Definition prism.c:12614
bool nonassoc
Whether or not this token can be used as non-associative binary operator.
Definition prism.c:12626
pm_binding_power_t right
The right binding power.
Definition prism.c:12617
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:9844
pm_buffer_t regexp_buffer
The buffer holding the regexp source.
Definition prism.c:9849
pm_token_buffer_t base
The embedded base buffer.
Definition prism.c:9846
RegularExpressionNode.
Definition ast.h:6994
RequiredParameterNode.
Definition ast.h:7066
RescueModifierNode.
Definition ast.h:7088
PM_NODE_ALIGNAS struct pm_node * rescue_expression
RescueModifierNode::rescue_expression.
Definition ast.h:7105
RescueNode.
Definition ast.h:7125
PM_NODE_ALIGNAS struct pm_rescue_node * subsequent
RescueNode::subsequent.
Definition ast.h:7162
pm_location_t then_keyword_loc
RescueNode::then_keyword_loc.
Definition ast.h:7152
SplatNode.
Definition ast.h:7415
PM_NODE_ALIGNAS struct pm_node * expression
SplatNode::expression.
Definition ast.h:7427
StatementsNode.
Definition ast.h:7442
struct pm_node_list body
StatementsNode::body.
Definition ast.h:7449
pm_node_t base
The embedded base node.
Definition ast.h:7444
StringNode.
Definition ast.h:7476
pm_node_t base
The embedded base node.
Definition ast.h:7478
pm_string_t unescaped
StringNode::unescaped.
Definition ast.h:7498
pm_location_t content_loc
StringNode::content_loc.
Definition ast.h:7488
pm_location_t closing_loc
StringNode::closing_loc.
Definition ast.h:7493
pm_location_t opening_loc
StringNode::opening_loc.
Definition ast.h:7483
A generic string type that can have various ownership semantics.
Definition stringy.h:18
const uint8_t * source
A pointer to the start of the string.
Definition stringy.h:20
enum pm_string_t::@118 type
The type of the string.
size_t length
The length of the string in bytes of memory.
Definition stringy.h:23
SuperNode.
Definition ast.h:7518
PM_NODE_ALIGNAS struct pm_arguments_node * arguments
SuperNode::arguments.
Definition ast.h:7537
pm_location_t lparen_loc
SuperNode::lparen_loc.
Definition ast.h:7530
PM_NODE_ALIGNAS struct pm_node * block
SuperNode::block.
Definition ast.h:7547
SymbolNode.
Definition ast.h:7570
pm_location_t value_loc
SymbolNode::value_loc.
Definition ast.h:7582
pm_string_t unescaped
SymbolNode::unescaped.
Definition ast.h:7592
When we're lexing certain types (strings, symbols, lists, etc.) we have string content associated wit...
Definition prism.c:9818
pm_buffer_t buffer
The buffer that we're using to keep track of the string content.
Definition prism.c:9823
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:9829
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