prism 1.3.0 → 1.5.0

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -1
  3. data/Makefile +2 -1
  4. data/README.md +1 -0
  5. data/config.yml +273 -37
  6. data/docs/parser_translation.md +8 -23
  7. data/docs/releasing.md +1 -1
  8. data/docs/ripper_translation.md +1 -1
  9. data/docs/ruby_api.md +1 -1
  10. data/ext/prism/api_node.c +1816 -1303
  11. data/ext/prism/extension.c +244 -110
  12. data/ext/prism/extension.h +4 -4
  13. data/include/prism/ast.h +291 -49
  14. data/include/prism/defines.h +4 -1
  15. data/include/prism/diagnostic.h +4 -0
  16. data/include/prism/options.h +89 -3
  17. data/include/prism/regexp.h +2 -2
  18. data/include/prism/util/pm_buffer.h +18 -0
  19. data/include/prism/util/pm_integer.h +4 -0
  20. data/include/prism/util/pm_list.h +6 -0
  21. data/include/prism/util/pm_string.h +12 -2
  22. data/include/prism/version.h +2 -2
  23. data/include/prism.h +41 -16
  24. data/lib/prism/compiler.rb +456 -151
  25. data/lib/prism/desugar_compiler.rb +1 -0
  26. data/lib/prism/dispatcher.rb +16 -0
  27. data/lib/prism/dot_visitor.rb +21 -1
  28. data/lib/prism/dsl.rb +13 -2
  29. data/lib/prism/ffi.rb +62 -34
  30. data/lib/prism/inspect_visitor.rb +5 -1
  31. data/lib/prism/lex_compat.rb +1 -0
  32. data/lib/prism/mutation_compiler.rb +3 -0
  33. data/lib/prism/node.rb +554 -345
  34. data/lib/prism/node_ext.rb +4 -1
  35. data/lib/prism/pack.rb +2 -0
  36. data/lib/prism/parse_result/comments.rb +1 -0
  37. data/lib/prism/parse_result/errors.rb +1 -0
  38. data/lib/prism/parse_result/newlines.rb +2 -1
  39. data/lib/prism/parse_result.rb +53 -0
  40. data/lib/prism/pattern.rb +1 -0
  41. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  42. data/lib/prism/polyfill/scan_byte.rb +14 -0
  43. data/lib/prism/polyfill/warn.rb +42 -0
  44. data/lib/prism/reflection.rb +5 -2
  45. data/lib/prism/relocation.rb +1 -0
  46. data/lib/prism/serialize.rb +1275 -783
  47. data/lib/prism/string_query.rb +1 -0
  48. data/lib/prism/translation/parser/builder.rb +62 -0
  49. data/lib/prism/translation/parser/compiler.rb +230 -152
  50. data/lib/prism/translation/parser/lexer.rb +446 -64
  51. data/lib/prism/translation/parser.rb +64 -4
  52. data/lib/prism/translation/parser33.rb +1 -0
  53. data/lib/prism/translation/parser34.rb +1 -0
  54. data/lib/prism/translation/parser35.rb +13 -0
  55. data/lib/prism/translation/parser_current.rb +24 -0
  56. data/lib/prism/translation/ripper/sexp.rb +1 -0
  57. data/lib/prism/translation/ripper.rb +30 -4
  58. data/lib/prism/translation/ruby_parser.rb +291 -7
  59. data/lib/prism/translation.rb +3 -0
  60. data/lib/prism/visitor.rb +457 -152
  61. data/lib/prism.rb +5 -3
  62. data/prism.gemspec +9 -1
  63. data/rbi/prism/dsl.rbi +9 -6
  64. data/rbi/prism/node.rbi +43 -16
  65. data/rbi/prism/parse_result.rbi +17 -0
  66. data/rbi/prism/translation/parser35.rbi +6 -0
  67. data/rbi/prism.rbi +39 -36
  68. data/sig/prism/dispatcher.rbs +3 -0
  69. data/sig/prism/dsl.rbs +7 -5
  70. data/sig/prism/node.rbs +461 -37
  71. data/sig/prism/node_ext.rbs +84 -17
  72. data/sig/prism/parse_result/comments.rbs +38 -0
  73. data/sig/prism/parse_result.rbs +14 -0
  74. data/sig/prism/reflection.rbs +1 -1
  75. data/sig/prism/serialize.rbs +4 -2
  76. data/sig/prism.rbs +22 -1
  77. data/src/diagnostic.c +9 -3
  78. data/src/node.c +23 -0
  79. data/src/options.c +33 -2
  80. data/src/prettyprint.c +32 -0
  81. data/src/prism.c +620 -242
  82. data/src/serialize.c +8 -0
  83. data/src/token_type.c +36 -34
  84. data/src/util/pm_buffer.c +40 -0
  85. data/src/util/pm_constant_pool.c +6 -2
  86. data/src/util/pm_strncasecmp.c +13 -1
  87. metadata +11 -7
@@ -24,12 +24,14 @@ VALUE rb_cPrismParseResult;
24
24
  VALUE rb_cPrismLexResult;
25
25
  VALUE rb_cPrismParseLexResult;
26
26
  VALUE rb_cPrismStringQuery;
27
+ VALUE rb_cPrismScope;
27
28
 
28
29
  VALUE rb_cPrismDebugEncoding;
29
30
 
30
31
  ID rb_id_option_command_line;
31
32
  ID rb_id_option_encoding;
32
33
  ID rb_id_option_filepath;
34
+ ID rb_id_option_freeze;
33
35
  ID rb_id_option_frozen_string_literal;
34
36
  ID rb_id_option_line;
35
37
  ID rb_id_option_main_script;
@@ -37,6 +39,10 @@ ID rb_id_option_partial_script;
37
39
  ID rb_id_option_scopes;
38
40
  ID rb_id_option_version;
39
41
  ID rb_id_source_for;
42
+ ID rb_id_forwarding_positionals;
43
+ ID rb_id_forwarding_keywords;
44
+ ID rb_id_forwarding_block;
45
+ ID rb_id_forwarding_all;
40
46
 
41
47
  /******************************************************************************/
42
48
  /* IO of Ruby code */
@@ -94,14 +100,53 @@ build_options_scopes(pm_options_t *options, VALUE scopes) {
94
100
  for (size_t scope_index = 0; scope_index < scopes_count; scope_index++) {
95
101
  VALUE scope = rb_ary_entry(scopes, scope_index);
96
102
 
97
- // Check that the scope is an array. If it's not, then raise a type
98
- // error.
99
- if (!RB_TYPE_P(scope, T_ARRAY)) {
100
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(scope));
103
+ // The scope can be either an array or it can be a Prism::Scope object.
104
+ // Parse out the correct values here from either.
105
+ VALUE locals;
106
+ uint8_t forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE;
107
+
108
+ if (RB_TYPE_P(scope, T_ARRAY)) {
109
+ locals = scope;
110
+ } else if (rb_obj_is_kind_of(scope, rb_cPrismScope)) {
111
+ locals = rb_ivar_get(scope, rb_intern("@locals"));
112
+ if (!RB_TYPE_P(locals, T_ARRAY)) {
113
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(locals));
114
+ }
115
+
116
+ VALUE names = rb_ivar_get(scope, rb_intern("@forwarding"));
117
+ if (!RB_TYPE_P(names, T_ARRAY)) {
118
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array)", rb_obj_class(names));
119
+ }
120
+
121
+ size_t names_count = RARRAY_LEN(names);
122
+ for (size_t name_index = 0; name_index < names_count; name_index++) {
123
+ VALUE name = rb_ary_entry(names, name_index);
124
+
125
+ // Check that the name is a symbol. If it's not, then raise
126
+ // a type error.
127
+ if (!RB_TYPE_P(name, T_SYMBOL)) {
128
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Symbol)", rb_obj_class(name));
129
+ }
130
+
131
+ ID id = SYM2ID(name);
132
+ if (id == rb_id_forwarding_positionals) {
133
+ forwarding |= PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS;
134
+ } else if (id == rb_id_forwarding_keywords) {
135
+ forwarding |= PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS;
136
+ } else if (id == rb_id_forwarding_block) {
137
+ forwarding |= PM_OPTIONS_SCOPE_FORWARDING_BLOCK;
138
+ } else if (id == rb_id_forwarding_all) {
139
+ forwarding |= PM_OPTIONS_SCOPE_FORWARDING_ALL;
140
+ } else {
141
+ rb_raise(rb_eArgError, "invalid forwarding value: %" PRIsVALUE, name);
142
+ }
143
+ }
144
+ } else {
145
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected Array or Prism::Scope)", rb_obj_class(scope));
101
146
  }
102
147
 
103
148
  // Initialize the scope array.
104
- size_t locals_count = RARRAY_LEN(scope);
149
+ size_t locals_count = RARRAY_LEN(locals);
105
150
  pm_options_scope_t *options_scope = &options->scopes[scope_index];
106
151
  if (!pm_options_scope_init(options_scope, locals_count)) {
107
152
  rb_raise(rb_eNoMemError, "failed to allocate memory");
@@ -109,7 +154,7 @@ build_options_scopes(pm_options_t *options, VALUE scopes) {
109
154
 
110
155
  // Iterate over the locals and add them to the scope.
111
156
  for (size_t local_index = 0; local_index < locals_count; local_index++) {
112
- VALUE local = rb_ary_entry(scope, local_index);
157
+ VALUE local = rb_ary_entry(locals, local_index);
113
158
 
114
159
  // Check that the local is a symbol. If it's not, then raise a
115
160
  // type error.
@@ -122,6 +167,9 @@ build_options_scopes(pm_options_t *options, VALUE scopes) {
122
167
  const char *name = rb_id2name(SYM2ID(local));
123
168
  pm_string_constant_init(scope_local, name, strlen(name));
124
169
  }
170
+
171
+ // Now set the forwarding options.
172
+ pm_options_scope_forwarding_set(options_scope, forwarding);
125
173
  }
126
174
  }
127
175
 
@@ -180,6 +228,8 @@ build_options_i(VALUE key, VALUE value, VALUE argument) {
180
228
  if (!NIL_P(value)) pm_options_main_script_set(options, RTEST(value));
181
229
  } else if (key_id == rb_id_option_partial_script) {
182
230
  if (!NIL_P(value)) pm_options_partial_script_set(options, RTEST(value));
231
+ } else if (key_id == rb_id_option_freeze) {
232
+ if (!NIL_P(value)) pm_options_freeze_set(options, RTEST(value));
183
233
  } else {
184
234
  rb_raise(rb_eArgError, "unknown keyword: %" PRIsVALUE, key);
185
235
  }
@@ -344,6 +394,7 @@ dump(int argc, VALUE *argv, VALUE self) {
344
394
  #endif
345
395
 
346
396
  VALUE value = dump_input(&input, &options);
397
+ if (options.freeze) rb_obj_freeze(value);
347
398
 
348
399
  #ifdef PRISM_BUILD_DEBUG
349
400
  xfree(dup);
@@ -383,56 +434,90 @@ dump_file(int argc, VALUE *argv, VALUE self) {
383
434
  /* Extracting values for the parse result */
384
435
  /******************************************************************************/
385
436
 
437
+ /**
438
+ * The same as rb_class_new_instance, but accepts an additional boolean to
439
+ * indicate whether or not the resulting class instance should be frozen.
440
+ */
441
+ static inline VALUE
442
+ rb_class_new_instance_freeze(int argc, const VALUE *argv, VALUE klass, bool freeze) {
443
+ VALUE value = rb_class_new_instance(argc, argv, klass);
444
+ if (freeze) rb_obj_freeze(value);
445
+ return value;
446
+ }
447
+
448
+ /**
449
+ * Create a new Location instance from the given parser and bounds.
450
+ */
451
+ static inline VALUE
452
+ parser_location(const pm_parser_t *parser, VALUE source, bool freeze, const uint8_t *start, size_t length) {
453
+ VALUE argv[] = { source, LONG2FIX(start - parser->start), LONG2FIX(length) };
454
+ return rb_class_new_instance_freeze(3, argv, rb_cPrismLocation, freeze);
455
+ }
456
+
457
+ /**
458
+ * Create a new Location instance from the given parser and location.
459
+ */
460
+ #define PARSER_LOCATION_LOC(parser, source, freeze, loc) \
461
+ parser_location(parser, source, freeze, loc.start, (size_t) (loc.end - loc.start))
462
+
463
+ /**
464
+ * Build a new Comment instance from the given parser and comment.
465
+ */
466
+ static inline VALUE
467
+ parser_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_comment_t *comment) {
468
+ VALUE argv[] = { PARSER_LOCATION_LOC(parser, source, freeze, comment->location) };
469
+ VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
470
+ return rb_class_new_instance_freeze(1, argv, type, freeze);
471
+ }
472
+
386
473
  /**
387
474
  * Extract the comments out of the parser into an array.
388
475
  */
389
476
  static VALUE
390
- parser_comments(pm_parser_t *parser, VALUE source) {
477
+ parser_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
391
478
  VALUE comments = rb_ary_new_capa(parser->comment_list.size);
392
479
 
393
- for (pm_comment_t *comment = (pm_comment_t *) parser->comment_list.head; comment != NULL; comment = (pm_comment_t *) comment->node.next) {
394
- VALUE location_argv[] = {
395
- source,
396
- LONG2FIX(comment->location.start - parser->start),
397
- LONG2FIX(comment->location.end - comment->location.start)
398
- };
399
-
400
- VALUE type = (comment->type == PM_COMMENT_EMBDOC) ? rb_cPrismEmbDocComment : rb_cPrismInlineComment;
401
- VALUE comment_argv[] = { rb_class_new_instance(3, location_argv, rb_cPrismLocation) };
402
- rb_ary_push(comments, rb_class_new_instance(1, comment_argv, type));
480
+ for (
481
+ const pm_comment_t *comment = (const pm_comment_t *) parser->comment_list.head;
482
+ comment != NULL;
483
+ comment = (const pm_comment_t *) comment->node.next
484
+ ) {
485
+ VALUE value = parser_comment(parser, source, freeze, comment);
486
+ rb_ary_push(comments, value);
403
487
  }
404
488
 
489
+ if (freeze) rb_obj_freeze(comments);
405
490
  return comments;
406
491
  }
407
492
 
493
+ /**
494
+ * Build a new MagicComment instance from the given parser and magic comment.
495
+ */
496
+ static inline VALUE
497
+ parser_magic_comment(const pm_parser_t *parser, VALUE source, bool freeze, const pm_magic_comment_t *magic_comment) {
498
+ VALUE key_loc = parser_location(parser, source, freeze, magic_comment->key_start, magic_comment->key_length);
499
+ VALUE value_loc = parser_location(parser, source, freeze, magic_comment->value_start, magic_comment->value_length);
500
+ VALUE argv[] = { key_loc, value_loc };
501
+ return rb_class_new_instance_freeze(2, argv, rb_cPrismMagicComment, freeze);
502
+ }
503
+
408
504
  /**
409
505
  * Extract the magic comments out of the parser into an array.
410
506
  */
411
507
  static VALUE
412
- parser_magic_comments(pm_parser_t *parser, VALUE source) {
508
+ parser_magic_comments(const pm_parser_t *parser, VALUE source, bool freeze) {
413
509
  VALUE magic_comments = rb_ary_new_capa(parser->magic_comment_list.size);
414
510
 
415
- for (pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) parser->magic_comment_list.head; magic_comment != NULL; magic_comment = (pm_magic_comment_t *) magic_comment->node.next) {
416
- VALUE key_loc_argv[] = {
417
- source,
418
- LONG2FIX(magic_comment->key_start - parser->start),
419
- LONG2FIX(magic_comment->key_length)
420
- };
421
-
422
- VALUE value_loc_argv[] = {
423
- source,
424
- LONG2FIX(magic_comment->value_start - parser->start),
425
- LONG2FIX(magic_comment->value_length)
426
- };
427
-
428
- VALUE magic_comment_argv[] = {
429
- rb_class_new_instance(3, key_loc_argv, rb_cPrismLocation),
430
- rb_class_new_instance(3, value_loc_argv, rb_cPrismLocation)
431
- };
432
-
433
- rb_ary_push(magic_comments, rb_class_new_instance(2, magic_comment_argv, rb_cPrismMagicComment));
511
+ for (
512
+ const pm_magic_comment_t *magic_comment = (const pm_magic_comment_t *) parser->magic_comment_list.head;
513
+ magic_comment != NULL;
514
+ magic_comment = (const pm_magic_comment_t *) magic_comment->node.next
515
+ ) {
516
+ VALUE value = parser_magic_comment(parser, source, freeze, magic_comment);
517
+ rb_ary_push(magic_comments, value);
434
518
  }
435
519
 
520
+ if (freeze) rb_obj_freeze(magic_comments);
436
521
  return magic_comments;
437
522
  }
438
523
 
@@ -441,17 +526,11 @@ parser_magic_comments(pm_parser_t *parser, VALUE source) {
441
526
  * exists.
442
527
  */
443
528
  static VALUE
444
- parser_data_loc(const pm_parser_t *parser, VALUE source) {
529
+ parser_data_loc(const pm_parser_t *parser, VALUE source, bool freeze) {
445
530
  if (parser->data_loc.end == NULL) {
446
531
  return Qnil;
447
532
  } else {
448
- VALUE argv[] = {
449
- source,
450
- LONG2FIX(parser->data_loc.start - parser->start),
451
- LONG2FIX(parser->data_loc.end - parser->data_loc.start)
452
- };
453
-
454
- return rb_class_new_instance(3, argv, rb_cPrismLocation);
533
+ return PARSER_LOCATION_LOC(parser, source, freeze, parser->data_loc);
455
534
  }
456
535
  }
457
536
 
@@ -459,16 +538,17 @@ parser_data_loc(const pm_parser_t *parser, VALUE source) {
459
538
  * Extract the errors out of the parser into an array.
460
539
  */
461
540
  static VALUE
462
- parser_errors(pm_parser_t *parser, rb_encoding *encoding, VALUE source) {
541
+ parser_errors(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
463
542
  VALUE errors = rb_ary_new_capa(parser->error_list.size);
464
- pm_diagnostic_t *error;
465
543
 
466
- for (error = (pm_diagnostic_t *) parser->error_list.head; error != NULL; error = (pm_diagnostic_t *) error->node.next) {
467
- VALUE location_argv[] = {
468
- source,
469
- LONG2FIX(error->location.start - parser->start),
470
- LONG2FIX(error->location.end - error->location.start)
471
- };
544
+ for (
545
+ const pm_diagnostic_t *error = (const pm_diagnostic_t *) parser->error_list.head;
546
+ error != NULL;
547
+ error = (const pm_diagnostic_t *) error->node.next
548
+ ) {
549
+ VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(error->diag_id)));
550
+ VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(error->message, encoding));
551
+ VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, error->location);
472
552
 
473
553
  VALUE level = Qnil;
474
554
  switch (error->level) {
@@ -485,16 +565,12 @@ parser_errors(pm_parser_t *parser, rb_encoding *encoding, VALUE source) {
485
565
  rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, error->level);
486
566
  }
487
567
 
488
- VALUE error_argv[] = {
489
- ID2SYM(rb_intern(pm_diagnostic_id_human(error->diag_id))),
490
- rb_enc_str_new_cstr(error->message, encoding),
491
- rb_class_new_instance(3, location_argv, rb_cPrismLocation),
492
- level
493
- };
494
-
495
- rb_ary_push(errors, rb_class_new_instance(4, error_argv, rb_cPrismParseError));
568
+ VALUE argv[] = { type, message, location, level };
569
+ VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseError, freeze);
570
+ rb_ary_push(errors, value);
496
571
  }
497
572
 
573
+ if (freeze) rb_obj_freeze(errors);
498
574
  return errors;
499
575
  }
500
576
 
@@ -502,16 +578,17 @@ parser_errors(pm_parser_t *parser, rb_encoding *encoding, VALUE source) {
502
578
  * Extract the warnings out of the parser into an array.
503
579
  */
504
580
  static VALUE
505
- parser_warnings(pm_parser_t *parser, rb_encoding *encoding, VALUE source) {
581
+ parser_warnings(const pm_parser_t *parser, rb_encoding *encoding, VALUE source, bool freeze) {
506
582
  VALUE warnings = rb_ary_new_capa(parser->warning_list.size);
507
- pm_diagnostic_t *warning;
508
583
 
509
- for (warning = (pm_diagnostic_t *) parser->warning_list.head; warning != NULL; warning = (pm_diagnostic_t *) warning->node.next) {
510
- VALUE location_argv[] = {
511
- source,
512
- LONG2FIX(warning->location.start - parser->start),
513
- LONG2FIX(warning->location.end - warning->location.start)
514
- };
584
+ for (
585
+ const pm_diagnostic_t *warning = (const pm_diagnostic_t *) parser->warning_list.head;
586
+ warning != NULL;
587
+ warning = (const pm_diagnostic_t *) warning->node.next
588
+ ) {
589
+ VALUE type = ID2SYM(rb_intern(pm_diagnostic_id_human(warning->diag_id)));
590
+ VALUE message = rb_obj_freeze(rb_enc_str_new_cstr(warning->message, encoding));
591
+ VALUE location = PARSER_LOCATION_LOC(parser, source, freeze, warning->location);
515
592
 
516
593
  VALUE level = Qnil;
517
594
  switch (warning->level) {
@@ -525,16 +602,12 @@ parser_warnings(pm_parser_t *parser, rb_encoding *encoding, VALUE source) {
525
602
  rb_raise(rb_eRuntimeError, "Unknown level: %" PRIu8, warning->level);
526
603
  }
527
604
 
528
- VALUE warning_argv[] = {
529
- ID2SYM(rb_intern(pm_diagnostic_id_human(warning->diag_id))),
530
- rb_enc_str_new_cstr(warning->message, encoding),
531
- rb_class_new_instance(3, location_argv, rb_cPrismLocation),
532
- level
533
- };
534
-
535
- rb_ary_push(warnings, rb_class_new_instance(4, warning_argv, rb_cPrismParseWarning));
605
+ VALUE argv[] = { type, message, location, level };
606
+ VALUE value = rb_class_new_instance_freeze(4, argv, rb_cPrismParseWarning, freeze);
607
+ rb_ary_push(warnings, value);
536
608
  }
537
609
 
610
+ if (freeze) rb_obj_freeze(warnings);
538
611
  return warnings;
539
612
  }
540
613
 
@@ -542,18 +615,18 @@ parser_warnings(pm_parser_t *parser, rb_encoding *encoding, VALUE source) {
542
615
  * Create a new parse result from the given parser, value, encoding, and source.
543
616
  */
544
617
  static VALUE
545
- parse_result_create(VALUE class, pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source) {
618
+ parse_result_create(VALUE class, const pm_parser_t *parser, VALUE value, rb_encoding *encoding, VALUE source, bool freeze) {
546
619
  VALUE result_argv[] = {
547
620
  value,
548
- parser_comments(parser, source),
549
- parser_magic_comments(parser, source),
550
- parser_data_loc(parser, source),
551
- parser_errors(parser, encoding, source),
552
- parser_warnings(parser, encoding, source),
621
+ parser_comments(parser, source, freeze),
622
+ parser_magic_comments(parser, source, freeze),
623
+ parser_data_loc(parser, source, freeze),
624
+ parser_errors(parser, encoding, source, freeze),
625
+ parser_warnings(parser, encoding, source, freeze),
553
626
  source
554
627
  };
555
628
 
556
- return rb_class_new_instance(7, result_argv, class);
629
+ return rb_class_new_instance_freeze(7, result_argv, class, freeze);
557
630
  }
558
631
 
559
632
  /******************************************************************************/
@@ -569,6 +642,7 @@ typedef struct {
569
642
  VALUE source;
570
643
  VALUE tokens;
571
644
  rb_encoding *encoding;
645
+ bool freeze;
572
646
  } parse_lex_data_t;
573
647
 
574
648
  /**
@@ -580,10 +654,13 @@ static void
580
654
  parse_lex_token(void *data, pm_parser_t *parser, pm_token_t *token) {
581
655
  parse_lex_data_t *parse_lex_data = (parse_lex_data_t *) parser->lex_callback->data;
582
656
 
583
- VALUE yields = rb_assoc_new(
584
- pm_token_new(parser, token, parse_lex_data->encoding, parse_lex_data->source),
585
- INT2FIX(parser->lex_state)
586
- );
657
+ VALUE value = pm_token_new(parser, token, parse_lex_data->encoding, parse_lex_data->source, parse_lex_data->freeze);
658
+ VALUE yields = rb_assoc_new(value, INT2FIX(parser->lex_state));
659
+
660
+ if (parse_lex_data->freeze) {
661
+ rb_obj_freeze(value);
662
+ rb_obj_freeze(yields);
663
+ }
587
664
 
588
665
  rb_ary_push(parse_lex_data->tokens, yields);
589
666
  }
@@ -603,14 +680,37 @@ parse_lex_encoding_changed_callback(pm_parser_t *parser) {
603
680
  // one or two tokens, since the encoding can only change at the top of the
604
681
  // file.
605
682
  VALUE tokens = parse_lex_data->tokens;
683
+ VALUE next_tokens = rb_ary_new();
684
+
606
685
  for (long index = 0; index < RARRAY_LEN(tokens); index++) {
607
686
  VALUE yields = rb_ary_entry(tokens, index);
608
687
  VALUE token = rb_ary_entry(yields, 0);
609
688
 
610
689
  VALUE value = rb_ivar_get(token, rb_intern("@value"));
611
- rb_enc_associate(value, parse_lex_data->encoding);
612
- ENC_CODERANGE_CLEAR(value);
690
+ VALUE next_value = rb_str_dup(value);
691
+
692
+ rb_enc_associate(next_value, parse_lex_data->encoding);
693
+ if (parse_lex_data->freeze) rb_obj_freeze(next_value);
694
+
695
+ VALUE next_token_argv[] = {
696
+ parse_lex_data->source,
697
+ rb_ivar_get(token, rb_intern("@type")),
698
+ next_value,
699
+ rb_ivar_get(token, rb_intern("@location"))
700
+ };
701
+
702
+ VALUE next_token = rb_class_new_instance(4, next_token_argv, rb_cPrismToken);
703
+ VALUE next_yields = rb_assoc_new(next_token, rb_ary_entry(yields, 1));
704
+
705
+ if (parse_lex_data->freeze) {
706
+ rb_obj_freeze(next_token);
707
+ rb_obj_freeze(next_yields);
708
+ }
709
+
710
+ rb_ary_push(next_tokens, next_yields);
613
711
  }
712
+
713
+ rb_ary_replace(parse_lex_data->tokens, next_tokens);
614
714
  }
615
715
 
616
716
  /**
@@ -630,7 +730,8 @@ parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nod
630
730
  parse_lex_data_t parse_lex_data = {
631
731
  .source = source,
632
732
  .tokens = rb_ary_new(),
633
- .encoding = rb_utf8_encoding()
733
+ .encoding = rb_utf8_encoding(),
734
+ .freeze = options->freeze,
634
735
  };
635
736
 
636
737
  parse_lex_data_t *data = &parse_lex_data;
@@ -653,14 +754,22 @@ parse_lex_input(pm_string_t *input, const pm_options_t *options, bool return_nod
653
754
  rb_ary_push(offsets, ULONG2NUM(parser.newline_list.offsets[index]));
654
755
  }
655
756
 
757
+ if (options->freeze) {
758
+ rb_obj_freeze(source_string);
759
+ rb_obj_freeze(offsets);
760
+ rb_obj_freeze(source);
761
+ rb_obj_freeze(parse_lex_data.tokens);
762
+ }
763
+
656
764
  VALUE result;
657
765
  if (return_nodes) {
658
766
  VALUE value = rb_ary_new_capa(2);
659
- rb_ary_push(value, pm_ast_new(&parser, node, parse_lex_data.encoding, source));
767
+ rb_ary_push(value, pm_ast_new(&parser, node, parse_lex_data.encoding, source, options->freeze));
660
768
  rb_ary_push(value, parse_lex_data.tokens);
661
- result = parse_result_create(rb_cPrismParseLexResult, &parser, value, parse_lex_data.encoding, source);
769
+ if (options->freeze) rb_obj_freeze(value);
770
+ result = parse_result_create(rb_cPrismParseLexResult, &parser, value, parse_lex_data.encoding, source, options->freeze);
662
771
  } else {
663
- result = parse_result_create(rb_cPrismLexResult, &parser, parse_lex_data.tokens, parse_lex_data.encoding, source);
772
+ result = parse_result_create(rb_cPrismLexResult, &parser, parse_lex_data.tokens, parse_lex_data.encoding, source, options->freeze);
664
773
  }
665
774
 
666
775
  pm_node_destroy(&parser, node);
@@ -726,9 +835,13 @@ parse_input(pm_string_t *input, const pm_options_t *options) {
726
835
  pm_node_t *node = pm_parse(&parser);
727
836
  rb_encoding *encoding = rb_enc_find(parser.encoding->name);
728
837
 
729
- VALUE source = pm_source_new(&parser, encoding);
730
- VALUE value = pm_ast_new(&parser, node, encoding, source);
731
- VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source) ;
838
+ VALUE source = pm_source_new(&parser, encoding, options->freeze);
839
+ VALUE value = pm_ast_new(&parser, node, encoding, source, options->freeze);
840
+ VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options->freeze);
841
+
842
+ if (options->freeze) {
843
+ rb_obj_freeze(source);
844
+ }
732
845
 
733
846
  pm_node_destroy(&parser, node);
734
847
  pm_parser_free(&parser);
@@ -750,6 +863,8 @@ parse_input(pm_string_t *input, const pm_options_t *options) {
750
863
  * encoding or nil.
751
864
  * * `filepath` - the filepath of the source being parsed. This should be a
752
865
  * string or nil.
866
+ * * `freeze` - whether or not to deeply freeze the AST. This should be a
867
+ * boolean or nil.
753
868
  * * `frozen_string_literal` - whether or not the frozen string literal pragma
754
869
  * has been set. This should be a boolean or nil.
755
870
  * * `line` - the line number that the parse starts on. This should be an
@@ -769,12 +884,12 @@ parse_input(pm_string_t *input, const pm_options_t *options) {
769
884
  * parsed. This should be an array of arrays of symbols or nil. Scopes are
770
885
  * ordered from the outermost scope to the innermost one.
771
886
  * * `version` - the version of Ruby syntax that prism should used to parse Ruby
772
- * code. By default prism assumes you want to parse with the latest version
773
- * of Ruby syntax (which you can trigger with `nil` or `"latest"`). You
774
- * may also restrict the syntax to a specific version of Ruby, e.g., with `"3.3.0"`.
775
- * To parse with the same syntax version that the current Ruby is running
776
- * use `version: RUBY_VERSION`. Raises ArgumentError if the version is not
777
- * currently supported by Prism.
887
+ * code. By default prism assumes you want to parse with the latest
888
+ * version of Ruby syntax (which you can trigger with `nil` or
889
+ * `"latest"`). You may also restrict the syntax to a specific version of
890
+ * Ruby, e.g., with `"3.3.0"`. To parse with the same syntax version that
891
+ * the current Ruby is running use `version: RUBY_VERSION`. Raises
892
+ * ArgumentError if the version is not currently supported by Prism.
778
893
  */
779
894
  static VALUE
780
895
  parse(int argc, VALUE *argv, VALUE self) {
@@ -879,6 +994,14 @@ profile_file(int argc, VALUE *argv, VALUE self) {
879
994
  return Qnil;
880
995
  }
881
996
 
997
+ static int
998
+ parse_stream_eof(void *stream) {
999
+ if (rb_funcall((VALUE) stream, rb_intern("eof?"), 0)) {
1000
+ return 1;
1001
+ }
1002
+ return 0;
1003
+ }
1004
+
882
1005
  /**
883
1006
  * An implementation of fgets that is suitable for use with Ruby IO objects.
884
1007
  */
@@ -919,12 +1042,12 @@ parse_stream(int argc, VALUE *argv, VALUE self) {
919
1042
  pm_parser_t parser;
920
1043
  pm_buffer_t buffer;
921
1044
 
922
- pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, &options);
1045
+ pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, parse_stream_eof, &options);
923
1046
  rb_encoding *encoding = rb_enc_find(parser.encoding->name);
924
1047
 
925
- VALUE source = pm_source_new(&parser, encoding);
926
- VALUE value = pm_ast_new(&parser, node, encoding, source);
927
- VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source);
1048
+ VALUE source = pm_source_new(&parser, encoding, options.freeze);
1049
+ VALUE value = pm_ast_new(&parser, node, encoding, source, options.freeze);
1050
+ VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options.freeze);
928
1051
 
929
1052
  pm_node_destroy(&parser, node);
930
1053
  pm_buffer_free(&buffer);
@@ -944,8 +1067,8 @@ parse_input_comments(pm_string_t *input, const pm_options_t *options) {
944
1067
  pm_node_t *node = pm_parse(&parser);
945
1068
  rb_encoding *encoding = rb_enc_find(parser.encoding->name);
946
1069
 
947
- VALUE source = pm_source_new(&parser, encoding);
948
- VALUE comments = parser_comments(&parser, source);
1070
+ VALUE source = pm_source_new(&parser, encoding, options->freeze);
1071
+ VALUE comments = parser_comments(&parser, source, options->freeze);
949
1072
 
950
1073
  pm_node_destroy(&parser, node);
951
1074
  pm_parser_free(&parser);
@@ -1216,6 +1339,11 @@ Init_prism(void) {
1216
1339
  );
1217
1340
  }
1218
1341
 
1342
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
1343
+ // Mark this extension as Ractor-safe.
1344
+ rb_ext_ractor_safe(true);
1345
+ #endif
1346
+
1219
1347
  // Grab up references to all of the constants that we're going to need to
1220
1348
  // reference throughout this extension.
1221
1349
  rb_cPrism = rb_define_module("Prism");
@@ -1234,12 +1362,14 @@ Init_prism(void) {
1234
1362
  rb_cPrismLexResult = rb_define_class_under(rb_cPrism, "LexResult", rb_cPrismResult);
1235
1363
  rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult);
1236
1364
  rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject);
1365
+ rb_cPrismScope = rb_define_class_under(rb_cPrism, "Scope", rb_cObject);
1237
1366
 
1238
1367
  // Intern all of the IDs eagerly that we support so that we don't have to do
1239
1368
  // it every time we parse.
1240
1369
  rb_id_option_command_line = rb_intern_const("command_line");
1241
1370
  rb_id_option_encoding = rb_intern_const("encoding");
1242
1371
  rb_id_option_filepath = rb_intern_const("filepath");
1372
+ rb_id_option_freeze = rb_intern_const("freeze");
1243
1373
  rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal");
1244
1374
  rb_id_option_line = rb_intern_const("line");
1245
1375
  rb_id_option_main_script = rb_intern_const("main_script");
@@ -1247,11 +1377,15 @@ Init_prism(void) {
1247
1377
  rb_id_option_scopes = rb_intern_const("scopes");
1248
1378
  rb_id_option_version = rb_intern_const("version");
1249
1379
  rb_id_source_for = rb_intern("for");
1380
+ rb_id_forwarding_positionals = rb_intern("*");
1381
+ rb_id_forwarding_keywords = rb_intern("**");
1382
+ rb_id_forwarding_block = rb_intern("&");
1383
+ rb_id_forwarding_all = rb_intern("...");
1250
1384
 
1251
1385
  /**
1252
1386
  * The version of the prism library.
1253
1387
  */
1254
- rb_define_const(rb_cPrism, "VERSION", rb_str_new2(EXPECTED_PRISM_VERSION));
1388
+ rb_define_const(rb_cPrism, "VERSION", rb_str_freeze(rb_str_new_cstr(EXPECTED_PRISM_VERSION)));
1255
1389
 
1256
1390
  // First, the functions that have to do with lexing and parsing.
1257
1391
  rb_define_singleton_method(rb_cPrism, "lex", lex, -1);
@@ -1,15 +1,15 @@
1
1
  #ifndef PRISM_EXT_NODE_H
2
2
  #define PRISM_EXT_NODE_H
3
3
 
4
- #define EXPECTED_PRISM_VERSION "1.3.0"
4
+ #define EXPECTED_PRISM_VERSION "1.5.0"
5
5
 
6
6
  #include <ruby.h>
7
7
  #include <ruby/encoding.h>
8
8
  #include "prism.h"
9
9
 
10
- VALUE pm_source_new(const pm_parser_t *parser, rb_encoding *encoding);
11
- VALUE pm_token_new(const pm_parser_t *parser, const pm_token_t *token, rb_encoding *encoding, VALUE source);
12
- VALUE pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encoding, VALUE source);
10
+ VALUE pm_source_new(const pm_parser_t *parser, rb_encoding *encoding, bool freeze);
11
+ VALUE pm_token_new(const pm_parser_t *parser, const pm_token_t *token, rb_encoding *encoding, VALUE source, bool freeze);
12
+ VALUE pm_ast_new(const pm_parser_t *parser, const pm_node_t *node, rb_encoding *encoding, VALUE source, bool freeze);
13
13
  VALUE pm_integer_new(const pm_integer_t *integer);
14
14
 
15
15
  void Init_prism_api_node(void);