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
@@ -39,8 +39,26 @@ typedef struct pm_options_scope {
39
39
 
40
40
  /** The names of the locals in the scope. */
41
41
  pm_string_t *locals;
42
+
43
+ /** Flags for the set of forwarding parameters in this scope. */
44
+ uint8_t forwarding;
42
45
  } pm_options_scope_t;
43
46
 
47
+ /** The default value for parameters. */
48
+ static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_NONE = 0x0;
49
+
50
+ /** When the scope is fowarding with the * parameter. */
51
+ static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS = 0x1;
52
+
53
+ /** When the scope is fowarding with the ** parameter. */
54
+ static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS = 0x2;
55
+
56
+ /** When the scope is fowarding with the & parameter. */
57
+ static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_BLOCK = 0x4;
58
+
59
+ /** When the scope is fowarding with the ... parameter. */
60
+ static const uint8_t PM_OPTIONS_SCOPE_FORWARDING_ALL = 0x8;
61
+
44
62
  // Forward declaration needed by the callback typedef.
45
63
  struct pm_options;
46
64
 
@@ -68,7 +86,10 @@ typedef enum {
68
86
  PM_OPTIONS_VERSION_LATEST = 0,
69
87
 
70
88
  /** The vendored version of prism in CRuby 3.3.x. */
71
- PM_OPTIONS_VERSION_CRUBY_3_3 = 1
89
+ PM_OPTIONS_VERSION_CRUBY_3_3 = 1,
90
+
91
+ /** The vendored version of prism in CRuby 3.4.x. */
92
+ PM_OPTIONS_VERSION_CRUBY_3_4 = 2
72
93
  } pm_options_version_t;
73
94
 
74
95
  /**
@@ -157,6 +178,13 @@ typedef struct pm_options {
157
178
  * inside another script.
158
179
  */
159
180
  bool partial_script;
181
+
182
+ /**
183
+ * Whether or not the parser should freeze the nodes that it creates. This
184
+ * makes it possible to have a deeply frozen AST that is safe to share
185
+ * between concurrency primitives.
186
+ */
187
+ bool freeze;
160
188
  } pm_options_t;
161
189
 
162
190
  /**
@@ -282,6 +310,14 @@ PRISM_EXPORTED_FUNCTION void pm_options_main_script_set(pm_options_t *options, b
282
310
  */
283
311
  PRISM_EXPORTED_FUNCTION void pm_options_partial_script_set(pm_options_t *options, bool partial_script);
284
312
 
313
+ /**
314
+ * Set the freeze option on the given options struct.
315
+ *
316
+ * @param options The options struct to set the freeze value on.
317
+ * @param freeze The freeze value to set.
318
+ */
319
+ PRISM_EXPORTED_FUNCTION void pm_options_freeze_set(pm_options_t *options, bool freeze);
320
+
285
321
  /**
286
322
  * Allocate and zero out the scopes array on the given options struct.
287
323
  *
@@ -319,6 +355,14 @@ PRISM_EXPORTED_FUNCTION bool pm_options_scope_init(pm_options_scope_t *scope, si
319
355
  */
320
356
  PRISM_EXPORTED_FUNCTION const pm_string_t * pm_options_scope_local_get(const pm_options_scope_t *scope, size_t index);
321
357
 
358
+ /**
359
+ * Set the forwarding option on the given scope struct.
360
+ *
361
+ * @param scope The scope struct to set the forwarding on.
362
+ * @param forwarding The forwarding value to set.
363
+ */
364
+ PRISM_EXPORTED_FUNCTION void pm_options_scope_forwarding_set(pm_options_scope_t *scope, uint8_t forwarding);
365
+
322
366
  /**
323
367
  * Free the internal memory associated with the options.
324
368
  *
@@ -352,6 +396,7 @@ PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options);
352
396
  * | `1` | encoding locked |
353
397
  * | `1` | main script |
354
398
  * | `1` | partial script |
399
+ * | `1` | freeze |
355
400
  * | `4` | the number of scopes |
356
401
  * | ... | the scopes |
357
402
  *
@@ -367,6 +412,7 @@ PRISM_EXPORTED_FUNCTION void pm_options_free(pm_options_t *options);
367
412
  * | # bytes | field |
368
413
  * | ------- | -------------------------- |
369
414
  * | `4` | the number of locals |
415
+ * | `1` | the forwarding flags |
370
416
  * | ... | the locals |
371
417
  *
372
418
  * Each local is laid out as follows:
@@ -137,6 +137,16 @@ void pm_buffer_append_varsint(pm_buffer_t *buffer, int32_t value);
137
137
  */
138
138
  void pm_buffer_append_double(pm_buffer_t *buffer, double value);
139
139
 
140
+ /**
141
+ * Append a unicode codepoint to the buffer.
142
+ *
143
+ * @param buffer The buffer to append to.
144
+ * @param value The character to append.
145
+ * @returns True if the codepoint was valid and appended successfully, false
146
+ * otherwise.
147
+ */
148
+ bool pm_buffer_append_unicode_codepoint(pm_buffer_t *buffer, uint32_t value);
149
+
140
150
  /**
141
151
  * The different types of escaping that can be performed by the buffer when
142
152
  * appending a slice of Ruby source code.
@@ -14,7 +14,7 @@
14
14
  /**
15
15
  * The minor version of the Prism library as an int.
16
16
  */
17
- #define PRISM_VERSION_MINOR 3
17
+ #define PRISM_VERSION_MINOR 4
18
18
 
19
19
  /**
20
20
  * The patch version of the Prism library as an int.
@@ -24,6 +24,6 @@
24
24
  /**
25
25
  * The version of the Prism library as a constant string.
26
26
  */
27
- #define PRISM_VERSION "1.3.0"
27
+ #define PRISM_VERSION "1.4.0"
28
28
 
29
29
  #endif
data/include/prism.h CHANGED
@@ -93,11 +93,11 @@ typedef char * (pm_parse_stream_fgets_t)(char *string, int size, void *stream);
93
93
  * @param parser The parser to use.
94
94
  * @param buffer The buffer to use.
95
95
  * @param stream The stream to parse.
96
- * @param fgets The function to use to read from the stream.
96
+ * @param stream_fgets The function to use to read from the stream.
97
97
  * @param options The optional options to use when parsing.
98
98
  * @return The AST representing the source.
99
99
  */
100
- PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *fgets, const pm_options_t *options);
100
+ PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const pm_options_t *options);
101
101
 
102
102
  // We optionally support serializing to a binary string. For systems that don't
103
103
  // want or need this functionality, it can be turned off with the
@@ -110,10 +110,10 @@ PRISM_EXPORTED_FUNCTION pm_node_t * pm_parse_stream(pm_parser_t *parser, pm_buff
110
110
  *
111
111
  * @param buffer The buffer to serialize to.
112
112
  * @param stream The stream to parse.
113
- * @param fgets The function to use to read from the stream.
113
+ * @param stream_fgets The function to use to read from the stream.
114
114
  * @param data The optional data to pass to the parser.
115
115
  */
116
- PRISM_EXPORTED_FUNCTION void pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *fgets, const char *data);
116
+ PRISM_EXPORTED_FUNCTION void pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const char *data);
117
117
 
118
118
  /**
119
119
  * Serialize the given list of comments to the given buffer.
@@ -3608,6 +3608,9 @@ module Prism
3608
3608
  table = Table.new("ParenthesesNode")
3609
3609
  id = node_id(node)
3610
3610
 
3611
+ # flags
3612
+ table.field("flags", parentheses_node_flags_inspect(node))
3613
+
3611
3614
  # body
3612
3615
  unless (body = node.body).nil?
3613
3616
  table.field("body", port: true)
@@ -3954,6 +3957,11 @@ module Prism
3954
3957
  digraph.edge("#{id}:reference -> #{node_id(reference)};")
3955
3958
  end
3956
3959
 
3960
+ # then_keyword_loc
3961
+ unless (then_keyword_loc = node.then_keyword_loc).nil?
3962
+ table.field("then_keyword_loc", location_inspect(then_keyword_loc))
3963
+ end
3964
+
3957
3965
  # statements
3958
3966
  unless (statements = node.statements).nil?
3959
3967
  table.field("statements", port: true)
@@ -4682,6 +4690,14 @@ module Prism
4682
4690
  flags.join(", ")
4683
4691
  end
4684
4692
 
4693
+ # Inspect a node that has parentheses_node_flags flags to display the flags as a
4694
+ # comma-separated list.
4695
+ def parentheses_node_flags_inspect(node)
4696
+ flags = [] #: Array[String]
4697
+ flags << "multiple_statements" if node.multiple_statements?
4698
+ flags.join(", ")
4699
+ end
4700
+
4685
4701
  # Inspect a node that has range_flags flags to display the flags as a
4686
4702
  # comma-separated list.
4687
4703
  def range_flags_inspect(node)
data/lib/prism/dsl.rb CHANGED
@@ -714,8 +714,8 @@ module Prism
714
714
  end
715
715
 
716
716
  # Create a new RescueNode node.
717
- def rescue_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, exceptions: [], operator_loc: nil, reference: nil, statements: nil, subsequent: nil)
718
- RescueNode.new(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, statements, subsequent)
717
+ def rescue_node(source: default_source, node_id: 0, location: default_location, flags: 0, keyword_loc: location, exceptions: [], operator_loc: nil, reference: nil, then_keyword_loc: nil, statements: nil, subsequent: nil)
718
+ RescueNode.new(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, then_keyword_loc, statements, subsequent)
719
719
  end
720
720
 
721
721
  # Create a new RestParameterNode node.
@@ -912,6 +912,14 @@ module Prism
912
912
  end
913
913
  end
914
914
 
915
+ # Retrieve the value of one of the ParenthesesNodeFlags flags.
916
+ def parentheses_node_flag(name)
917
+ case name
918
+ when :multiple_statements then ParenthesesNodeFlags::MULTIPLE_STATEMENTS
919
+ else Kernel.raise ArgumentError, "invalid ParenthesesNodeFlags flag: #{name.inspect}"
920
+ end
921
+ end
922
+
915
923
  # Retrieve the value of one of the RangeFlags flags.
916
924
  def range_flag(name)
917
925
  case name
data/lib/prism/ffi.rb CHANGED
@@ -15,7 +15,8 @@ module Prism
15
15
  # must align with the build shared library from make/rake.
16
16
  libprism_in_build = File.expand_path("../../build/libprism.#{RbConfig::CONFIG["SOEXT"]}", __dir__)
17
17
  libprism_in_libdir = "#{RbConfig::CONFIG["libdir"]}/prism/libprism.#{RbConfig::CONFIG["SOEXT"]}"
18
- if File.exist? libprism_in_build
18
+
19
+ if File.exist?(libprism_in_build)
19
20
  INCLUDE_DIR = File.expand_path("../../include", __dir__)
20
21
  ffi_lib libprism_in_build
21
22
  else
@@ -279,7 +280,7 @@ module Prism
279
280
  # access to the IO object already through the closure of the lambda, we
280
281
  # can pass a null pointer here and not worry.
281
282
  LibRubyParser.pm_serialize_parse_stream(buffer.pointer, nil, callback, dump_options(options))
282
- Prism.load(source, buffer.read)
283
+ Prism.load(source, buffer.read, options.fetch(:freeze, false))
283
284
  end
284
285
  end
285
286
 
@@ -354,50 +355,37 @@ module Prism
354
355
  def dump_common(string, options) # :nodoc:
355
356
  LibRubyParser::PrismBuffer.with do |buffer|
356
357
  LibRubyParser.pm_serialize_parse(buffer.pointer, string.pointer, string.length, dump_options(options))
357
- buffer.read
358
+
359
+ dumped = buffer.read
360
+ dumped.freeze if options.fetch(:freeze, false)
361
+
362
+ dumped
358
363
  end
359
364
  end
360
365
 
361
366
  def lex_common(string, code, options) # :nodoc:
362
- serialized = LibRubyParser::PrismBuffer.with do |buffer|
367
+ LibRubyParser::PrismBuffer.with do |buffer|
363
368
  LibRubyParser.pm_serialize_lex(buffer.pointer, string.pointer, string.length, dump_options(options))
364
- buffer.read
369
+ Serialize.load_lex(code, buffer.read, options.fetch(:freeze, false))
365
370
  end
366
-
367
- Serialize.load_tokens(Source.for(code), serialized)
368
371
  end
369
372
 
370
373
  def parse_common(string, code, options) # :nodoc:
371
374
  serialized = dump_common(string, options)
372
- Prism.load(code, serialized)
375
+ Serialize.load_parse(code, serialized, options.fetch(:freeze, false))
373
376
  end
374
377
 
375
378
  def parse_comments_common(string, code, options) # :nodoc:
376
379
  LibRubyParser::PrismBuffer.with do |buffer|
377
380
  LibRubyParser.pm_serialize_parse_comments(buffer.pointer, string.pointer, string.length, dump_options(options))
378
-
379
- source = Source.for(code)
380
- loader = Serialize::Loader.new(source, buffer.read)
381
-
382
- loader.load_header
383
- loader.load_encoding
384
- loader.load_start_line
385
- loader.load_comments
381
+ Serialize.load_parse_comments(code, buffer.read, options.fetch(:freeze, false))
386
382
  end
387
383
  end
388
384
 
389
385
  def parse_lex_common(string, code, options) # :nodoc:
390
386
  LibRubyParser::PrismBuffer.with do |buffer|
391
387
  LibRubyParser.pm_serialize_parse_lex(buffer.pointer, string.pointer, string.length, dump_options(options))
392
-
393
- source = Source.for(code)
394
- loader = Serialize::Loader.new(source, buffer.read)
395
-
396
- tokens = loader.load_tokens
397
- node, comments, magic_comments, data_loc, errors, warnings = loader.load_nodes
398
- tokens.each { |token,| token.value.force_encoding(loader.encoding) }
399
-
400
- ParseLexResult.new([node, tokens], comments, magic_comments, data_loc, errors, warnings, source)
388
+ Serialize.load_parse_lex(code, buffer.read, options.fetch(:freeze, false))
401
389
  end
402
390
  end
403
391
 
@@ -431,6 +419,8 @@ module Prism
431
419
  when /\A3\.3(\.\d+)?\z/
432
420
  1
433
421
  when /\A3\.4(\.\d+)?\z/
422
+ 2
423
+ when /\A3\.5(\.\d+)?\z/
434
424
  0
435
425
  else
436
426
  raise ArgumentError, "invalid version: #{version}"
@@ -480,15 +470,43 @@ module Prism
480
470
  template << "C"
481
471
  values << (options.fetch(:partial_script, false) ? 1 : 0)
482
472
 
473
+ template << "C"
474
+ values << (options.fetch(:freeze, false) ? 1 : 0)
475
+
483
476
  template << "L"
484
477
  if (scopes = options[:scopes])
485
478
  values << scopes.length
486
479
 
487
480
  scopes.each do |scope|
481
+ locals = nil
482
+ forwarding = 0
483
+
484
+ case scope
485
+ when Array
486
+ locals = scope
487
+ when Scope
488
+ locals = scope.locals
489
+
490
+ scope.forwarding.each do |forward|
491
+ case forward
492
+ when :* then forwarding |= 0x1
493
+ when :** then forwarding |= 0x2
494
+ when :& then forwarding |= 0x4
495
+ when :"..." then forwarding |= 0x8
496
+ else raise ArgumentError, "invalid forwarding value: #{forward}"
497
+ end
498
+ end
499
+ else
500
+ raise TypeError, "wrong argument type #{scope.class.inspect} (expected Array or Prism::Scope)"
501
+ end
502
+
488
503
  template << "L"
489
- values << scope.length
504
+ values << locals.length
505
+
506
+ template << "C"
507
+ values << forwarding
490
508
 
491
- scope.each do |local|
509
+ locals.each do |local|
492
510
  name = local.name
493
511
  template << "L"
494
512
  values << name.bytesize
@@ -1879,7 +1879,7 @@ module Prism
1879
1879
  # Inspect a ParenthesesNode node.
1880
1880
  def visit_parentheses_node(node)
1881
1881
  commands << [inspect_node("ParenthesesNode", node), indent]
1882
- flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ].compact
1882
+ flags = [("newline" if node.newline?), ("static_literal" if node.static_literal?), ("multiple_statements" if node.multiple_statements?)].compact
1883
1883
  commands << ["├── flags: #{flags.empty? ? "∅" : flags.join(", ")}\n", indent]
1884
1884
  if (body = node.body).nil?
1885
1885
  commands << ["├── body: ∅\n", indent]
@@ -2053,6 +2053,7 @@ module Prism
2053
2053
  commands << ["├── reference:\n", indent]
2054
2054
  commands << [reference, "#{indent}│ "]
2055
2055
  end
2056
+ commands << ["├── then_keyword_loc: #{inspect_location(node.then_keyword_loc)}\n", indent]
2056
2057
  if (statements = node.statements).nil?
2057
2058
  commands << ["├── statements: ∅\n", indent]
2058
2059
  else
data/lib/prism/node.rb CHANGED
@@ -10849,7 +10849,7 @@ module Prism
10849
10849
  [*opening_loc, *parts, *closing_loc] #: Array[Prism::node | Location]
10850
10850
  end
10851
10851
 
10852
- # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode], ?closing_loc: Location?) -> InterpolatedStringNode
10852
+ # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode], ?closing_loc: Location?) -> InterpolatedStringNode
10853
10853
  def copy(node_id: self.node_id, location: self.location, flags: self.flags, opening_loc: self.opening_loc, parts: self.parts, closing_loc: self.closing_loc)
10854
10854
  InterpolatedStringNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc)
10855
10855
  end
@@ -10857,7 +10857,7 @@ module Prism
10857
10857
  # def deconstruct: () -> Array[nil | Node]
10858
10858
  alias deconstruct child_nodes
10859
10859
 
10860
- # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode], closing_loc: Location? }
10860
+ # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode], closing_loc: Location? }
10861
10861
  def deconstruct_keys(keys)
10862
10862
  { node_id: node_id, location: location, opening_loc: opening_loc, parts: parts, closing_loc: closing_loc }
10863
10863
  end
@@ -10891,7 +10891,7 @@ module Prism
10891
10891
  repository.enter(node_id, :opening_loc) unless @opening_loc.nil?
10892
10892
  end
10893
10893
 
10894
- # attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode]
10894
+ # attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode]
10895
10895
  attr_reader :parts
10896
10896
 
10897
10897
  # attr_reader closing_loc: Location?
@@ -14306,6 +14306,11 @@ module Prism
14306
14306
  { node_id: node_id, location: location, body: body, opening_loc: opening_loc, closing_loc: closing_loc }
14307
14307
  end
14308
14308
 
14309
+ # def multiple_statements?: () -> bool
14310
+ def multiple_statements?
14311
+ flags.anybits?(ParenthesesNodeFlags::MULTIPLE_STATEMENTS)
14312
+ end
14313
+
14309
14314
  # attr_reader body: Prism::node?
14310
14315
  attr_reader :body
14311
14316
 
@@ -14364,6 +14369,7 @@ module Prism
14364
14369
  # comparing the value of locations. Locations are checked only for presence.
14365
14370
  def ===(other)
14366
14371
  other.is_a?(ParenthesesNode) &&
14372
+ (flags === other.flags) &&
14367
14373
  (body === other.body) &&
14368
14374
  (opening_loc.nil? == other.opening_loc.nil?) &&
14369
14375
  (closing_loc.nil? == other.closing_loc.nil?)
@@ -15709,7 +15715,7 @@ module Prism
15709
15715
  # `Foo, *splat, Bar` are in the `exceptions` field. `ex` is in the `reference` field.
15710
15716
  class RescueNode < Node
15711
15717
  # Initialize a new RescueNode node.
15712
- def initialize(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, statements, subsequent)
15718
+ def initialize(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, then_keyword_loc, statements, subsequent)
15713
15719
  @source = source
15714
15720
  @node_id = node_id
15715
15721
  @location = location
@@ -15718,6 +15724,7 @@ module Prism
15718
15724
  @exceptions = exceptions
15719
15725
  @operator_loc = operator_loc
15720
15726
  @reference = reference
15727
+ @then_keyword_loc = then_keyword_loc
15721
15728
  @statements = statements
15722
15729
  @subsequent = subsequent
15723
15730
  end
@@ -15744,20 +15751,20 @@ module Prism
15744
15751
 
15745
15752
  # def comment_targets: () -> Array[Node | Location]
15746
15753
  def comment_targets
15747
- [keyword_loc, *exceptions, *operator_loc, *reference, *statements, *subsequent] #: Array[Prism::node | Location]
15754
+ [keyword_loc, *exceptions, *operator_loc, *reference, *then_keyword_loc, *statements, *subsequent] #: Array[Prism::node | Location]
15748
15755
  end
15749
15756
 
15750
- # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?exceptions: Array[Prism::node], ?operator_loc: Location?, ?reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil, ?statements: StatementsNode?, ?subsequent: RescueNode?) -> RescueNode
15751
- def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, exceptions: self.exceptions, operator_loc: self.operator_loc, reference: self.reference, statements: self.statements, subsequent: self.subsequent)
15752
- RescueNode.new(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, statements, subsequent)
15757
+ # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?keyword_loc: Location, ?exceptions: Array[Prism::node], ?operator_loc: Location?, ?reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil, ?then_keyword_loc: Location?, ?statements: StatementsNode?, ?subsequent: RescueNode?) -> RescueNode
15758
+ def copy(node_id: self.node_id, location: self.location, flags: self.flags, keyword_loc: self.keyword_loc, exceptions: self.exceptions, operator_loc: self.operator_loc, reference: self.reference, then_keyword_loc: self.then_keyword_loc, statements: self.statements, subsequent: self.subsequent)
15759
+ RescueNode.new(source, node_id, location, flags, keyword_loc, exceptions, operator_loc, reference, then_keyword_loc, statements, subsequent)
15753
15760
  end
15754
15761
 
15755
15762
  # def deconstruct: () -> Array[nil | Node]
15756
15763
  alias deconstruct child_nodes
15757
15764
 
15758
- # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, exceptions: Array[Prism::node], operator_loc: Location?, reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil, statements: StatementsNode?, subsequent: RescueNode? }
15765
+ # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, keyword_loc: Location, exceptions: Array[Prism::node], operator_loc: Location?, reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil, then_keyword_loc: Location?, statements: StatementsNode?, subsequent: RescueNode? }
15759
15766
  def deconstruct_keys(keys)
15760
- { node_id: node_id, location: location, keyword_loc: keyword_loc, exceptions: exceptions, operator_loc: operator_loc, reference: reference, statements: statements, subsequent: subsequent }
15767
+ { node_id: node_id, location: location, keyword_loc: keyword_loc, exceptions: exceptions, operator_loc: operator_loc, reference: reference, then_keyword_loc: then_keyword_loc, statements: statements, subsequent: subsequent }
15761
15768
  end
15762
15769
 
15763
15770
  # attr_reader keyword_loc: Location
@@ -15798,6 +15805,25 @@ module Prism
15798
15805
  # attr_reader reference: LocalVariableTargetNode | InstanceVariableTargetNode | ClassVariableTargetNode | GlobalVariableTargetNode | ConstantTargetNode | ConstantPathTargetNode | CallTargetNode | IndexTargetNode | BackReferenceReadNode | NumberedReferenceReadNode | MissingNode | nil
15799
15806
  attr_reader :reference
15800
15807
 
15808
+ # attr_reader then_keyword_loc: Location?
15809
+ def then_keyword_loc
15810
+ location = @then_keyword_loc
15811
+ case location
15812
+ when nil
15813
+ nil
15814
+ when Location
15815
+ location
15816
+ else
15817
+ @then_keyword_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF)
15818
+ end
15819
+ end
15820
+
15821
+ # Save the then_keyword_loc location using the given saved source so that
15822
+ # it can be retrieved later.
15823
+ def save_then_keyword_loc(repository)
15824
+ repository.enter(node_id, :then_keyword_loc) unless @then_keyword_loc.nil?
15825
+ end
15826
+
15801
15827
  # attr_reader statements: StatementsNode?
15802
15828
  attr_reader :statements
15803
15829
 
@@ -15814,6 +15840,11 @@ module Prism
15814
15840
  operator_loc&.slice
15815
15841
  end
15816
15842
 
15843
+ # def then_keyword: () -> String?
15844
+ def then_keyword
15845
+ then_keyword_loc&.slice
15846
+ end
15847
+
15817
15848
  # def inspect -> String
15818
15849
  def inspect
15819
15850
  InspectVisitor.compose(self)
@@ -15838,6 +15869,7 @@ module Prism
15838
15869
  exceptions.zip(other.exceptions).all? { |left, right| left === right } &&
15839
15870
  (operator_loc.nil? == other.operator_loc.nil?) &&
15840
15871
  (reference === other.reference) &&
15872
+ (then_keyword_loc.nil? == other.then_keyword_loc.nil?) &&
15841
15873
  (statements === other.statements) &&
15842
15874
  (subsequent === other.subsequent)
15843
15875
  end
@@ -18513,6 +18545,12 @@ module Prism
18513
18545
  REPEATED_PARAMETER = 1 << 2
18514
18546
  end
18515
18547
 
18548
+ # Flags for parentheses nodes.
18549
+ module ParenthesesNodeFlags
18550
+ # parentheses that contain multiple potentially void statements
18551
+ MULTIPLE_STATEMENTS = 1 << 2
18552
+ end
18553
+
18516
18554
  # Flags for range and flip-flop nodes.
18517
18555
  module RangeFlags
18518
18556
  # ... operator
@@ -63,7 +63,7 @@ module Prism
63
63
 
64
64
  class Node
65
65
  def newline_flag? # :nodoc:
66
- @newline_flag ? true : false
66
+ !!defined?(@newline_flag)
67
67
  end
68
68
 
69
69
  def newline_flag!(lines) # :nodoc:
@@ -48,6 +48,16 @@ module Prism
48
48
  @offsets = offsets # set after parsing is done
49
49
  end
50
50
 
51
+ # Replace the value of start_line with the given value.
52
+ def replace_start_line(start_line)
53
+ @start_line = start_line
54
+ end
55
+
56
+ # Replace the value of offsets with the given value.
57
+ def replace_offsets(offsets)
58
+ @offsets.replace(offsets)
59
+ end
60
+
51
61
  # Returns the encoding of the source code, which is set by parameters to the
52
62
  # parser or by the encoding magic comment.
53
63
  def encoding
@@ -132,6 +142,13 @@ module Prism
132
142
  code_units_offset(byte_offset, encoding) - code_units_offset(line_start(byte_offset), encoding)
133
143
  end
134
144
 
145
+ # Freeze this object and the objects it contains.
146
+ def deep_freeze
147
+ source.freeze
148
+ offsets.freeze
149
+ freeze
150
+ end
151
+
135
152
  private
136
153
 
137
154
  # Binary search through the offsets to find the line number for the given
@@ -854,5 +871,40 @@ module Prism
854
871
  location
855
872
  super
856
873
  end
874
+
875
+ # Freeze this object and the objects it contains.
876
+ def deep_freeze
877
+ value.freeze
878
+ location.freeze
879
+ freeze
880
+ end
881
+ end
882
+
883
+ # This object is passed to the various Prism.* methods that accept the
884
+ # `scopes` option as an element of the list. It defines both the local
885
+ # variables visible at that scope as well as the forwarding parameters
886
+ # available at that scope.
887
+ class Scope
888
+ # The list of local variables that are defined in this scope. This should be
889
+ # defined as an array of symbols.
890
+ attr_reader :locals
891
+
892
+ # The list of local variables that are forwarded to the next scope. This
893
+ # should by defined as an array of symbols containing the specific values of
894
+ # :*, :**, :&, or :"...".
895
+ attr_reader :forwarding
896
+
897
+ # Create a new scope object with the given locals and forwarding.
898
+ def initialize(locals, forwarding)
899
+ @locals = locals
900
+ @forwarding = forwarding
901
+ end
902
+ end
903
+
904
+ # Create a new scope with the given locals and forwarding options that is
905
+ # suitable for passing into one of the Prism.* methods that accepts the
906
+ # `scopes` option.
907
+ def self.scope(locals: [], forwarding: [])
908
+ Scope.new(locals, forwarding)
857
909
  end
858
910
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Polyfill for String#append_as_bytes, which didn't exist until Ruby 3.4.
4
+ if !("".respond_to?(:append_as_bytes))
5
+ String.include(
6
+ Module.new {
7
+ def append_as_bytes(*args)
8
+ args.each do |arg|
9
+ arg = Integer === arg ? [arg].pack("C") : arg.b
10
+ self.<<(arg) # steep:ignore
11
+ end
12
+ end
13
+ }
14
+ )
15
+ end
@@ -334,7 +334,7 @@ module Prism
334
334
  when :parameters_node
335
335
  [NodeListField.new(:requireds), NodeListField.new(:optionals), OptionalNodeField.new(:rest), NodeListField.new(:posts), NodeListField.new(:keywords), OptionalNodeField.new(:keyword_rest), OptionalNodeField.new(:block)]
336
336
  when :parentheses_node
337
- [OptionalNodeField.new(:body), LocationField.new(:opening_loc), LocationField.new(:closing_loc)]
337
+ [FlagsField.new(:flags, [:multiple_statements?]), OptionalNodeField.new(:body), LocationField.new(:opening_loc), LocationField.new(:closing_loc)]
338
338
  when :pinned_expression_node
339
339
  [NodeField.new(:expression), LocationField.new(:operator_loc), LocationField.new(:lparen_loc), LocationField.new(:rparen_loc)]
340
340
  when :pinned_variable_node
@@ -360,7 +360,7 @@ module Prism
360
360
  when :rescue_modifier_node
361
361
  [NodeField.new(:expression), LocationField.new(:keyword_loc), NodeField.new(:rescue_expression)]
362
362
  when :rescue_node
363
- [LocationField.new(:keyword_loc), NodeListField.new(:exceptions), OptionalLocationField.new(:operator_loc), OptionalNodeField.new(:reference), OptionalNodeField.new(:statements), OptionalNodeField.new(:subsequent)]
363
+ [LocationField.new(:keyword_loc), NodeListField.new(:exceptions), OptionalLocationField.new(:operator_loc), OptionalNodeField.new(:reference), OptionalLocationField.new(:then_keyword_loc), OptionalNodeField.new(:statements), OptionalNodeField.new(:subsequent)]
364
364
  when :rest_parameter_node
365
365
  [FlagsField.new(:flags, [:repeated_parameter?]), OptionalConstantField.new(:name), OptionalLocationField.new(:name_loc), LocationField.new(:operator_loc)]
366
366
  when :retry_node