prism 1.3.0 → 1.4.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -1
  3. data/config.yml +9 -0
  4. data/docs/releasing.md +1 -1
  5. data/docs/ruby_api.md +1 -1
  6. data/ext/prism/api_node.c +1814 -1303
  7. data/ext/prism/extension.c +230 -109
  8. data/ext/prism/extension.h +4 -4
  9. data/include/prism/ast.h +16 -0
  10. data/include/prism/defines.h +4 -1
  11. data/include/prism/options.h +47 -1
  12. data/include/prism/util/pm_buffer.h +10 -0
  13. data/include/prism/version.h +2 -2
  14. data/include/prism.h +4 -4
  15. data/lib/prism/dot_visitor.rb +16 -0
  16. data/lib/prism/dsl.rb +10 -2
  17. data/lib/prism/ffi.rb +45 -27
  18. data/lib/prism/inspect_visitor.rb +2 -1
  19. data/lib/prism/node.rb +48 -10
  20. data/lib/prism/parse_result/newlines.rb +1 -1
  21. data/lib/prism/parse_result.rb +52 -0
  22. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  23. data/lib/prism/reflection.rb +2 -2
  24. data/lib/prism/serialize.rb +1252 -765
  25. data/lib/prism/translation/parser/builder.rb +61 -0
  26. data/lib/prism/translation/parser/compiler.rb +192 -136
  27. data/lib/prism/translation/parser/lexer.rb +435 -61
  28. data/lib/prism/translation/parser.rb +51 -3
  29. data/lib/prism/translation/parser35.rb +12 -0
  30. data/lib/prism/translation/ripper.rb +13 -3
  31. data/lib/prism/translation/ruby_parser.rb +5 -4
  32. data/lib/prism/translation.rb +1 -0
  33. data/lib/prism.rb +3 -3
  34. data/prism.gemspec +5 -1
  35. data/rbi/prism/dsl.rbi +6 -3
  36. data/rbi/prism/node.rbi +22 -7
  37. data/rbi/prism/parse_result.rbi +17 -0
  38. data/rbi/prism/translation/parser35.rbi +6 -0
  39. data/rbi/prism.rbi +39 -36
  40. data/sig/prism/dsl.rbs +4 -2
  41. data/sig/prism/node.rbs +17 -7
  42. data/sig/prism/parse_result.rbs +10 -0
  43. data/sig/prism/serialize.rbs +4 -2
  44. data/sig/prism.rbs +22 -1
  45. data/src/diagnostic.c +2 -2
  46. data/src/node.c +21 -0
  47. data/src/options.c +31 -0
  48. data/src/prettyprint.c +30 -0
  49. data/src/prism.c +374 -118
  50. data/src/serialize.c +6 -0
  51. data/src/util/pm_buffer.c +40 -0
  52. data/src/util/pm_constant_pool.c +6 -2
  53. data/src/util/pm_strncasecmp.c +13 -1
  54. metadata +7 -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) {
@@ -922,9 +1037,9 @@ parse_stream(int argc, VALUE *argv, VALUE self) {
922
1037
  pm_node_t *node = pm_parse_stream(&parser, &buffer, (void *) stream, parse_stream_fgets, &options);
923
1038
  rb_encoding *encoding = rb_enc_find(parser.encoding->name);
924
1039
 
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);
1040
+ VALUE source = pm_source_new(&parser, encoding, options.freeze);
1041
+ VALUE value = pm_ast_new(&parser, node, encoding, source, options.freeze);
1042
+ VALUE result = parse_result_create(rb_cPrismParseResult, &parser, value, encoding, source, options.freeze);
928
1043
 
929
1044
  pm_node_destroy(&parser, node);
930
1045
  pm_buffer_free(&buffer);
@@ -944,8 +1059,8 @@ parse_input_comments(pm_string_t *input, const pm_options_t *options) {
944
1059
  pm_node_t *node = pm_parse(&parser);
945
1060
  rb_encoding *encoding = rb_enc_find(parser.encoding->name);
946
1061
 
947
- VALUE source = pm_source_new(&parser, encoding);
948
- VALUE comments = parser_comments(&parser, source);
1062
+ VALUE source = pm_source_new(&parser, encoding, options->freeze);
1063
+ VALUE comments = parser_comments(&parser, source, options->freeze);
949
1064
 
950
1065
  pm_node_destroy(&parser, node);
951
1066
  pm_parser_free(&parser);
@@ -1234,12 +1349,14 @@ Init_prism(void) {
1234
1349
  rb_cPrismLexResult = rb_define_class_under(rb_cPrism, "LexResult", rb_cPrismResult);
1235
1350
  rb_cPrismParseLexResult = rb_define_class_under(rb_cPrism, "ParseLexResult", rb_cPrismResult);
1236
1351
  rb_cPrismStringQuery = rb_define_class_under(rb_cPrism, "StringQuery", rb_cObject);
1352
+ rb_cPrismScope = rb_define_class_under(rb_cPrism, "Scope", rb_cObject);
1237
1353
 
1238
1354
  // Intern all of the IDs eagerly that we support so that we don't have to do
1239
1355
  // it every time we parse.
1240
1356
  rb_id_option_command_line = rb_intern_const("command_line");
1241
1357
  rb_id_option_encoding = rb_intern_const("encoding");
1242
1358
  rb_id_option_filepath = rb_intern_const("filepath");
1359
+ rb_id_option_freeze = rb_intern_const("freeze");
1243
1360
  rb_id_option_frozen_string_literal = rb_intern_const("frozen_string_literal");
1244
1361
  rb_id_option_line = rb_intern_const("line");
1245
1362
  rb_id_option_main_script = rb_intern_const("main_script");
@@ -1247,11 +1364,15 @@ Init_prism(void) {
1247
1364
  rb_id_option_scopes = rb_intern_const("scopes");
1248
1365
  rb_id_option_version = rb_intern_const("version");
1249
1366
  rb_id_source_for = rb_intern("for");
1367
+ rb_id_forwarding_positionals = rb_intern("*");
1368
+ rb_id_forwarding_keywords = rb_intern("**");
1369
+ rb_id_forwarding_block = rb_intern("&");
1370
+ rb_id_forwarding_all = rb_intern("...");
1250
1371
 
1251
1372
  /**
1252
1373
  * The version of the prism library.
1253
1374
  */
1254
- rb_define_const(rb_cPrism, "VERSION", rb_str_new2(EXPECTED_PRISM_VERSION));
1375
+ rb_define_const(rb_cPrism, "VERSION", rb_str_freeze(rb_str_new_cstr(EXPECTED_PRISM_VERSION)));
1255
1376
 
1256
1377
  // First, the functions that have to do with lexing and parsing.
1257
1378
  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.4.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);
data/include/prism/ast.h CHANGED
@@ -6441,6 +6441,9 @@ typedef struct pm_parameters_node {
6441
6441
  * ^^^^^^^^^
6442
6442
  *
6443
6443
  * Type: ::PM_PARENTHESES_NODE
6444
+
6445
+ * Flags (#pm_parentheses_node_flags):
6446
+ * * ::PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS
6444
6447
  *
6445
6448
  * @extends pm_node_t
6446
6449
  */
@@ -6935,6 +6938,11 @@ typedef struct pm_rescue_node {
6935
6938
  */
6936
6939
  struct pm_node *reference;
6937
6940
 
6941
+ /**
6942
+ * RescueNode#then_keyword_loc
6943
+ */
6944
+ pm_location_t then_keyword_loc;
6945
+
6938
6946
  /**
6939
6947
  * RescueNode#statements
6940
6948
  */
@@ -7846,6 +7854,14 @@ typedef enum pm_parameter_flags {
7846
7854
  PM_PARAMETER_FLAGS_REPEATED_PARAMETER = 4,
7847
7855
  } pm_parameter_flags_t;
7848
7856
 
7857
+ /**
7858
+ * Flags for parentheses nodes.
7859
+ */
7860
+ typedef enum pm_parentheses_node_flags {
7861
+ /** parentheses that contain multiple potentially void statements */
7862
+ PM_PARENTHESES_NODE_FLAGS_MULTIPLE_STATEMENTS = 4,
7863
+ } pm_parentheses_node_flags_t;
7864
+
7849
7865
  /**
7850
7866
  * Flags for range and flip-flop nodes.
7851
7867
  */
@@ -23,6 +23,9 @@
23
23
  * some platforms they aren't included unless this is already defined.
24
24
  */
25
25
  #define __STDC_FORMAT_MACROS
26
+ // Include sys/types.h before inttypes.h to work around issue with
27
+ // certain versions of GCC and newlib which causes omission of PRIx64
28
+ #include <sys/types.h>
26
29
  #include <inttypes.h>
27
30
 
28
31
  /**
@@ -31,7 +34,7 @@
31
34
  * specifying a maximum depth to which we are allowed to recurse.
32
35
  */
33
36
  #ifndef PRISM_DEPTH_MAXIMUM
34
- #define PRISM_DEPTH_MAXIMUM 1000
37
+ #define PRISM_DEPTH_MAXIMUM 10000
35
38
  #endif
36
39
 
37
40
  /**