prism 2.0.0.rc2-x86_64-linux-gnu → 2.1.0-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -41
  3. data/Makefile +8 -8
  4. data/README.md +3 -1
  5. data/docs/fuzzing.md +5 -5
  6. data/docs/serialization.md +19 -6
  7. data/ext/prism/api_node.c +80 -80
  8. data/ext/prism/extension.c +34 -67
  9. data/ext/prism/extension.h +1 -1
  10. data/include/prism/arena.h +8 -0
  11. data/include/prism/arena.hpp +42 -0
  12. data/include/prism/ast.h +11 -1
  13. data/include/prism/ast.hpp +641 -0
  14. data/include/prism/buffer.h +8 -0
  15. data/include/prism/buffer.hpp +55 -0
  16. data/include/prism/comments.h +17 -0
  17. data/include/prism/compiler/branch.h +26 -0
  18. data/include/prism/compiler/cplusplus.hpp +16 -0
  19. data/include/prism/compiler/exceptions.hpp +17 -0
  20. data/include/prism/compiler/format.h +2 -0
  21. data/include/prism/compiler/lifetimebound.hpp +25 -0
  22. data/include/prism/constant_pool.h +25 -0
  23. data/include/prism/diagnostic.h +18 -0
  24. data/include/prism/errors_format.h +8 -0
  25. data/include/prism/internal/comments.h +2 -1
  26. data/include/prism/internal/node.h +8 -4
  27. data/include/prism/internal/parser.h +26 -15
  28. data/include/prism/internal/static_literals.h +24 -64
  29. data/include/prism/internal/stringy.h +1 -6
  30. data/include/prism/json.h +8 -0
  31. data/include/prism/line_offset_list.h +8 -0
  32. data/include/prism/magic_comments.h +18 -0
  33. data/include/prism/node.h +55 -10
  34. data/include/prism/node.hpp +272 -0
  35. data/include/prism/node_type.hpp +38 -0
  36. data/include/prism/options.h +10 -1
  37. data/include/prism/options.hpp +43 -0
  38. data/include/prism/parser.h +72 -64
  39. data/include/prism/parser.hpp +274 -0
  40. data/include/prism/prettyprint.h +8 -0
  41. data/include/prism/serialize.h +8 -0
  42. data/include/prism/source.h +8 -0
  43. data/include/prism/stream.h +8 -0
  44. data/include/prism/string_query.h +8 -0
  45. data/include/prism/stringy.h +16 -27
  46. data/include/prism/stringy.hpp +31 -0
  47. data/include/prism/version.h +10 -2
  48. data/include/prism.h +0 -8
  49. data/include/prism.hpp +24 -0
  50. data/lib/prism/3.3/prism.so +0 -0
  51. data/lib/prism/3.4/prism.so +0 -0
  52. data/lib/prism/4.0/prism.so +0 -0
  53. data/lib/prism/serialize.rb +2550 -3282
  54. data/prism.gemspec +14 -1
  55. data/rbi/generated/prism/node.rbi +736 -0
  56. data/rbi/generated/prism/serialize.rbi +6 -5
  57. data/sig/generated/prism/node.rbs +736 -0
  58. data/sig/generated/prism/serialize.rbs +7 -5
  59. data/src/arena.c +6 -1
  60. data/src/comments.c +11 -10
  61. data/src/diagnostic.c +24 -4
  62. data/src/integer.c +6 -1
  63. data/src/node.c +216 -368
  64. data/src/options.c +7 -13
  65. data/src/parser.c +52 -48
  66. data/src/prism.c +222 -240
  67. data/src/regexp.c +0 -1
  68. data/src/serialize.c +2300 -1950
  69. data/src/source.c +8 -2
  70. data/src/static_literals.c +280 -405
  71. data/src/stringy.c +3 -33
  72. metadata +14 -1
data/src/prism.c CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "prism/compiler/accel.h"
2
+ #include "prism/compiler/branch.h"
2
3
  #include "prism/compiler/fallthrough.h"
3
4
  #include "prism/compiler/unused.h"
4
5
 
@@ -68,24 +69,6 @@
68
69
  # define PM_STATIC_ASSERT(line, condition, message) typedef char PM_CONCATENATE(static_assert_, line)[(condition) ? 1 : -1]
69
70
  #endif
70
71
 
71
- /**
72
- * Support PRISM_LIKELY and PRISM_UNLIKELY to help the compiler optimize its
73
- * branch predication.
74
- */
75
- #if defined(__GNUC__) || defined(__clang__)
76
- /** The compiler should predicate that this branch will be taken. */
77
- #define PRISM_LIKELY(x) __builtin_expect(!!(x), 1)
78
-
79
- /** The compiler should predicate that this branch will not be taken. */
80
- #define PRISM_UNLIKELY(x) __builtin_expect(!!(x), 0)
81
- #else
82
- /** Void because this platform does not support branch prediction hints. */
83
- #define PRISM_LIKELY(x) (x)
84
-
85
- /** Void because this platform does not support branch prediction hints. */
86
- #define PRISM_UNLIKELY(x) (x)
87
- #endif
88
-
89
72
  /**
90
73
  * The prism version and the serialization format.
91
74
  */
@@ -201,27 +184,22 @@ lex_mode_terminator(const uint8_t start) {
201
184
  }
202
185
 
203
186
  /**
204
- * Push a new lex state onto the stack. If we're still within the pre-allocated
205
- * space of the lex state stack, then we'll just use a new slot. Otherwise we'll
206
- * allocate a new pointer and use that.
187
+ * Push a new lex state onto the stack, reusing a popped lex mode if there is
188
+ * one and allocating a new one otherwise.
207
189
  */
208
190
  static void
209
191
  lex_mode_push(pm_parser_t *parser, pm_lex_mode_t lex_mode) {
210
- lex_mode.prev = parser->lex_modes.current;
211
- parser->lex_modes.index++;
212
-
213
- if (parser->lex_modes.index > PM_LEX_STACK_SIZE - 1) {
214
- parser->lex_modes.current = (pm_lex_mode_t *) xmalloc(sizeof(pm_lex_mode_t));
215
- if (PRISM_UNLIKELY(parser->lex_modes.current == NULL)) {
216
- fprintf(stderr, "prism: out of memory; aborting\n");
217
- abort();
218
- }
192
+ pm_lex_mode_t *next = parser->lex_modes.free;
219
193
 
220
- *parser->lex_modes.current = lex_mode;
194
+ if (next != NULL) {
195
+ parser->lex_modes.free = next->prev;
221
196
  } 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];
197
+ next = (pm_lex_mode_t *) pm_arena_alloc(&parser->scratch_arena, sizeof(pm_lex_mode_t), PRISM_ALIGNOF(pm_lex_mode_t));
224
198
  }
199
+
200
+ lex_mode.prev = parser->lex_modes.current;
201
+ *next = lex_mode;
202
+ parser->lex_modes.current = next;
225
203
  }
226
204
 
227
205
  /**
@@ -374,22 +352,20 @@ lex_mode_push_string_eof(pm_parser_t *parser) {
374
352
  }
375
353
 
376
354
  /**
377
- * Pop the current lex state off the stack. If we're within the pre-allocated
378
- * space of the lex state stack, then we'll just decrement the index. Otherwise
379
- * we'll free the current pointer and use the previous pointer.
355
+ * Pop the current lex state off the stack, keeping it for reuse by the next
356
+ * push. The base lex mode is never popped, so popping it instead resets it to
357
+ * the default mode.
380
358
  */
381
359
  static void
382
360
  lex_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];
361
+ pm_lex_mode_t *popped = parser->lex_modes.current;
362
+
363
+ if (popped == &parser->lex_modes.base) {
364
+ popped->mode = PM_LEX_DEFAULT;
388
365
  } 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;
366
+ parser->lex_modes.current = popped->prev;
367
+ popped->prev = parser->lex_modes.free;
368
+ parser->lex_modes.free = popped;
393
369
  }
394
370
  }
395
371
 
@@ -726,7 +702,7 @@ pm_parser_scope_push(pm_parser_t *parser, bool closed) {
726
702
  memset(locals.locals, 0, locals.capacity * sizeof(pm_local_t));
727
703
  }
728
704
  } else {
729
- scope = (pm_scope_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_scope_t), PRISM_ALIGNOF(pm_scope_t));
705
+ scope = (pm_scope_t *) pm_arena_alloc(&parser->scratch_arena, sizeof(pm_scope_t), PRISM_ALIGNOF(pm_scope_t));
730
706
  }
731
707
 
732
708
  *scope = (pm_scope_t) {
@@ -2452,7 +2428,7 @@ pm_arguments_node_arguments_append(pm_arena_t *arena, pm_arguments_node_t *node,
2452
2428
  PM_NODE_LENGTH_SET_NODE(node, argument);
2453
2429
  }
2454
2430
 
2455
- pm_node_list_append(arena, &node->arguments, argument);
2431
+ pm_node_list_append_inline(arena, &node->arguments, argument);
2456
2432
 
2457
2433
  if (PM_NODE_TYPE_P(argument, PM_SPLAT_NODE)) {
2458
2434
  if (PM_NODE_FLAG_P(node, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_SPLAT)) {
@@ -2500,7 +2476,7 @@ pm_array_node_elements_append(pm_arena_t *arena, pm_array_node_t *node, pm_node_
2500
2476
  PM_NODE_START_SET_NODE(node, element);
2501
2477
  }
2502
2478
 
2503
- pm_node_list_append(arena, &node->elements, element);
2479
+ pm_node_list_append_inline(arena, &node->elements, element);
2504
2480
  PM_NODE_LENGTH_SET_NODE(node, element);
2505
2481
 
2506
2482
  // If the element is not a static literal, then the array is not a static
@@ -2553,9 +2529,9 @@ pm_array_pattern_node_node_list_create(pm_parser_t *parser, pm_node_list_t *node
2553
2529
  node->rest = child;
2554
2530
  found_rest = true;
2555
2531
  } else if (found_rest) {
2556
- pm_node_list_append(parser->arena, &node->posts, child);
2532
+ pm_node_list_append_inline(parser->arena, &node->posts, child);
2557
2533
  } else {
2558
- pm_node_list_append(parser->arena, &node->requireds, child);
2534
+ pm_node_list_append_inline(parser->arena, &node->requireds, child);
2559
2535
  }
2560
2536
  }
2561
2537
 
@@ -2623,7 +2599,7 @@ pm_array_pattern_node_empty_create(pm_parser_t *parser, const pm_token_t *openin
2623
2599
 
2624
2600
  static PRISM_INLINE void
2625
2601
  pm_array_pattern_node_requireds_append(pm_arena_t *arena, pm_array_pattern_node_t *node, pm_node_t *inner) {
2626
- pm_node_list_append(arena, &node->requireds, inner);
2602
+ pm_node_list_append_inline(arena, &node->requireds, inner);
2627
2603
  }
2628
2604
 
2629
2605
  /**
@@ -2887,7 +2863,7 @@ pm_block_local_variable_node_create(pm_parser_t *parser, const pm_token_t *name)
2887
2863
  */
2888
2864
  static void
2889
2865
  pm_block_parameters_node_append_local(pm_arena_t *arena, pm_block_parameters_node_t *node, const pm_block_local_variable_node_t *local) {
2890
- pm_node_list_append(arena, &node->locals, UP(local));
2866
+ pm_node_list_append_inline(arena, &node->locals, UP(local));
2891
2867
 
2892
2868
  if (PM_NODE_LENGTH(node) == 0) {
2893
2869
  PM_NODE_START_SET_NODE(node, local);
@@ -3540,7 +3516,7 @@ static void
3540
3516
  pm_case_node_condition_append(pm_arena_t *arena, pm_case_node_t *node, pm_node_t *condition) {
3541
3517
  assert(PM_NODE_TYPE_P(condition, PM_WHEN_NODE));
3542
3518
 
3543
- pm_node_list_append(arena, &node->conditions, condition);
3519
+ pm_node_list_append_inline(arena, &node->conditions, condition);
3544
3520
  PM_NODE_LENGTH_SET_NODE(node, condition);
3545
3521
  }
3546
3522
 
@@ -3587,7 +3563,7 @@ static void
3587
3563
  pm_case_match_node_condition_append(pm_arena_t *arena, pm_case_match_node_t *node, pm_node_t *condition) {
3588
3564
  assert(PM_NODE_TYPE_P(condition, PM_IN_NODE));
3589
3565
 
3590
- pm_node_list_append(arena, &node->conditions, condition);
3566
+ pm_node_list_append_inline(arena, &node->conditions, condition);
3591
3567
  PM_NODE_LENGTH_SET_NODE(node, condition);
3592
3568
  }
3593
3569
 
@@ -4127,7 +4103,7 @@ pm_find_pattern_node_create(pm_parser_t *parser, pm_node_list_t *nodes) {
4127
4103
  // much more efficient, as we could instead resize the node list to only point
4128
4104
  // to 1...-1.
4129
4105
  for (size_t index = 1; index < nodes->size - 1; index++) {
4130
- pm_node_list_append(parser->arena, &node->requireds, nodes->nodes[index]);
4106
+ pm_node_list_append_inline(parser->arena, &node->requireds, nodes->nodes[index]);
4131
4107
  }
4132
4108
 
4133
4109
  return node;
@@ -4624,7 +4600,7 @@ pm_hash_node_create(pm_parser_t *parser, const pm_token_t *opening) {
4624
4600
  */
4625
4601
  static PRISM_INLINE void
4626
4602
  pm_hash_node_elements_append(pm_arena_t *arena, pm_hash_node_t *hash, pm_node_t *element) {
4627
- pm_node_list_append(arena, &hash->elements, element);
4603
+ pm_node_list_append_inline(arena, &hash->elements, element);
4628
4604
 
4629
4605
  bool static_literal = PM_NODE_TYPE_P(element, PM_ASSOC_NODE);
4630
4606
  if (static_literal) {
@@ -5049,7 +5025,7 @@ pm_interpolated_node_append(pm_arena_t *arena, pm_node_t *node, pm_node_list_t *
5049
5025
  break;
5050
5026
  }
5051
5027
 
5052
- pm_node_list_append(arena, parts, part);
5028
+ pm_node_list_append_inline(arena, parts, part);
5053
5029
  }
5054
5030
 
5055
5031
  /**
@@ -5191,7 +5167,7 @@ pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_n
5191
5167
  // These will only happen in error cases. But we want to handle it
5192
5168
  // here so that we don't fail the assertion.
5193
5169
  CLEAR_FLAGS(node);
5194
- pm_node_list_append(arena, &node->parts, UP(pm_error_recovery_node_create_unexpected(parser, part)));
5170
+ pm_node_list_append_inline(arena, &node->parts, UP(pm_error_recovery_node_create_unexpected(parser, part)));
5195
5171
  return;
5196
5172
  case PM_ERROR_RECOVERY_NODE:
5197
5173
  CLEAR_FLAGS(node);
@@ -5201,7 +5177,7 @@ pm_interpolated_string_node_append(pm_parser_t *parser, pm_interpolated_string_n
5201
5177
  break;
5202
5178
  }
5203
5179
 
5204
- pm_node_list_append(arena, &node->parts, part);
5180
+ pm_node_list_append_inline(arena, &node->parts, part);
5205
5181
 
5206
5182
  #undef CLEAR_FLAGS
5207
5183
  #undef MUTABLE_FLAGS
@@ -5381,7 +5357,7 @@ pm_keyword_hash_node_elements_append(pm_arena_t *arena, pm_keyword_hash_node_t *
5381
5357
  pm_node_flag_unset(UP(hash), PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS);
5382
5358
  }
5383
5359
 
5384
- pm_node_list_append(arena, &hash->elements, element);
5360
+ pm_node_list_append_inline(arena, &hash->elements, element);
5385
5361
  if (PM_NODE_LENGTH(hash) == 0) {
5386
5362
  PM_NODE_START_SET_NODE(hash, element);
5387
5363
  }
@@ -5729,19 +5705,19 @@ pm_multi_target_node_targets_append(pm_parser_t *parser, pm_multi_target_node_t
5729
5705
  node->rest = target;
5730
5706
  } else {
5731
5707
  pm_parser_err_node(parser, target, PM_ERR_MULTI_ASSIGN_MULTI_SPLATS);
5732
- pm_node_list_append(parser->arena, &node->rights, target);
5708
+ pm_node_list_append_inline(parser->arena, &node->rights, target);
5733
5709
  }
5734
5710
  } else if (PM_NODE_TYPE_P(target, PM_IMPLICIT_REST_NODE)) {
5735
5711
  if (node->rest == NULL) {
5736
5712
  node->rest = target;
5737
5713
  } else {
5738
5714
  PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, &parser->current, PM_ERR_MULTI_ASSIGN_UNEXPECTED_REST);
5739
- pm_node_list_append(parser->arena, &node->rights, target);
5715
+ pm_node_list_append_inline(parser->arena, &node->rights, target);
5740
5716
  }
5741
5717
  } else if (node->rest == NULL) {
5742
- pm_node_list_append(parser->arena, &node->lefts, target);
5718
+ pm_node_list_append_inline(parser->arena, &node->lefts, target);
5743
5719
  } else {
5744
- pm_node_list_append(parser->arena, &node->rights, target);
5720
+ pm_node_list_append_inline(parser->arena, &node->rights, target);
5745
5721
  }
5746
5722
 
5747
5723
  if (PM_NODE_LENGTH(node) == 0 || (PM_NODE_START(node) > PM_NODE_START(target))) {
@@ -6016,7 +5992,7 @@ pm_parameters_node_location_set(pm_parameters_node_t *params, pm_node_t *param)
6016
5992
  static void
6017
5993
  pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6018
5994
  pm_parameters_node_location_set(params, param);
6019
- pm_node_list_append(arena, &params->requireds, param);
5995
+ pm_node_list_append_inline(arena, &params->requireds, param);
6020
5996
  }
6021
5997
 
6022
5998
  /**
@@ -6025,7 +6001,7 @@ pm_parameters_node_requireds_append(pm_arena_t *arena, pm_parameters_node_t *par
6025
6001
  static void
6026
6002
  pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_optional_parameter_node_t *param) {
6027
6003
  pm_parameters_node_location_set(params, UP(param));
6028
- pm_node_list_append(arena, &params->optionals, UP(param));
6004
+ pm_node_list_append_inline(arena, &params->optionals, UP(param));
6029
6005
  }
6030
6006
 
6031
6007
  /**
@@ -6034,7 +6010,7 @@ pm_parameters_node_optionals_append(pm_arena_t *arena, pm_parameters_node_t *par
6034
6010
  static void
6035
6011
  pm_parameters_node_posts_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6036
6012
  pm_parameters_node_location_set(params, param);
6037
- pm_node_list_append(arena, &params->posts, param);
6013
+ pm_node_list_append_inline(arena, &params->posts, param);
6038
6014
  }
6039
6015
 
6040
6016
  /**
@@ -6052,7 +6028,7 @@ pm_parameters_node_rest_set(pm_parameters_node_t *params, pm_node_t *param) {
6052
6028
  static void
6053
6029
  pm_parameters_node_keywords_append(pm_arena_t *arena, pm_parameters_node_t *params, pm_node_t *param) {
6054
6030
  pm_parameters_node_location_set(params, param);
6055
- pm_node_list_append(arena, &params->keywords, param);
6031
+ pm_node_list_append_inline(arena, &params->keywords, param);
6056
6032
  }
6057
6033
 
6058
6034
  /**
@@ -6340,7 +6316,7 @@ pm_rescue_node_subsequent_set(pm_rescue_node_t *node, pm_rescue_node_t *subseque
6340
6316
  */
6341
6317
  static void
6342
6318
  pm_rescue_node_exceptions_append(pm_arena_t *arena, pm_rescue_node_t *node, pm_node_t *exception) {
6343
- pm_node_list_append(arena, &node->exceptions, exception);
6319
+ pm_node_list_append_inline(arena, &node->exceptions, exception);
6344
6320
  PM_NODE_LENGTH_SET_NODE(node, exception);
6345
6321
  }
6346
6322
 
@@ -6573,7 +6549,7 @@ pm_statements_node_body_append(pm_parser_t *parser, pm_statements_node_t *node,
6573
6549
  }
6574
6550
  }
6575
6551
 
6576
- pm_node_list_append(parser->arena, &node->body, statement);
6552
+ pm_node_list_append_inline(parser->arena, &node->body, statement);
6577
6553
  if (newline) pm_node_flag_set(statement, PM_NODE_FLAG_NEWLINE);
6578
6554
  }
6579
6555
 
@@ -6820,7 +6796,7 @@ pm_symbol_node_synthesized_create(pm_parser_t *parser, const char *content) {
6820
6796
  ((pm_string_t) { 0 })
6821
6797
  );
6822
6798
 
6823
- pm_string_constant_init(&node->unescaped, content, strlen(content));
6799
+ pm_string_init(&node->unescaped, (const uint8_t *) content, strlen(content));
6824
6800
  return node;
6825
6801
  }
6826
6802
 
@@ -6963,7 +6939,7 @@ pm_undef_node_create(pm_parser_t *parser, const pm_token_t *token) {
6963
6939
  static void
6964
6940
  pm_undef_node_append(pm_arena_t *arena, pm_undef_node_t *node, pm_node_t *name) {
6965
6941
  PM_NODE_LENGTH_SET_NODE(node, name);
6966
- pm_node_list_append(arena, &node->names, name);
6942
+ pm_node_list_append_inline(arena, &node->names, name);
6967
6943
  }
6968
6944
 
6969
6945
  /**
@@ -7105,7 +7081,7 @@ pm_when_node_create(pm_parser_t *parser, const pm_token_t *keyword) {
7105
7081
  static void
7106
7082
  pm_when_node_conditions_append(pm_arena_t *arena, pm_when_node_t *node, pm_node_t *condition) {
7107
7083
  PM_NODE_LENGTH_SET_NODE(node, condition);
7108
- pm_node_list_append(arena, &node->conditions, condition);
7084
+ pm_node_list_append_inline(arena, &node->conditions, condition);
7109
7085
  }
7110
7086
 
7111
7087
  /**
@@ -7279,7 +7255,7 @@ pm_parser_local_depth(pm_parser_t *parser, pm_token_t *token) {
7279
7255
  */
7280
7256
  static PRISM_INLINE void
7281
7257
  pm_parser_local_add(pm_parser_t *parser, pm_constant_id_t constant_id, const uint8_t *start, const uint8_t *end, uint32_t reads) {
7282
- pm_locals_write(&parser->metadata_arena, &parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
7258
+ pm_locals_write(&parser->scratch_arena, &parser->current_scope->locals, constant_id, U32(start - parser->start), U32(end - start), reads);
7283
7259
  }
7284
7260
 
7285
7261
  /**
@@ -7692,6 +7668,27 @@ parser_lex_magic_comment_emacs_marker(pm_parser_t *parser, const uint8_t *cursor
7692
7668
  return NULL;
7693
7669
  }
7694
7670
 
7671
+ /**
7672
+ * Returns true if the given magic comment key matches the given name, which is
7673
+ * written in lowercase with underscores and is the same length as the key.
7674
+ * Keys match case-insensitively, and a dash in the key matches an underscore in
7675
+ * the name. Every byte of a name is ASCII, so a byte of a multibyte character
7676
+ * in the key never matches one.
7677
+ */
7678
+ static bool
7679
+ parser_magic_comment_key_p(const uint8_t *key, size_t key_length, const char *name) {
7680
+ assert(name[key_length] == '\0');
7681
+
7682
+ for (size_t index = 0; index < key_length; index++) {
7683
+ uint8_t byte = key[index];
7684
+ if (byte == '-') byte = '_';
7685
+ if ('A' <= byte && byte <= 'Z') byte |= 0x20;
7686
+ if (byte != (uint8_t) name[index]) return false;
7687
+ }
7688
+
7689
+ return true;
7690
+ }
7691
+
7695
7692
  /**
7696
7693
  * Parse the current token on the parser to see if it's a magic comment and
7697
7694
  * potentially perform some action based on that. A regular expression that this
@@ -7783,73 +7780,34 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
7783
7780
  if (cursor != end) return false;
7784
7781
  }
7785
7782
 
7786
- // Here, we need to do some processing on the key to swap out dashes for
7787
- // underscores. We only need to do this if there _is_ a dash in the key.
7788
- pm_string_t key;
7783
+ /* Finally, we can start checking the key against the list of known
7784
+ * magic comment keys, and potentially change state based on that. */
7789
7785
  const size_t key_length = (size_t) (key_end - key_start);
7790
- const uint8_t *dash = pm_memchr(key_start, '-', key_length, parser->encoding_changed, parser->encoding);
7791
-
7792
- if (dash == NULL) {
7793
- pm_string_shared_init(&key, key_start, key_end);
7794
- } else {
7795
- uint8_t *buffer = xmalloc(key_length);
7796
- if (buffer == NULL) break;
7797
-
7798
- memcpy(buffer, key_start, key_length);
7799
- buffer[dash - key_start] = '_';
7800
-
7801
- while ((dash = pm_memchr(dash + 1, '-', (size_t) (key_end - dash - 1), parser->encoding_changed, parser->encoding)) != NULL) {
7802
- buffer[dash - key_start] = '_';
7803
- }
7804
-
7805
- pm_string_owned_init(&key, buffer, key_length);
7806
- }
7807
-
7808
- // Finally, we can start checking the key against the list of known
7809
- // magic comment keys, and potentially change state based on that.
7810
- const uint8_t *key_source = pm_string_source(&key);
7811
7786
  uint32_t value_length = (uint32_t) (value_end - value_start);
7812
7787
 
7813
- // We only want to attempt to compare against encoding comments if it's
7814
- // the first line in the file (or the second in the case of a shebang).
7815
- if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7816
- if (
7817
- (key_length == 8 && pm_strncasecmp(key_source, (const uint8_t *) "encoding", 8) == 0) ||
7818
- (key_length == 6 && pm_strncasecmp(key_source, (const uint8_t *) "coding", 6) == 0)
7819
- ) {
7820
- result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7821
- }
7822
- }
7823
-
7824
- if (key_length == 11) {
7825
- if (pm_strncasecmp(key_source, (const uint8_t *) "warn_indent", 11) == 0) {
7826
- switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7827
- case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7828
- PM_PARSER_WARN_TOKEN_FORMAT(
7829
- parser,
7830
- &parser->current,
7831
- PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7832
- (int) key_length,
7833
- (const char *) key_source,
7834
- (int) value_length,
7835
- (const char *) value_start
7836
- );
7837
- break;
7838
- case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7839
- parser->warn_mismatched_indentation = false;
7840
- break;
7841
- case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7842
- parser->warn_mismatched_indentation = true;
7843
- break;
7788
+ switch (key_length) {
7789
+ case 6:
7790
+ /* We only want to attempt to compare against encoding comments
7791
+ * if it is the first line in the file (or the second in the
7792
+ * case of a shebang). */
7793
+ if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7794
+ if (parser_magic_comment_key_p(key_start, key_length, "coding")) {
7795
+ result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7796
+ }
7844
7797
  }
7845
- }
7846
- } else if (key_length == 21) {
7847
- if (pm_strncasecmp(key_source, (const uint8_t *) "frozen_string_literal", 21) == 0) {
7848
- // We only want to handle frozen string literal comments if it's
7849
- // before any semantic tokens have been seen.
7850
- if (semantic_token_seen) {
7851
- pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
7852
- } else {
7798
+ break;
7799
+ case 8:
7800
+ /* We only want to attempt to compare against encoding comments
7801
+ * if it is the first line in the file (or the second in the
7802
+ * case of a shebang). */
7803
+ if (parser->current.start == parser->encoding_comment_start && !parser->encoding_locked) {
7804
+ if (parser_magic_comment_key_p(key_start, key_length, "encoding")) {
7805
+ result = parser_lex_magic_comment_encoding_value(parser, value_start, value_end);
7806
+ }
7807
+ }
7808
+ break;
7809
+ case 11:
7810
+ if (parser_magic_comment_key_p(key_start, key_length, "warn_indent")) {
7853
7811
  switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7854
7812
  case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7855
7813
  PM_PARSER_WARN_TOKEN_FORMAT(
@@ -7857,53 +7815,79 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
7857
7815
  &parser->current,
7858
7816
  PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7859
7817
  (int) key_length,
7860
- (const char *) key_source,
7818
+ "warn_indent",
7861
7819
  (int) value_length,
7862
7820
  (const char *) value_start
7863
7821
  );
7864
7822
  break;
7865
7823
  case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7866
- parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
7824
+ parser->warn_mismatched_indentation = false;
7867
7825
  break;
7868
7826
  case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7869
- parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
7827
+ parser->warn_mismatched_indentation = true;
7870
7828
  break;
7871
7829
  }
7872
7830
  }
7873
- }
7874
- } else if (key_length == 24) {
7875
- if (pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) {
7876
- const uint8_t *cursor = parser->current.start;
7877
- while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
7878
-
7879
- if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
7880
- pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
7881
- } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
7882
- pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
7883
- } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
7884
- pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
7885
- } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
7886
- pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
7887
- } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
7888
- pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
7889
- } else {
7890
- PM_PARSER_WARN_TOKEN_FORMAT(
7891
- parser,
7892
- &parser->current,
7893
- PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7894
- (int) key_length,
7895
- (const char *) key_source,
7896
- (int) value_length,
7897
- (const char *) value_start
7898
- );
7831
+ break;
7832
+ case 21:
7833
+ if (parser_magic_comment_key_p(key_start, key_length, "frozen_string_literal")) {
7834
+ /* We only want to handle frozen string literal comments if
7835
+ * it is before any semantic tokens have been seen. */
7836
+ if (semantic_token_seen) {
7837
+ pm_parser_warn_token(parser, &parser->current, PM_WARN_IGNORED_FROZEN_STRING_LITERAL);
7838
+ } else {
7839
+ switch (parser_lex_magic_comment_boolean_value(value_start, value_length)) {
7840
+ case PM_MAGIC_COMMENT_BOOLEAN_VALUE_INVALID:
7841
+ PM_PARSER_WARN_TOKEN_FORMAT(
7842
+ parser,
7843
+ &parser->current,
7844
+ PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7845
+ (int) key_length,
7846
+ "frozen_string_literal",
7847
+ (int) value_length,
7848
+ (const char *) value_start
7849
+ );
7850
+ break;
7851
+ case PM_MAGIC_COMMENT_BOOLEAN_VALUE_FALSE:
7852
+ parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_DISABLED;
7853
+ break;
7854
+ case PM_MAGIC_COMMENT_BOOLEAN_VALUE_TRUE:
7855
+ parser->frozen_string_literal = PM_OPTIONS_FROZEN_STRING_LITERAL_ENABLED;
7856
+ break;
7857
+ }
7858
+ }
7899
7859
  }
7900
- }
7860
+ break;
7861
+ case 24:
7862
+ if (parser_magic_comment_key_p(key_start, key_length, "shareable_constant_value")) {
7863
+ const uint8_t *cursor = parser->current.start;
7864
+ while ((cursor > parser->start) && ((cursor[-1] == ' ') || (cursor[-1] == '\t'))) cursor--;
7865
+
7866
+ if (!((cursor == parser->start) || (cursor[-1] == '\n'))) {
7867
+ pm_parser_warn_token(parser, &parser->current, PM_WARN_SHAREABLE_CONSTANT_VALUE_LINE);
7868
+ } else if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
7869
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
7870
+ } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
7871
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
7872
+ } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
7873
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
7874
+ } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
7875
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
7876
+ } else {
7877
+ PM_PARSER_WARN_TOKEN_FORMAT(
7878
+ parser,
7879
+ &parser->current,
7880
+ PM_WARN_INVALID_MAGIC_COMMENT_VALUE,
7881
+ (int) key_length,
7882
+ "shareable_constant_value",
7883
+ (int) value_length,
7884
+ (const char *) value_start
7885
+ );
7886
+ }
7887
+ }
7888
+ break;
7901
7889
  }
7902
7890
 
7903
- // When we're done, we want to free the string in case we had to
7904
- // allocate memory for it.
7905
- pm_string_cleanup(&key);
7906
-
7907
7891
  // Allocate a new magic comment node to append to the parser's list.
7908
7892
  pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_magic_comment_t), PRISM_ALIGNOF(pm_magic_comment_t));
7909
7893
  magic_comment->node.next = NULL;
@@ -8006,7 +7990,7 @@ context_push(pm_parser_t *parser, pm_context_t context) {
8006
7990
  if (context_node != NULL) {
8007
7991
  parser->free_contexts = context_node->prev;
8008
7992
  } else {
8009
- context_node = (pm_context_node_t *) pm_arena_alloc(&parser->metadata_arena, sizeof(pm_context_node_t), PRISM_ALIGNOF(pm_context_node_t));
7993
+ context_node = (pm_context_node_t *) pm_arena_alloc(&parser->scratch_arena, sizeof(pm_context_node_t), PRISM_ALIGNOF(pm_context_node_t));
8010
7994
  }
8011
7995
 
8012
7996
  pm_context_node_t *previous = parser->current_context;
@@ -9650,7 +9634,7 @@ lex_question_mark(pm_parser_t *parser) {
9650
9634
 
9651
9635
  // Copy buffer data into the arena and free the heap buffer.
9652
9636
  void *arena_data = pm_arena_memdup(parser->arena, buffer.value, buffer.length, PRISM_ALIGNOF(uint8_t));
9653
- pm_string_constant_init(&parser->current_string, (const char *) arena_data, buffer.length);
9637
+ pm_string_init(&parser->current_string, (const uint8_t *) arena_data, buffer.length);
9654
9638
  pm_buffer_cleanup(&buffer);
9655
9639
 
9656
9640
  return PM_TOKEN_CHARACTER_LITERAL;
@@ -9974,7 +9958,7 @@ pm_token_buffer_copy(pm_parser_t *parser, pm_token_buffer_t *token_buffer) {
9974
9958
  // Copy buffer data into the arena and free the heap buffer.
9975
9959
  size_t len = pm_buffer_length(&token_buffer->buffer);
9976
9960
  void *arena_data = pm_arena_memdup(parser->arena, pm_buffer_value(&token_buffer->buffer), len, PRISM_ALIGNOF(uint8_t));
9977
- pm_string_constant_init(&parser->current_string, (const char *) arena_data, len);
9961
+ pm_string_init(&parser->current_string, (const uint8_t *) arena_data, len);
9978
9962
  pm_buffer_cleanup(&token_buffer->buffer);
9979
9963
  }
9980
9964
 
@@ -13187,7 +13171,7 @@ parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power,
13187
13171
  return parse_value_expression(parser, binding_power, flags, diag_id, depth);
13188
13172
  }
13189
13173
 
13190
- static bool
13174
+ static pm_visit_result_t
13191
13175
  pm_node_unreference_each(const pm_node_t *node, void *data) {
13192
13176
  switch (PM_NODE_TYPE(node)) {
13193
13177
  /* When we are about to destroy a set of nodes that could potentially
@@ -13213,15 +13197,15 @@ pm_node_unreference_each(const pm_node_t *node, void *data) {
13213
13197
  }
13214
13198
  parser->current_block_exits->size--;
13215
13199
 
13216
- /* Note returning true here because these nodes could have
13200
+ /* Note descending here because these nodes could have
13217
13201
  * arguments that are themselves block exits. */
13218
- return true;
13202
+ return PM_VISIT_DESCEND;
13219
13203
  }
13220
13204
 
13221
13205
  index++;
13222
13206
  }
13223
13207
 
13224
- return true;
13208
+ return PM_VISIT_DESCEND;
13225
13209
  }
13226
13210
  /* When an implicit local variable is written to or targeted, it becomes
13227
13211
  * a regular, named local variable. This branch removes it from the list
@@ -13245,10 +13229,10 @@ pm_node_unreference_each(const pm_node_t *node, void *data) {
13245
13229
  }
13246
13230
  }
13247
13231
 
13248
- return false;
13232
+ return PM_VISIT_SKIP;
13249
13233
  }
13250
13234
  default:
13251
- return true;
13235
+ return PM_VISIT_DESCEND;
13252
13236
  }
13253
13237
  }
13254
13238
 
@@ -13899,7 +13883,7 @@ parse_statements(pm_parser_t *parser, pm_context_t context, uint16_t depth) {
13899
13883
  static void
13900
13884
  pm_hash_key_duplicated_warn(pm_parser_t *parser, const pm_node_t *duplicated, const pm_node_t *node) {
13901
13885
  pm_buffer_t buffer = { 0 };
13902
- pm_static_literal_inspect(&buffer, &parser->line_offsets, parser->start, parser->start_line, parser->encoding, duplicated);
13886
+ pm_static_literal_inspect(&buffer, parser, duplicated);
13903
13887
 
13904
13888
  pm_diagnostic_list_append_format(
13905
13889
  &parser->metadata_arena,
@@ -13921,7 +13905,7 @@ pm_hash_key_duplicated_warn(pm_parser_t *parser, const pm_node_t *duplicated, co
13921
13905
  */
13922
13906
  static void
13923
13907
  pm_hash_key_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13924
- const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, true);
13908
+ const pm_node_t *duplicated = pm_static_literals_add(parser, literals, node, true);
13925
13909
 
13926
13910
  if (duplicated != NULL) {
13927
13911
  pm_hash_key_duplicated_warn(parser, duplicated, node);
@@ -13945,7 +13929,7 @@ pm_hash_key_static_literals_merge(pm_parser_t *parser, pm_static_literals_t *lit
13945
13929
  switch (PM_NODE_TYPE(element)) {
13946
13930
  case PM_ASSOC_NODE: {
13947
13931
  pm_node_t *key = ((pm_assoc_node_t *) element)->key;
13948
- const pm_node_t *duplicated = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, key, true);
13932
+ const pm_node_t *duplicated = pm_static_literals_add(parser, literals, key, true);
13949
13933
 
13950
13934
  if (duplicated != NULL && PM_NODE_START(duplicated) < boundary) {
13951
13935
  pm_hash_key_duplicated_warn(parser, duplicated, key);
@@ -13976,7 +13960,7 @@ static void
13976
13960
  pm_when_clause_static_literals_add(pm_parser_t *parser, pm_static_literals_t *literals, pm_node_t *node) {
13977
13961
  pm_node_t *previous;
13978
13962
 
13979
- if ((previous = pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, literals, node, false)) != NULL) {
13963
+ if ((previous = pm_static_literals_add(parser, literals, node, false)) != NULL) {
13980
13964
  pm_diagnostic_list_append_format(
13981
13965
  &parser->metadata_arena,
13982
13966
  &parser->warning_list,
@@ -15990,7 +15974,7 @@ ensure_valid_jump(pm_parser_t *parser, pm_node_t *node) {
15990
15974
  // block exit to the list of exits for the expression, and
15991
15975
  // the node parsing will handle validating it instead.
15992
15976
  assert(parser->current_block_exits != NULL);
15993
- pm_node_list_append(parser->arena, parser->current_block_exits, node);
15977
+ pm_node_list_append_inline(&parser->scratch_arena, parser->current_block_exits, node);
15994
15978
  return;
15995
15979
  case PM_CONTEXT_BEGIN_ELSE:
15996
15980
  case PM_CONTEXT_BEGIN_ENSURE:
@@ -16082,7 +16066,7 @@ pop_block_exits(pm_parser_t *parser, pm_node_list_t *previous_block_exits) {
16082
16066
  // However, they could still become valid in a higher level context if
16083
16067
  // there is another list above this one. In this case we'll push all of
16084
16068
  // the block exits up to the previous list.
16085
- pm_node_list_concat(parser->arena, previous_block_exits, parser->current_block_exits);
16069
+ pm_node_list_concat(&parser->scratch_arena, previous_block_exits, parser->current_block_exits);
16086
16070
  parser->current_block_exits = previous_block_exits;
16087
16071
  } else {
16088
16072
  // If we did not match a trailing while/until and this was the last
@@ -16728,12 +16712,12 @@ parse_variable(pm_parser_t *parser) {
16728
16712
  }
16729
16713
 
16730
16714
  pm_node_t *node = UP(pm_local_variable_read_node_create_constant_id(parser, &parser->previous, name_id, 0, false));
16731
- pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16715
+ pm_node_list_append_inline(&parser->scratch_arena, &current_scope->implicit_parameters, node);
16732
16716
 
16733
16717
  return node;
16734
16718
  } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16735
16719
  pm_node_t *node = UP(pm_it_local_variable_read_node_create(parser, &parser->previous));
16736
- pm_node_list_append(parser->arena, &current_scope->implicit_parameters, node);
16720
+ pm_node_list_append_inline(&parser->scratch_arena, &current_scope->implicit_parameters, node);
16737
16721
 
16738
16722
  return node;
16739
16723
  }
@@ -16790,18 +16774,13 @@ parse_method_definition_name(pm_parser_t *parser) {
16790
16774
 
16791
16775
  static void
16792
16776
  parse_heredoc_dedent_string(pm_arena_t *arena, pm_string_t *string, size_t common_whitespace) {
16793
- // Make a writable copy in the arena if the string isn't already writable.
16794
- // We keep a mutable pointer to the arena memory so we can memmove into it
16795
- // below without casting away const from the string's source field.
16796
- uint8_t *writable;
16797
-
16798
- if (string->type != PM_STRING_OWNED) {
16799
- size_t length = pm_string_length(string);
16800
- writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
16801
- pm_string_constant_init(string, (const char *) writable, length);
16802
- } else {
16803
- writable = (uint8_t *) string->source;
16804
- }
16777
+ // Make a writable copy of the string in the arena, since strings only view
16778
+ // their bytes. We keep a mutable pointer to the arena memory so we can
16779
+ // memmove into it below without casting away const from the string's
16780
+ // source field.
16781
+ size_t length = pm_string_length(string);
16782
+ uint8_t *writable = (uint8_t *) pm_arena_memdup(arena, pm_string_source(string), length, PRISM_ALIGNOF(uint8_t));
16783
+ pm_string_init(string, writable, length);
16805
16784
 
16806
16785
  // Now get the bounds of the existing string. We'll use this as a
16807
16786
  // destination to move bytes into. We'll also use it for bounds checking
@@ -16966,11 +16945,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
16966
16945
  if (match1(parser, PM_TOKEN_STRING_CONTENT)) {
16967
16946
  pm_node_list_t parts = { 0 };
16968
16947
  pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &content, NULL, &unescaped));
16969
- pm_node_list_append(parser->arena, &parts, part);
16948
+ pm_node_list_append_inline(parser->arena, &parts, part);
16970
16949
 
16971
16950
  do {
16972
16951
  part = UP(pm_string_node_create_current_string(parser, NULL, &parser->current, NULL));
16973
- pm_node_list_append(parser->arena, &parts, part);
16952
+ pm_node_list_append_inline(parser->arena, &parts, part);
16974
16953
  parser_lex(parser);
16975
16954
  } while (match1(parser, PM_TOKEN_STRING_CONTENT));
16976
16955
 
@@ -17025,11 +17004,11 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
17025
17004
  pm_node_list_t parts = { 0 };
17026
17005
  pm_node_t *part = UP(pm_string_node_create_unescaped(parser, NULL, &parser->previous, NULL, &unescaped));
17027
17006
  pm_node_flag_set(part, parse_unescaped_encoding(parser, explicit_encoding));
17028
- pm_node_list_append(parser->arena, &parts, part);
17007
+ pm_node_list_append_inline(parser->arena, &parts, part);
17029
17008
 
17030
17009
  while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
17031
17010
  if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
17032
- pm_node_list_append(parser->arena, &parts, part);
17011
+ pm_node_list_append_inline(parser->arena, &parts, part);
17033
17012
  }
17034
17013
  }
17035
17014
 
@@ -17053,7 +17032,7 @@ parse_strings(pm_parser_t *parser, pm_node_t *current, bool accepts_label, uint1
17053
17032
 
17054
17033
  while (!match3(parser, PM_TOKEN_STRING_END, PM_TOKEN_LABEL_END, PM_TOKEN_EOF)) {
17055
17034
  if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
17056
- pm_node_list_append(parser->arena, &parts, part);
17035
+ pm_node_list_append_inline(parser->arena, &parts, part);
17057
17036
  }
17058
17037
  }
17059
17038
 
@@ -17125,7 +17104,7 @@ parse_pattern_capture(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_co
17125
17104
  // Skip this capture if it starts with an underscore.
17126
17105
  if (peek_at(parser, parser->start + location->start) == '_') return;
17127
17106
 
17128
- if (!pm_constant_id_set_insert(parser->arena, captures, capture)) {
17107
+ if (!pm_constant_id_set_insert(&parser->scratch_arena, captures, capture)) {
17129
17108
  pm_parser_err(parser, location->start, location->length, PM_ERR_PATTERN_CAPTURE_DUPLICATE);
17130
17109
  }
17131
17110
  }
@@ -17391,7 +17370,7 @@ parse_pattern_hash_implicit_value(pm_parser_t *parser, pm_constant_id_set_t *cap
17391
17370
  */
17392
17371
  static void
17393
17372
  parse_pattern_hash_key(pm_parser_t *parser, pm_static_literals_t *keys, pm_node_t *node) {
17394
- if (pm_static_literals_add(&parser->line_offsets, parser->start, parser->start_line, parser->encoding, keys, node, true) != NULL) {
17373
+ if (pm_static_literals_add(parser, keys, node, true) != NULL) {
17395
17374
  pm_parser_err_node(parser, node, PM_ERR_PATTERN_HASH_KEY_DUPLICATE);
17396
17375
  }
17397
17376
  }
@@ -17439,7 +17418,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
17439
17418
  }
17440
17419
 
17441
17420
  pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17442
- pm_node_list_append(parser->arena, &assocs, assoc);
17421
+ pm_node_list_append_inline(parser->arena, &assocs, assoc);
17443
17422
  break;
17444
17423
  }
17445
17424
  }
@@ -17454,7 +17433,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
17454
17433
  pm_node_t *value = UP(pm_error_recovery_node_create(parser, PM_NODE_START(first_node), PM_NODE_LENGTH(first_node)));
17455
17434
  pm_node_t *assoc = UP(pm_assoc_node_create(parser, first_node, NULL, value));
17456
17435
 
17457
- pm_node_list_append(parser->arena, &assocs, assoc);
17436
+ pm_node_list_append_inline(parser->arena, &assocs, assoc);
17458
17437
  break;
17459
17438
  }
17460
17439
  }
@@ -17482,7 +17461,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
17482
17461
  rest = assoc;
17483
17462
  } else {
17484
17463
  pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17485
- pm_node_list_append(parser->arena, &assocs, assoc);
17464
+ pm_node_list_append_inline(parser->arena, &assocs, assoc);
17486
17465
  }
17487
17466
  } else {
17488
17467
  pm_node_t *key;
@@ -17528,7 +17507,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_node_
17528
17507
  pm_parser_err_node(parser, assoc, PM_ERR_PATTERN_EXPRESSION_AFTER_REST);
17529
17508
  }
17530
17509
 
17531
- pm_node_list_append(parser->arena, &assocs, assoc);
17510
+ pm_node_list_append_inline(parser->arena, &assocs, assoc);
17532
17511
  }
17533
17512
  }
17534
17513
 
@@ -17808,7 +17787,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_
17808
17787
  * are not captures of this pattern. A nested alternation is not visited
17809
17788
  * either: its operands were already checked when it was parsed.
17810
17789
  */
17811
- static bool
17790
+ static pm_visit_result_t
17812
17791
  parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
17813
17792
  pm_parser_t *parser = (pm_parser_t *) data;
17814
17793
 
@@ -17819,7 +17798,7 @@ parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
17819
17798
  if (peek_at(parser, parser->start + PM_NODE_START(node)) != '_') {
17820
17799
  pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
17821
17800
  }
17822
- return false;
17801
+ return PM_VISIT_SKIP;
17823
17802
  case PM_ARRAY_PATTERN_NODE:
17824
17803
  case PM_ASSOC_NODE:
17825
17804
  case PM_ASSOC_SPLAT_NODE:
@@ -17829,9 +17808,9 @@ parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
17829
17808
  case PM_IMPLICIT_NODE:
17830
17809
  case PM_PARENTHESES_NODE:
17831
17810
  case PM_SPLAT_NODE:
17832
- return true;
17811
+ return PM_VISIT_DESCEND;
17833
17812
  default:
17834
- return false;
17813
+ return PM_VISIT_SKIP;
17835
17814
  }
17836
17815
  }
17837
17816
 
@@ -18027,7 +18006,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags
18027
18006
  // or a find pattern. We need to parse all of the patterns, put them
18028
18007
  // into a big list, and then determine which type of node we have.
18029
18008
  pm_node_list_t nodes = { 0 };
18030
- pm_node_list_append(parser->arena, &nodes, node);
18009
+ pm_node_list_append_inline(parser->arena, &nodes, node);
18031
18010
 
18032
18011
  // Gather up all of the patterns into the list.
18033
18012
  while (accept1(parser, PM_TOKEN_COMMA)) {
@@ -18046,7 +18025,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags
18046
18025
  }
18047
18026
 
18048
18027
  node = UP(pm_implicit_rest_node_create(parser, &parser->previous));
18049
- pm_node_list_append(parser->arena, &nodes, node);
18028
+ pm_node_list_append_inline(parser->arena, &nodes, node);
18050
18029
  trailing_rest = true;
18051
18030
  break;
18052
18031
  }
@@ -18066,7 +18045,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_set_t *captures, uint8_t flags
18066
18045
  node = parse_pattern_primitives(parser, captures, NULL, PM_ERR_PATTERN_EXPRESSION_AFTER_COMMA, (uint16_t) (depth + 1));
18067
18046
  }
18068
18047
 
18069
- pm_node_list_append(parser->arena, &nodes, node);
18048
+ pm_node_list_append_inline(parser->arena, &nodes, node);
18070
18049
  }
18071
18050
 
18072
18051
  // If the first pattern and the last pattern are rest patterns, then we
@@ -19572,11 +19551,11 @@ parse_heredoc(pm_parser_t *parser, uint16_t depth) {
19572
19551
  // so we'll need to create an interpolated string node to hold
19573
19552
  // them all.
19574
19553
  pm_node_list_t parts = { 0 };
19575
- pm_node_list_append(parser->arena, &parts, part);
19554
+ pm_node_list_append_inline(parser->arena, &parts, part);
19576
19555
 
19577
19556
  while (!match2(parser, PM_TOKEN_HEREDOC_END, PM_TOKEN_EOF)) {
19578
19557
  if ((part = parse_string_part(parser, (uint16_t) (depth + 1))) != NULL) {
19579
- pm_node_list_append(parser->arena, &parts, part);
19558
+ pm_node_list_append_inline(parser->arena, &parts, part);
19580
19559
  }
19581
19560
  }
19582
19561
 
@@ -21818,7 +21797,7 @@ parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *c
21818
21797
  * Add this name to the set of constants if it is valid, not duplicated,
21819
21798
  * and not a keyword.
21820
21799
  */
21821
- if (name != 0 && pm_constant_id_set_insert(parser->arena, names, name)) {
21800
+ if (name != 0 && pm_constant_id_set_insert(&parser->scratch_arena, names, name)) {
21822
21801
 
21823
21802
  int depth;
21824
21803
  if ((depth = pm_parser_local_depth_constant_id(parser, name)) == -1) {
@@ -21845,7 +21824,7 @@ parse_regular_expression_named_capture(pm_parser_t *parser, const pm_string_t *c
21845
21824
  pm_token_t token = { .type = 0, .start = start, .end = end };
21846
21825
  pm_location_t token_loc = TOK2LOC(parser, &token);
21847
21826
  pm_node_t *target = UP(pm_local_variable_target_node_create(parser, &token_loc, name, depth == -1 ? 0 : (uint32_t) depth));
21848
- pm_node_list_append(parser->arena, &name_data->match->targets, target);
21827
+ pm_node_list_append_inline(parser->arena, &name_data->match->targets, target);
21849
21828
  }
21850
21829
 
21851
21830
  pm_buffer_cleanup(&unescaped);
@@ -22014,9 +21993,7 @@ parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_pow
22014
21993
  }
22015
21994
 
22016
21995
  if (!interpolated && total_length > 0) {
22017
- void *memory = xmalloc(total_length);
22018
- if (!memory) abort();
22019
-
21996
+ uint8_t *memory = (uint8_t *) pm_arena_alloc(&parser->scratch_arena, total_length, PRISM_ALIGNOF(uint8_t));
22020
21997
  uint8_t *cursor = memory;
22021
21998
  PM_NODE_LIST_FOREACH(parts, index, part) {
22022
21999
  pm_string_t *unescaped = &((pm_string_node_t *) part)->unescaped;
@@ -22026,11 +22003,9 @@ parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_pow
22026
22003
  cursor += length;
22027
22004
  }
22028
22005
 
22029
- pm_string_t owned;
22030
- pm_string_owned_init(&owned, (uint8_t *) memory, total_length);
22031
-
22032
- result = parse_interpolated_regular_expression_named_captures(parser, &owned, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
22033
- pm_string_cleanup(&owned);
22006
+ pm_string_t content;
22007
+ pm_string_init(&content, memory, total_length);
22008
+ result = parse_interpolated_regular_expression_named_captures(parser, &content, call, PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED));
22034
22009
  }
22035
22010
  } else if (PM_NODE_TYPE_P(node, PM_REGULAR_EXPRESSION_NODE)) {
22036
22011
  /* If we have a regular expression node, then we can parse the named
@@ -22040,7 +22015,7 @@ parse_match(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t binding_pow
22040
22015
  .call = call,
22041
22016
  .match = NULL,
22042
22017
  .names = { 0 },
22043
- .shared = regexp->unescaped.type == PM_STRING_SHARED,
22018
+ .shared = pm_string_source(&regexp->unescaped) == parser->start + regexp->content_loc.start,
22044
22019
  };
22045
22020
 
22046
22021
  pm_node_flag_set(UP(regexp), pm_regexp_parse(parser, regexp, parse_regular_expression_named_capture, &name_data));
@@ -23279,6 +23254,16 @@ pm_parser_init_shebang(pm_parser_t *parser, const pm_options_t *options, const c
23279
23254
  parser->frozen_string_literal = next_options.frozen_string_literal;
23280
23255
  }
23281
23256
 
23257
+ /**
23258
+ * The smallest capacity the constant pool starts with, whatever the source
23259
+ * size suggests. An embedder that interns a fixed set of names into the pool
23260
+ * at every parse (a symbol table) can raise it to that set's size, so the
23261
+ * pool does not grow to hold them and leave each outgrown array in the arena.
23262
+ */
23263
+ #ifndef PM_CONSTANT_POOL_MIN_CAPACITY
23264
+ #define PM_CONSTANT_POOL_MIN_CAPACITY 4
23265
+ #endif
23266
+
23282
23267
  /**
23283
23268
  * Initialize a parser with the given start and end pointers.
23284
23269
  */
@@ -23298,9 +23283,8 @@ pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, si
23298
23283
  .do_loop_stack = 0,
23299
23284
  .accepts_block_stack = 0,
23300
23285
  .lex_modes = {
23301
- .index = 0,
23302
- .stack = {{ .mode = PM_LEX_DEFAULT }},
23303
- .current = &parser->lex_modes.stack[0],
23286
+ .current = &parser->lex_modes.base,
23287
+ .base = { .mode = PM_LEX_DEFAULT },
23304
23288
  },
23305
23289
  .start = source,
23306
23290
  .end = source + size,
@@ -23357,7 +23341,8 @@ pm_parser_init(pm_arena_t *arena, pm_parser_t *parser, const uint8_t *source, si
23357
23341
  * frequency. Resizes are cheap with arena allocation, so we lean toward
23358
23342
  * under-estimating. */
23359
23343
  uint32_t constant_size = ((uint32_t) size) / 120;
23360
- pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size < 4 ? 4 : constant_size);
23344
+ if (constant_size < PM_CONSTANT_POOL_MIN_CAPACITY) constant_size = PM_CONSTANT_POOL_MIN_CAPACITY;
23345
+ pm_constant_pool_init(&parser->metadata_arena, &parser->constant_pool, constant_size);
23361
23346
 
23362
23347
  /* Initialize the line offset list. Similar to the constant pool, we are
23363
23348
  * going to estimate the number of newlines that we will need based on the
@@ -23566,12 +23551,8 @@ pm_parser_new(pm_arena_t *arena, const uint8_t *source, size_t size, const pm_op
23566
23551
  */
23567
23552
  void
23568
23553
  pm_parser_cleanup(pm_parser_t *parser) {
23569
- pm_string_cleanup(&parser->filepath);
23570
23554
  pm_arena_cleanup(&parser->metadata_arena);
23571
-
23572
- while (parser->lex_modes.index >= PM_LEX_STACK_SIZE) {
23573
- lex_mode_pop(parser);
23574
- }
23555
+ pm_arena_cleanup(&parser->scratch_arena);
23575
23556
  }
23576
23557
 
23577
23558
  /**
@@ -23753,6 +23734,7 @@ pm_parse(pm_parser_t *parser) {
23753
23734
  pm_node_t *node = parse_program(parser);
23754
23735
  pm_parse_continuable(parser);
23755
23736
  if (parser->attach_comments) pm_comments_attach(parser, node);
23737
+ pm_arena_cleanup(&parser->scratch_arena);
23756
23738
  return node;
23757
23739
  }
23758
23740